code wiki / (root) / nx_pe_compile_win_organ.nx

nx_pe_compile_win_organ.nx source

↩ module page · 284 lines · 16146 B

1// nx_pe_compile_win_organ.nx -- W3b-6: a realistic organ runs ENTIRELY native (mmap+write+exit). 2// 3// The milestone: one nxc2-compiled organ that ALLOCATES a buffer (sys_mmap), FILLS it, PRINTS it 4// (sys_write), and EXITS (sys_exit) -- all three genuine nx_syscalls calls lowered to kernel32 by the 5// keystone-linker resolving a TABLE of three labels in one PE. 5 imports (GetProcessHeap, HeapAlloc, 6// GetStdHandle, WriteFile, ExitProcess). No compiler or assembler edits. 7// 8// sys_mmap shim -> jmp HALLOC_THUNK (29B: GetProcessHeap+HeapAlloc; W3b-5a') 9// sys_write shim -> jmp WRITE_THUNK (66B: fd-routed GetStdHandle+WriteFile, returns; W3b-4) 10// sys_exit shim -> jmp EXIT_THUNK (13B: ExitProcess; W3b-2) 11// .text (raw 0x400) = [entry stub 18][compiled code][HALLOC 29][WRITE 66][EXIT 13] 12// 13// NO-FALSE-GREEN: source allocates p=sys_mmap(4096), writes p[0..2]={78,88,10}='N','X','\n', then 14// sys_write(1,p,3), then sys_exit(88). stdout "NX\n" proves the bytes round-tripped through HeapAlloc 15// memory and WriteFile read them; exit 88 proves sys_exit reached ExitProcess (terminating before the 16// final `return w+e`). All three syscalls cooperate. Tamper any import -> 0xC0000139. 17// 18// PIPELINE (build WSL sovereign, run native): src.nx -> nx_compile_x86_native.elf > /tmp/nxwin.s 19// -> nx_sov_build_run.elf nx_pe_compile_win_organ -> _offc/nx_win_compiled_organ.exe -> run native. 20// 21// HONEST SCOPE: three syscalls hand-listed (not yet a fully data-driven N-from-spec table); sys_mmap 22// uses HeapAlloc (benign; VirtualAlloc is Defender-blocked). read/openat remain. This is the 23// "realistic organ runs native" milestone, not yet a full container-manager. Replicates keystone 24// assemble (reuses axc_pass; no shared edit). lineage_id: substrate_pe_compile_win_organ_v1 25 26import "nx_syscalls.nx" 27import "nxasm_x86.nx" 28import "nx_pe_writer.nx" 29 30const NXORG_CODE_CAP: i64 = 1048576 31const NXORG_FILE_SIZE: i64 = 0x800 // headers(0x200) + .text(0x400) + .idata(0x200) 32const NXORG_STUB_LEN: i64 = 18 33const NXORG_HLEN: i64 = 29 // halloc thunk 34const NXORG_WLEN: i64 = 66 // write thunk 35const NXORG_ELEN: i64 = 13 // exit thunk 36const NXORG_TEXT_CAP: i64 = 0x400 // .text raw region (file 0x200..0x600) 37const NXORG_RVA_TEXT: i64 = 0x1000 38const NXORG_RVA_IDATA: i64 = 0x2000 // .text raw 0x400 -> virtual rounds to 0x1000 -> idata at 0x2000 39const NXORG_FOFF_TEXT: i64 = 0x200 40const NXORG_FOFF_IDATA:i64 = 0x600 41const NXORG_IAT_GPH: i64 = 0x2058 42const NXORG_IAT_HA: i64 = 0x2060 43const NXORG_IAT_GSH: i64 = 0x2068 44const NXORG_IAT_WF: i64 = 0x2070 45const NXORG_IAT_EXIT: i64 = 0x2078 46 47func nxorg_assemble(src: *u8, n: i64, out: *u8, out_cap: i64, p_main: *i64, p_mm: *i64, p_sw: *i64, p_se: *i64) -> i64 { 48 let lab_off: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 49 let lab_len: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 50 let lab_addr: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 51 let lab_sec: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 52 let op0: *i64 = sys_mmap(72) as *i64 // 9 slots for SIB (matches nxasm_x86) 53 let op1: *i64 = sys_mmap(72) as *i64 54 let op2: *i64 = sys_mmap(72) as *i64 // API DRIFT FIX: axc_pass gained op2 55 let scratch: *u8 = sys_mmap(64) 56 let posbox: *i64 = sys_mmap(16) as *i64 57 let n_lab_box: *i64 = sys_mmap(16) as *i64 58 n_lab_box[0] = 0 59 let lh: *i64 = sys_mmap(ASM_LH_SIZE * 8) as *i64 60 61 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) 62 if text_size < 0 { return text_size } 63 let n_lab: i64 = n_lab_box[0] 64 65 var main_a: i64 = 0 - 1 66 var mm_a: i64 = 0 - 1 67 var sw_a: i64 = 0 - 1 68 var se_a: i64 = 0 - 1 69 var k: i64 = 0 70 while k < n_lab { 71 if lab_sec[k] == 1 { lab_addr[k] = lab_addr[k] + text_size } 72 if axc_tok_is(src, lab_off[k], lab_len[k], "main") == 1 { main_a = lab_addr[k] } 73 if axc_tok_is(src, lab_off[k], lab_len[k], "sys_mmap") == 1 { mm_a = lab_addr[k] } 74 if axc_tok_is(src, lab_off[k], lab_len[k], "sys_write") == 1 { sw_a = lab_addr[k] } 75 if axc_tok_is(src, lab_off[k], lab_len[k], "sys_exit") == 1 { se_a = lab_addr[k] } 76 k = k + 1 77 } 78 axc_lh_build(src, lab_off, lab_len, n_lab, lh) 79 80 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) 81 if total < 0 { return total } 82 if total > out_cap { return 0 - 200 } 83 p_main[0] = main_a 84 p_mm[0] = mm_a 85 p_sw[0] = sw_a 86 p_se[0] = se_a 87 return total 88} 89 90func nxorg_emit_pe(buf: *u8, code: *u8, code_len: i64, main_off: i64, mm_off: i64, sw_off: i64, se_off: i64) -> i64 { 91 if (buf as i64) == 0 { return 0 - NX_PE_BAD_INPUT } 92 if mm_off < 0 { return 0 - NX_PE_BAD_INPUT } 93 if sw_off < 0 { return 0 - NX_PE_BAD_INPUT } 94 if se_off < 0 { return 0 - NX_PE_BAD_INPUT } 95 let hoff: i64 = NXORG_STUB_LEN + code_len 96 let woff: i64 = hoff + NXORG_HLEN 97 let eoff: i64 = woff + NXORG_WLEN 98 let text_vsize: i64 = eoff + NXORG_ELEN 99 if text_vsize > NXORG_TEXT_CAP { return 0 - NX_PE_BAD_INPUT } 100 101 _w16(buf, 0, 0x5A4D) 102 _w32(buf, 0x3C, FOFF_PE_SIG) 103 _w32(buf, FOFF_PE_SIG, 0x00004550) 104 _w16(buf, FOFF_COFF + 0, PE_MACHINE_AMD64) 105 _w16(buf, FOFF_COFF + 2, 2) 106 _w16(buf, FOFF_COFF + 16, 0xF0) 107 _w16(buf, FOFF_COFF + 18, PE_CHAR_EXEC | PE_CHAR_LARGE_ADDR) 108 _w16(buf, FOFF_OPT + 0, PE_OH_MAGIC_PEPLUS) 109 _w8(buf, FOFF_OPT + 2, 1) 110 _w32(buf, FOFF_OPT + 4, 0x400) // SizeOfCode 111 _w32(buf, FOFF_OPT + 8, 0x200) // SizeOfInitializedData 112 _w32(buf, FOFF_OPT + 16, NXORG_RVA_TEXT) 113 _w32(buf, FOFF_OPT + 20, NXORG_RVA_TEXT) 114 _w64(buf, FOFF_OPT + 24, IMG_BASE_LO, IMG_BASE_HI) 115 _w32(buf, FOFF_OPT + 32, 0x1000) 116 _w32(buf, FOFF_OPT + 36, 0x200) 117 _w16(buf, FOFF_OPT + 40, 6) 118 _w16(buf, FOFF_OPT + 48, 6) 119 _w32(buf, FOFF_OPT + 56, 0x3000) // SizeOfImage (headers + .text 0x1000 + .idata 0x1000) 120 _w32(buf, FOFF_OPT + 60, 0x200) 121 _w16(buf, FOFF_OPT + 68, PE_SUBSYSTEM_CONSOLE) 122 _w64(buf, FOFF_OPT + 72, 0x100000, 0) 123 _w64(buf, FOFF_OPT + 80, 0x1000, 0) 124 _w64(buf, FOFF_OPT + 88, 0x100000, 0) 125 _w64(buf, FOFF_OPT + 96, 0x1000, 0) 126 _w32(buf, FOFF_OPT + 108, 16) 127 _w32(buf, FOFF_OPT + 112 + 8, NXORG_RVA_IDATA) 128 _w32(buf, FOFF_OPT + 112 + 12, 0x28) 129 // .text raw size 0x400 (two file chunks) so a larger organ fits 130 _emit_section_header(buf, FOFF_SECT_TBL, 46, 116, 101, 120, 116, 0, 0, 0, text_vsize, NXORG_RVA_TEXT, 0x400, NXORG_FOFF_TEXT, PE_SECT_CODE_X_R) 131 _emit_section_header(buf, FOFF_SECT_TBL + 40, 46, 105, 100, 97, 116, 97, 0, 0, 0xDD, NXORG_RVA_IDATA, 0x200, NXORG_FOFF_IDATA, PE_SECT_DATA_R) 132 133 // ===== .text: entry stub (call main; exit with its return) ===== 134 let t: i64 = NXORG_FOFF_TEXT 135 _w8(buf, t+0, 0x48); _w8(buf, t+1, 0x83); _w8(buf, t+2, 0xEC); _w8(buf, t+3, 0x28) 136 _w8(buf, t+4, 0xE8); _w32(buf, t+5, 0x9 + main_off) 137 _w8(buf, t+9, 0x89); _w8(buf, t+10, 0xC1) 138 _w8(buf, t+11, 0xFF); _w8(buf, t+12, 0x15); _w32(buf, t+13, NXORG_IAT_EXIT - (NXORG_RVA_TEXT + 17)) 139 _w8(buf, t+17, 0xCC) 140 var i: i64 = 0 141 while i < code_len { buf[t + NXORG_STUB_LEN + i] = code[i]; i = i + 1 } 142 143 // ===== HALLOC thunk (sys_mmap -> GetProcessHeap+HeapAlloc) ===== 144 let hrva: i64 = NXORG_RVA_TEXT + hoff 145 let h: i64 = NXORG_FOFF_TEXT + hoff 146 _w8(buf, h+0, 0x48); _w8(buf, h+1, 0x83); _w8(buf, h+2, 0xEC); _w8(buf, h+3, 0x28) // sub rsp,0x28 147 _w8(buf, h+4, 0xFF); _w8(buf, h+5, 0x15); _w32(buf, h+6, NXORG_IAT_GPH - (hrva + 10)) // call GetProcessHeap 148 _w8(buf, h+10, 0x48); _w8(buf, h+11, 0x89); _w8(buf, h+12, 0xC1) // mov rcx,rax 149 _w8(buf, h+13, 0x31); _w8(buf, h+14, 0xD2) // xor edx,edx 150 _w8(buf, h+15, 0x49); _w8(buf, h+16, 0x89); _w8(buf, h+17, 0xF8) // mov r8,rdi 151 _w8(buf, h+18, 0xFF); _w8(buf, h+19, 0x15); _w32(buf, h+20, NXORG_IAT_HA - (hrva + 24)) // call HeapAlloc 152 _w8(buf, h+24, 0x48); _w8(buf, h+25, 0x83); _w8(buf, h+26, 0xC4); _w8(buf, h+27, 0x28) // add rsp,0x28 153 _w8(buf, h+28, 0xC3) // ret 154 155 // ===== WRITE thunk (sys_write -> fd-routed GetStdHandle+WriteFile, returns) ===== 156 let wrva: i64 = NXORG_RVA_TEXT + woff 157 let w: i64 = NXORG_FOFF_TEXT + woff 158 _w8(buf, w+0, 0x48); _w8(buf, w+1, 0x83); _w8(buf, w+2, 0xEC); _w8(buf, w+3, 0x38) // sub rsp,0x38 159 _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 160 _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 161 _w8(buf, w+14, 0x83); _w8(buf, w+15, 0xFF); _w8(buf, w+16, 0x02) // cmp edi,2 162 _w8(buf, w+17, 0x75); _w8(buf, w+18, 0x05) // jne +5 163 _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 164 _w8(buf, w+24, 0xFF); _w8(buf, w+25, 0x15); _w32(buf, w+26, NXORG_IAT_GSH - (wrva + 30)) // call GetStdHandle 165 _w8(buf, w+30, 0x48); _w8(buf, w+31, 0x89); _w8(buf, w+32, 0xC1) // mov rcx,rax 166 _w8(buf, w+33, 0x48); _w8(buf, w+34, 0x89); _w8(buf, w+35, 0xF2) // mov rdx,rsi 167 _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] 168 _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] 169 _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 170 _w8(buf, w+55, 0xFF); _w8(buf, w+56, 0x15); _w32(buf, w+57, NXORG_IAT_WF - (wrva + 61)) // call WriteFile 171 _w8(buf, w+61, 0x48); _w8(buf, w+62, 0x83); _w8(buf, w+63, 0xC4); _w8(buf, w+64, 0x38) // add rsp,0x38 172 _w8(buf, w+65, 0xC3) // ret 173 174 // ===== EXIT thunk (sys_exit -> ExitProcess) ===== 175 let erva: i64 = NXORG_RVA_TEXT + eoff 176 let e: i64 = NXORG_FOFF_TEXT + eoff 177 _w8(buf, e+0, 0x89); _w8(buf, e+1, 0xF9) // mov ecx,edi 178 _w8(buf, e+2, 0x48); _w8(buf, e+3, 0x83); _w8(buf, e+4, 0xEC); _w8(buf, e+5, 0x28) // sub rsp,0x28 179 _w8(buf, e+6, 0xFF); _w8(buf, e+7, 0x15); _w32(buf, e+8, NXORG_IAT_EXIT - (erva + 12)) // call ExitProcess 180 _w8(buf, e+12, 0xCC) // int3 181 182 // ===== OVERWRITE the three shims with jmp rel32 -> their thunks ===== 183 let mm_rva: i64 = NXORG_RVA_TEXT + NXORG_STUB_LEN + mm_off 184 let mm: i64 = NXORG_FOFF_TEXT + NXORG_STUB_LEN + mm_off 185 _w8(buf, mm+0, 0xE9); _w32(buf, mm+1, hrva - (mm_rva + 5)) 186 let sw_rva: i64 = NXORG_RVA_TEXT + NXORG_STUB_LEN + sw_off 187 let sw: i64 = NXORG_FOFF_TEXT + NXORG_STUB_LEN + sw_off 188 _w8(buf, sw+0, 0xE9); _w32(buf, sw+1, wrva - (sw_rva + 5)) 189 let se_rva: i64 = NXORG_RVA_TEXT + NXORG_STUB_LEN + se_off 190 let se: i64 = NXORG_FOFF_TEXT + NXORG_STUB_LEN + se_off 191 _w8(buf, se+0, 0xE9); _w32(buf, se+1, erva - (se_rva + 5)) 192 193 // ===== .idata: 5 kernel32 imports ===== 194 let d: i64 = NXORG_FOFF_IDATA 195 _w32(buf, d + 0, 0x2028) // OriginalFirstThunk = INT RVA 196 _w32(buf, d + 12, 0x20D0) // Name = "kernel32.dll" RVA 197 _w32(buf, d + 16, 0x2058) // FirstThunk = IAT RVA 198 // INT (RVA 0x2028) -> name RVAs 199 _w64(buf, d + 0x28, 0x2088, 0) // GetProcessHeap 200 _w64(buf, d + 0x30, 0x209A, 0) // HeapAlloc 201 _w64(buf, d + 0x38, 0x20A6, 0) // GetStdHandle 202 _w64(buf, d + 0x40, 0x20B6, 0) // WriteFile 203 _w64(buf, d + 0x48, 0x20C2, 0) // ExitProcess 204 _w64(buf, d + 0x50, 0, 0) // null 205 // IAT (RVA 0x2058) -- same, loader overwrites 206 _w64(buf, d + 0x58, 0x2088, 0) 207 _w64(buf, d + 0x60, 0x209A, 0) 208 _w64(buf, d + 0x68, 0x20A6, 0) 209 _w64(buf, d + 0x70, 0x20B6, 0) 210 _w64(buf, d + 0x78, 0x20C2, 0) 211 _w64(buf, d + 0x80, 0, 0) 212 // IMAGE_IMPORT_BY_NAME GetProcessHeap (RVA 0x2088) 213 _w16(buf, d + 0x88, 0) 214 _w8(buf,d+0x8A,71);_w8(buf,d+0x8B,101);_w8(buf,d+0x8C,116);_w8(buf,d+0x8D,80) // GetP 215 _w8(buf,d+0x8E,114);_w8(buf,d+0x8F,111);_w8(buf,d+0x90,99);_w8(buf,d+0x91,101) // roce 216 _w8(buf,d+0x92,115);_w8(buf,d+0x93,115);_w8(buf,d+0x94,72);_w8(buf,d+0x95,101) // ssHe 217 _w8(buf,d+0x96,97);_w8(buf,d+0x97,112);_w8(buf,d+0x98,0);_w8(buf,d+0x99,0) // ap\0 pad 218 // HeapAlloc (RVA 0x209A) 219 _w16(buf, d + 0x9A, 0) 220 _w8(buf,d+0x9C,72);_w8(buf,d+0x9D,101);_w8(buf,d+0x9E,97);_w8(buf,d+0x9F,112) // Heap 221 _w8(buf,d+0xA0,65);_w8(buf,d+0xA1,108);_w8(buf,d+0xA2,108);_w8(buf,d+0xA3,111) // Allo 222 _w8(buf,d+0xA4,99);_w8(buf,d+0xA5,0) // c\0 223 // GetStdHandle (RVA 0x20A6) 224 _w16(buf, d + 0xA6, 0) 225 _w8(buf,d+0xA8,71);_w8(buf,d+0xA9,101);_w8(buf,d+0xAA,116);_w8(buf,d+0xAB,83) // GetS 226 _w8(buf,d+0xAC,116);_w8(buf,d+0xAD,100);_w8(buf,d+0xAE,72);_w8(buf,d+0xAF,97) // tdHa 227 _w8(buf,d+0xB0,110);_w8(buf,d+0xB1,100);_w8(buf,d+0xB2,108);_w8(buf,d+0xB3,101) // ndle 228 _w8(buf,d+0xB4,0);_w8(buf,d+0xB5,0) // \0 pad 229 // WriteFile (RVA 0x20B6) 230 _w16(buf, d + 0xB6, 0) 231 _w8(buf,d+0xB8,87);_w8(buf,d+0xB9,114);_w8(buf,d+0xBA,105);_w8(buf,d+0xBB,116) // Writ 232 _w8(buf,d+0xBC,101);_w8(buf,d+0xBD,70);_w8(buf,d+0xBE,105);_w8(buf,d+0xBF,108) // eFil 233 _w8(buf,d+0xC0,101);_w8(buf,d+0xC1,0) // e\0 234 // ExitProcess (RVA 0x20C2) 235 _w16(buf, d + 0xC2, 0) 236 _w8(buf,d+0xC4,69);_w8(buf,d+0xC5,120);_w8(buf,d+0xC6,105);_w8(buf,d+0xC7,116) // Exit 237 _w8(buf,d+0xC8,80);_w8(buf,d+0xC9,114);_w8(buf,d+0xCA,111);_w8(buf,d+0xCB,99) // Proc 238 _w8(buf,d+0xCC,101);_w8(buf,d+0xCD,115);_w8(buf,d+0xCE,115);_w8(buf,d+0xCF,0) // ess\0 239 // "kernel32.dll\0" (RVA 0x20D0) 240 _w8(buf,d+0xD0,107);_w8(buf,d+0xD1,101);_w8(buf,d+0xD2,114);_w8(buf,d+0xD3,110) // kern 241 _w8(buf,d+0xD4,101);_w8(buf,d+0xD5,108);_w8(buf,d+0xD6,51);_w8(buf,d+0xD7,50) // el32 242 _w8(buf,d+0xD8,46);_w8(buf,d+0xD9,100);_w8(buf,d+0xDA,108);_w8(buf,d+0xDB,108);_w8(buf,d+0xDC,0) // .dll\0 243 244 return NX_PE_OK 245} 246 247func main() -> i64 { 248 let lenbox: *i64 = sys_mmap(16) as *i64 249 let src: *u8 = sys_read_file("/tmp/nxwin.s" as *u8, lenbox) 250 if (src as i64) == 0 { return 1 } 251 let n: i64 = lenbox[0] 252 if n <= 0 { return 2 } 253 254 let code: *u8 = sys_mmap(NXORG_CODE_CAP) 255 let mainbox: *i64 = sys_mmap(16) as *i64 256 let mmbox: *i64 = sys_mmap(16) as *i64 257 let swbox: *i64 = sys_mmap(16) as *i64 258 let sebox: *i64 = sys_mmap(16) as *i64 259 mainbox[0] = 0 - 1 260 mmbox[0] = 0 - 1 261 swbox[0] = 0 - 1 262 sebox[0] = 0 - 1 263 let code_len: i64 = nxorg_assemble(src, n, code, NXORG_CODE_CAP, mainbox, mmbox, swbox, sebox) 264 if code_len < 0 { return 3 } 265 let main_off: i64 = mainbox[0] 266 if main_off < 0 { return 4 } 267 let mm_off: i64 = mmbox[0] 268 if mm_off < 0 { return 6 } 269 let sw_off: i64 = swbox[0] 270 if sw_off < 0 { return 7 } 271 let se_off: i64 = sebox[0] 272 if se_off < 0 { return 8 } 273 274 let buf: *u8 = sys_mmap(NXORG_FILE_SIZE) 275 let rc: i64 = nxorg_emit_pe(buf, code, code_len, main_off, mm_off, sw_off, se_off) 276 if rc != NX_PE_OK { return 5 } 277 278 let outp: *u8 = "/mnt/c/Users/elder/nishi-core/nxc2/_offc/nx_win_compiled_organ.exe" as *u8 279 if nx_pe_write_to_file(outp, buf, NXORG_FILE_SIZE) != NX_PE_OK { return 70 } 280 281 let msg: *u8 = "[substrate] nxc2-compiled native realistic organ PE written: nx_win_compiled_organ.exe\n" as *u8 282 sys_write(1, msg, 87) 283 return 0 284}