code wiki / (root) / nx_pe_compile_win_table.nx

nx_pe_compile_win_table.nx source

↩ module page · 245 lines · 13354 B

1// nx_pe_compile_win_table.nx -- W3b-5b: a MULTI-syscall organ runs native (redirect table). 2// 3// W3b-4 redirected one real syscall (sys_write). This rung proves the keystone-linker resolves a 4// TABLE of syscall labels in ONE organ: both `sys_write` AND `sys_exit` are redirected to kernel32 5// thunks, so a compiled organ that calls more than one syscall runs natively. The generalization that 6// a real container-manager organ needs. No compiler or assembler edits. 7// 8// sys_write shim -> jmp WRITE_THUNK (66B, fd-routed GetStdHandle+WriteFile, RETURNS; from W3b-4) 9// sys_exit shim -> jmp EXIT_THUNK (13B, mov ecx,edi; sub rsp,0x28; call[ExitProcess]; int3; from W3b-2) 10// .text = [entry stub][compiled code][WRITE_THUNK][EXIT_THUNK] 11// 12// NO-FALSE-GREEN: source `{ let a = sys_write(1,"TABLE-OK\n",9); let b = sys_exit(55); return a+b }`. 13// stdout shows "TABLE-OK\n" (sys_write reached WriteFile) AND the process exits 55 (sys_exit reached 14// ExitProcess with arg 55 -- terminating BEFORE `return a+b`, so 55 != the entry-stub's main-return 15// path). Two distinct syscalls, both redirected, both observable. Tamper either import -> 0xC0000139. 16// 17// Uses only the already-proven benign imports (GetStdHandle/WriteFile/ExitProcess). NOTE: sys_mmap -> 18// VirtualAlloc (W3b-5a) is BLOCKED by Windows Defender, which flags a minimal hand-built PE importing 19// VirtualAlloc as a shellcode-loader (see pe_compile_win_mmap_gate.log) -- a real finding for the 20// eventual container runtime; resolution (HeapAlloc vs an AV exclusion) is a user decision. 21// 22// PIPELINE (build WSL sovereign, run native): src.nx 23// -> ./_offc/nx_compile_x86_native.elf <src> > /tmp/nxwin.s 24// -> ./_offc/nx_sov_build_run.elf nx_pe_compile_win_table (reads /tmp/nxwin.s) 25// -> _offc/nx_win_compiled_table.exe -> run native on Windows 11. 26// 27// HONEST SCOPE: 2 syscalls (write, exit) via a hand-listed pair of redirects; a fully data-driven 28// N-entry table + the remaining syscalls (mmap/read/openat) are follow-ons. Replicates keystone 29// assemble (reuses axc_pass; no shared edit). lineage_id: substrate_pe_compile_win_table_v1 30 31import "nx_syscalls.nx" 32import "nxasm_x86.nx" 33import "nx_pe_writer.nx" 34 35const NXT_CODE_CAP: i64 = 1048576 36const NXT_FILE_SIZE: i64 = 0x800 37const NXT_STUB_LEN: i64 = 18 38const NXT_WTHUNK_LEN: i64 = 66 // fd-routed GetStdHandle+WriteFile returning thunk 39const NXT_ETHUNK_LEN: i64 = 13 // ExitProcess thunk 40const NXT_TEXT_CAP: i64 = 0x200 41const NXT_RVA_TEXT: i64 = 0x1000 42const NXT_RVA_RDATA: i64 = 0x2000 43const NXT_RVA_IDATA: i64 = 0x3000 44const NXT_FOFF_TEXT: i64 = 0x200 45const NXT_FOFF_RDATA:i64 = 0x400 46const NXT_FOFF_IDATA:i64 = 0x600 47const NXT_IAT_GSH: i64 = 0x3048 48const NXT_IAT_WF: i64 = 0x3050 49const NXT_IAT_EXIT: i64 = 0x3058 50 51// Assemble; report `main`, `sys_write`, `sys_exit` offsets (reuses axc_pass; no shared edit). 52func nxt_assemble(src: *u8, n: i64, out: *u8, out_cap: i64, p_main: *i64, p_sw: *i64, p_se: *i64) -> i64 { 53 let lab_off: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 54 let lab_len: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 55 let lab_addr: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 56 let lab_sec: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 57 let op0: *i64 = sys_mmap(72) as *i64 // 9 slots for SIB (matches nxasm_x86) 58 let op1: *i64 = sys_mmap(72) as *i64 59 let op2: *i64 = sys_mmap(72) as *i64 // API DRIFT FIX: axc_pass gained op2 60 let scratch: *u8 = sys_mmap(64) 61 let posbox: *i64 = sys_mmap(16) as *i64 62 let n_lab_box: *i64 = sys_mmap(16) as *i64 63 n_lab_box[0] = 0 64 let lh: *i64 = sys_mmap(ASM_LH_SIZE * 8) as *i64 65 66 let text_size: i64 = axc_pass(src, n, out, 0, 0, lab_off, lab_len, lab_addr, lab_sec, n_lab_box, lh, op0, op1, op2, scratch, posbox) 67 if text_size < 0 { return text_size } 68 let n_lab: i64 = n_lab_box[0] 69 70 var main_a: i64 = 0 - 1 71 var sw_a: i64 = 0 - 1 72 var se_a: i64 = 0 - 1 73 var k: i64 = 0 74 while k < n_lab { 75 if lab_sec[k] == 1 { lab_addr[k] = lab_addr[k] + text_size } 76 if axc_tok_is(src, lab_off[k], lab_len[k], "main") == 1 { main_a = lab_addr[k] } 77 if axc_tok_is(src, lab_off[k], lab_len[k], "sys_write") == 1 { sw_a = lab_addr[k] } 78 if axc_tok_is(src, lab_off[k], lab_len[k], "sys_exit") == 1 { se_a = lab_addr[k] } 79 k = k + 1 80 } 81 axc_lh_build(src, lab_off, lab_len, n_lab, lh) 82 83 let total: i64 = axc_pass(src, n, out, text_size, 1, lab_off, lab_len, lab_addr, lab_sec, n_lab_box, lh, op0, op1, op2, scratch, posbox) 84 if total < 0 { return total } 85 if total > out_cap { return 0 - 200 } 86 p_main[0] = main_a 87 p_sw[0] = sw_a 88 p_se[0] = se_a 89 return total 90} 91 92func nxt_emit_pe(buf: *u8, code: *u8, code_len: i64, main_off: i64, sw_off: i64, se_off: i64) -> i64 { 93 if (buf as i64) == 0 { return 0 - NX_PE_BAD_INPUT } 94 if sw_off < 0 { return 0 - NX_PE_BAD_INPUT } 95 if se_off < 0 { return 0 - NX_PE_BAD_INPUT } 96 let wthunk_off: i64 = NXT_STUB_LEN + code_len 97 let ethunk_off: i64 = wthunk_off + NXT_WTHUNK_LEN 98 let text_vsize: i64 = ethunk_off + NXT_ETHUNK_LEN 99 if text_vsize > NXT_TEXT_CAP { return 0 - NX_PE_BAD_INPUT } 100 101 // ===== DOS + PE sig / COFF (3 sections) / Optional Header (W3b-1 layout) ===== 102 _w16(buf, 0, 0x5A4D) 103 _w32(buf, 0x3C, FOFF_PE_SIG) 104 _w32(buf, FOFF_PE_SIG, 0x00004550) 105 _w16(buf, FOFF_COFF + 0, PE_MACHINE_AMD64) 106 _w16(buf, FOFF_COFF + 2, 3) 107 _w16(buf, FOFF_COFF + 16, 0xF0) 108 _w16(buf, FOFF_COFF + 18, PE_CHAR_EXEC | PE_CHAR_LARGE_ADDR) 109 _w16(buf, FOFF_OPT + 0, PE_OH_MAGIC_PEPLUS) 110 _w8(buf, FOFF_OPT + 2, 1) 111 _w32(buf, FOFF_OPT + 4, 0x200) 112 _w32(buf, FOFF_OPT + 8, 0x400) 113 _w32(buf, FOFF_OPT + 16, NXT_RVA_TEXT) 114 _w32(buf, FOFF_OPT + 20, NXT_RVA_TEXT) 115 _w64(buf, FOFF_OPT + 24, IMG_BASE_LO, IMG_BASE_HI) 116 _w32(buf, FOFF_OPT + 32, 0x1000) 117 _w32(buf, FOFF_OPT + 36, 0x200) 118 _w16(buf, FOFF_OPT + 40, 6) 119 _w16(buf, FOFF_OPT + 48, 6) 120 _w32(buf, FOFF_OPT + 56, 0x4000) 121 _w32(buf, FOFF_OPT + 60, 0x200) 122 _w16(buf, FOFF_OPT + 68, PE_SUBSYSTEM_CONSOLE) 123 _w64(buf, FOFF_OPT + 72, 0x100000, 0) 124 _w64(buf, FOFF_OPT + 80, 0x1000, 0) 125 _w64(buf, FOFF_OPT + 88, 0x100000, 0) 126 _w64(buf, FOFF_OPT + 96, 0x1000, 0) 127 _w32(buf, FOFF_OPT + 108, 16) 128 _w32(buf, FOFF_OPT + 112 + 8, NXT_RVA_IDATA) 129 _w32(buf, FOFF_OPT + 112 + 12, 0x28) 130 _emit_section_header(buf, FOFF_SECT_TBL, 46, 116, 101, 120, 116, 0, 0, 0, text_vsize, NXT_RVA_TEXT, 0x200, NXT_FOFF_TEXT, PE_SECT_CODE_X_R) 131 _emit_section_header(buf, FOFF_SECT_TBL + 40, 46, 114, 100, 97, 116, 97, 0, 0, 16, NXT_RVA_RDATA, 0x200, NXT_FOFF_RDATA, PE_SECT_DATA_R) 132 _emit_section_header(buf, FOFF_SECT_TBL + 80, 46, 105, 100, 97, 116, 97, 0, 0, 0xA1, NXT_RVA_IDATA, 0x200, NXT_FOFF_IDATA, PE_SECT_DATA_R) 133 134 // ===== .text: entry stub ===== 135 let t: i64 = NXT_FOFF_TEXT 136 _w8(buf, t+0, 0x48); _w8(buf, t+1, 0x83); _w8(buf, t+2, 0xEC); _w8(buf, t+3, 0x28) 137 _w8(buf, t+4, 0xE8); _w32(buf, t+5, 0x9 + main_off) 138 _w8(buf, t+9, 0x89); _w8(buf, t+10, 0xC1) 139 _w8(buf, t+11, 0xFF); _w8(buf, t+12, 0x15); _w32(buf, t+13, NXT_IAT_EXIT - (NXT_RVA_TEXT + 17)) 140 _w8(buf, t+17, 0xCC) 141 var i: i64 = 0 142 while i < code_len { buf[t + NXT_STUB_LEN + i] = code[i]; i = i + 1 } 143 144 // ===== WRITE thunk (fd-routed GetStdHandle+WriteFile, returns) ===== 145 let wrva: i64 = NXT_RVA_TEXT + wthunk_off 146 let w: i64 = NXT_FOFF_TEXT + wthunk_off 147 _w8(buf, w+0, 0x48); _w8(buf, w+1, 0x83); _w8(buf, w+2, 0xEC); _w8(buf, w+3, 0x38) // sub rsp,0x38 148 _w8(buf, w+4, 0x48); _w8(buf, w+5, 0x89); _w8(buf, w+6, 0x54); _w8(buf, w+7, 0x24); _w8(buf, w+8, 0x30) // mov [rsp+0x30],rdx 149 _w8(buf, w+9, 0xB9); _w8(buf, w+10, 0xF5); _w8(buf, w+11, 0xFF); _w8(buf, w+12, 0xFF); _w8(buf, w+13, 0xFF) // mov ecx,-11 150 _w8(buf, w+14, 0x83); _w8(buf, w+15, 0xFF); _w8(buf, w+16, 0x02) // cmp edi,2 151 _w8(buf, w+17, 0x75); _w8(buf, w+18, 0x05) // jne +5 152 _w8(buf, w+19, 0xB9); _w8(buf, w+20, 0xF4); _w8(buf, w+21, 0xFF); _w8(buf, w+22, 0xFF); _w8(buf, w+23, 0xFF) // mov ecx,-12 153 _w8(buf, w+24, 0xFF); _w8(buf, w+25, 0x15); _w32(buf, w+26, NXT_IAT_GSH - (wrva + 30)) // call [rip] GetStdHandle 154 _w8(buf, w+30, 0x48); _w8(buf, w+31, 0x89); _w8(buf, w+32, 0xC1) // mov rcx,rax 155 _w8(buf, w+33, 0x48); _w8(buf, w+34, 0x89); _w8(buf, w+35, 0xF2) // mov rdx,rsi 156 _w8(buf, w+36, 0x4C); _w8(buf, w+37, 0x8B); _w8(buf, w+38, 0x44); _w8(buf, w+39, 0x24); _w8(buf, w+40, 0x30) // mov r8,[rsp+0x30] 157 _w8(buf, w+41, 0x4C); _w8(buf, w+42, 0x8D); _w8(buf, w+43, 0x4C); _w8(buf, w+44, 0x24); _w8(buf, w+45, 0x28) // lea r9,[rsp+0x28] 158 _w8(buf, w+46, 0x48); _w8(buf, w+47, 0xC7); _w8(buf, w+48, 0x44); _w8(buf, w+49, 0x24); _w8(buf, w+50, 0x20); _w32(buf, w+51, 0) // mov qword[rsp+0x20],0 159 _w8(buf, w+55, 0xFF); _w8(buf, w+56, 0x15); _w32(buf, w+57, NXT_IAT_WF - (wrva + 61)) // call [rip] WriteFile 160 _w8(buf, w+61, 0x48); _w8(buf, w+62, 0x83); _w8(buf, w+63, 0xC4); _w8(buf, w+64, 0x38) // add rsp,0x38 161 _w8(buf, w+65, 0xC3) // ret 162 163 // ===== EXIT thunk (ExitProcess) ===== 164 let erva: i64 = NXT_RVA_TEXT + ethunk_off 165 let e: i64 = NXT_FOFF_TEXT + ethunk_off 166 _w8(buf, e+0, 0x89); _w8(buf, e+1, 0xF9) // mov ecx,edi 167 _w8(buf, e+2, 0x48); _w8(buf, e+3, 0x83); _w8(buf, e+4, 0xEC); _w8(buf, e+5, 0x28) // sub rsp,0x28 168 _w8(buf, e+6, 0xFF); _w8(buf, e+7, 0x15); _w32(buf, e+8, NXT_IAT_EXIT - (erva + 12)) // call [rip] ExitProcess 169 _w8(buf, e+12, 0xCC) // int3 170 171 // ===== OVERWRITE both shims with jmp rel32 -> their thunks ===== 172 let sw_rva: i64 = NXT_RVA_TEXT + NXT_STUB_LEN + sw_off 173 let sw: i64 = NXT_FOFF_TEXT + NXT_STUB_LEN + sw_off 174 _w8(buf, sw+0, 0xE9); _w32(buf, sw+1, wrva - (sw_rva + 5)) 175 let se_rva: i64 = NXT_RVA_TEXT + NXT_STUB_LEN + se_off 176 let se: i64 = NXT_FOFF_TEXT + NXT_STUB_LEN + se_off 177 _w8(buf, se+0, 0xE9); _w32(buf, se+1, erva - (se_rva + 5)) 178 179 // ===== .idata: 3 kernel32 imports (GetStdHandle, WriteFile, ExitProcess) -- W3b-1 layout ===== 180 let d: i64 = NXT_FOFF_IDATA 181 _w32(buf, d + 0, 0x3028) 182 _w32(buf, d + 12, 0x3092) 183 _w32(buf, d + 16, 0x3048) 184 _w64(buf, d + 0x28, 0x3068, 0) 185 _w64(buf, d + 0x30, 0x3078, 0) 186 _w64(buf, d + 0x38, 0x3084, 0) 187 _w64(buf, d + 0x40, 0, 0) 188 _w64(buf, d + 0x48, 0x3068, 0) 189 _w64(buf, d + 0x50, 0x3078, 0) 190 _w64(buf, d + 0x58, 0x3084, 0) 191 _w64(buf, d + 0x60, 0, 0) 192 _w16(buf, d + 0x68, 0) 193 _w8(buf,d+0x6A,71);_w8(buf,d+0x6B,101);_w8(buf,d+0x6C,116);_w8(buf,d+0x6D,83) // GetS 194 _w8(buf,d+0x6E,116);_w8(buf,d+0x6F,100);_w8(buf,d+0x70,72);_w8(buf,d+0x71,97) // tdHa 195 _w8(buf,d+0x72,110);_w8(buf,d+0x73,100);_w8(buf,d+0x74,108);_w8(buf,d+0x75,101) // ndle 196 _w8(buf,d+0x76,0);_w8(buf,d+0x77,0) 197 _w16(buf, d + 0x78, 0) 198 _w8(buf,d+0x7A,87);_w8(buf,d+0x7B,114);_w8(buf,d+0x7C,105);_w8(buf,d+0x7D,116) // Writ 199 _w8(buf,d+0x7E,101);_w8(buf,d+0x7F,70);_w8(buf,d+0x80,105);_w8(buf,d+0x81,108) // eFil 200 _w8(buf,d+0x82,101);_w8(buf,d+0x83,0) // e\0 201 _w16(buf, d + 0x84, 0) 202 _w8(buf,d+0x86,69);_w8(buf,d+0x87,120);_w8(buf,d+0x88,105);_w8(buf,d+0x89,116) // Exit 203 _w8(buf,d+0x8A,80);_w8(buf,d+0x8B,114);_w8(buf,d+0x8C,111);_w8(buf,d+0x8D,99) // Proc 204 _w8(buf,d+0x8E,101);_w8(buf,d+0x8F,115);_w8(buf,d+0x90,115);_w8(buf,d+0x91,0) // ess\0 205 _w8(buf,d+0x92,107);_w8(buf,d+0x93,101);_w8(buf,d+0x94,114);_w8(buf,d+0x95,110) // kern 206 _w8(buf,d+0x96,101);_w8(buf,d+0x97,108);_w8(buf,d+0x98,51);_w8(buf,d+0x99,50) // el32 207 _w8(buf,d+0x9A,46);_w8(buf,d+0x9B,100);_w8(buf,d+0x9C,108);_w8(buf,d+0x9D,108);_w8(buf,d+0x9E,0) // .dll\0 208 209 return NX_PE_OK 210} 211 212func main() -> i64 { 213 let lenbox: *i64 = sys_mmap(16) as *i64 214 let src: *u8 = sys_read_file("/tmp/nxwin.s" as *u8, lenbox) 215 if (src as i64) == 0 { return 1 } 216 let n: i64 = lenbox[0] 217 if n <= 0 { return 2 } 218 219 let code: *u8 = sys_mmap(NXT_CODE_CAP) 220 let mainbox: *i64 = sys_mmap(16) as *i64 221 let swbox: *i64 = sys_mmap(16) as *i64 222 let sebox: *i64 = sys_mmap(16) as *i64 223 mainbox[0] = 0 - 1 224 swbox[0] = 0 - 1 225 sebox[0] = 0 - 1 226 let code_len: i64 = nxt_assemble(src, n, code, NXT_CODE_CAP, mainbox, swbox, sebox) 227 if code_len < 0 { return 3 } 228 let main_off: i64 = mainbox[0] 229 if main_off < 0 { return 4 } 230 let sw_off: i64 = swbox[0] 231 if sw_off < 0 { return 6 } 232 let se_off: i64 = sebox[0] 233 if se_off < 0 { return 7 } 234 235 let buf: *u8 = sys_mmap(NXT_FILE_SIZE) 236 let rc: i64 = nxt_emit_pe(buf, code, code_len, main_off, sw_off, se_off) 237 if rc != NX_PE_OK { return 5 } 238 239 let outp: *u8 = "/mnt/c/Users/elder/nishi-core/nxc2/_offc/nx_win_compiled_table.exe" as *u8 240 if nx_pe_write_to_file(outp, buf, NXT_FILE_SIZE) != NX_PE_OK { return 70 } 241 242 let msg: *u8 = "[substrate] nxc2-compiled native multi-syscall PE written: nx_win_compiled_table.exe\n" as *u8 243 sys_write(1, msg, 85) 244 return 0 245}