code wiki / (root) / nx_pe_compile_win_halloc.nx

nx_pe_compile_win_halloc.nx source

↩ module page · 214 lines · 11012 B

1// nx_pe_compile_win_halloc.nx -- W3b-5a' : sys_mmap reaches a BENIGN allocator (HeapAlloc). 2// 3// W3b-5a redirected sys_mmap -> VirtualAlloc, which Windows Defender quarantined (a minimal hand-PE 4// importing VirtualAlloc matches the shellcode-loader heuristic; see pe_compile_win_mmap_gate.log). 5// This rung uses the recommended benign alternative: sys_mmap -> HeapAlloc(GetProcessHeap(), 0, size), 6// the allocation pattern of ordinary programs, which should not trip the heuristic. No compiler/asm edits. 7// 8// thunk (SysV in: rdi=size): 9// sub rsp, 0x28 ; shadow + 16-byte align 10// call [rip+GetProcessHeap] ; rax = default heap handle; rdi(size) survives (MS-x64 non-volatile) 11// mov rcx, rax ; arg1 hHeap 12// xor edx, edx ; arg2 dwFlags = 0 13// mov r8, rdi ; arg3 dwBytes = size 14// call [rip+HeapAlloc] ; rax = pointer 15// add rsp, 0x28 / ret ; rax = base pointer (SysV return) 16// 17// NO-FALSE-GREEN: source `{ let p = sys_mmap(4096); p[0]=77; p[1]=22; return p[0]+p[1] }`. Exit 99 18// proves HeapAlloc returned a VALID, WRITABLE region the compiled code wrote+read back; a NULL/bad 19// pointer faults (0xC0000005). Tamper (corrupt HeapAlloc import name) -> 0xC0000139. 20// 21// PIPELINE (build WSL sovereign, run native): src.nx -> nx_compile_x86_native.elf > /tmp/nxwin.s 22// -> nx_sov_build_run.elf nx_pe_compile_win_halloc -> _offc/nx_win_compiled_halloc.exe -> run native. 23// 24// HONEST SCOPE: single redirect (sys_mmap) via HeapAlloc; combine with the W3b-5b table for a full 25// multi-syscall organ. 3 imports (GetProcessHeap, HeapAlloc, ExitProcess). Replicates keystone 26// assemble (reuses axc_pass; no shared edit). lineage_id: substrate_pe_compile_win_halloc_v1 27 28import "nx_syscalls.nx" 29import "nxasm_x86.nx" 30import "nx_pe_writer.nx" 31 32const NXH_CODE_CAP: i64 = 1048576 33const NXH_FILE_SIZE: i64 = 0x600 // headers + .text + .idata 34const NXH_STUB_LEN: i64 = 18 35const NXH_THUNK_LEN: i64 = 29 36const NXH_TEXT_CAP: i64 = 0x200 37const NXH_RVA_TEXT: i64 = 0x1000 38const NXH_RVA_IDATA: i64 = 0x2000 39const NXH_FOFF_TEXT: i64 = 0x200 40const NXH_FOFF_IDATA:i64 = 0x400 41const NXH_IAT_GPH: i64 = 0x2048 // IAT GetProcessHeap RVA 42const NXH_IAT_HA: i64 = 0x2050 // IAT HeapAlloc RVA 43const NXH_IAT_EXIT: i64 = 0x2058 // IAT ExitProcess RVA 44 45func nxh_assemble(src: *u8, n: i64, out: *u8, out_cap: i64, p_main: *i64, p_mm: *i64) -> i64 { 46 let lab_off: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 47 let lab_len: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 48 let lab_addr: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 49 let lab_sec: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 50 let op0: *i64 = sys_mmap(72) as *i64 // 9 slots for SIB (matches nxasm_x86) 51 let op1: *i64 = sys_mmap(72) as *i64 52 let op2: *i64 = sys_mmap(72) as *i64 // API DRIFT FIX: axc_pass gained op2 53 let scratch: *u8 = sys_mmap(64) 54 let posbox: *i64 = sys_mmap(16) as *i64 55 let n_lab_box: *i64 = sys_mmap(16) as *i64 56 n_lab_box[0] = 0 57 let lh: *i64 = sys_mmap(ASM_LH_SIZE * 8) as *i64 58 59 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) 60 if text_size < 0 { return text_size } 61 let n_lab: i64 = n_lab_box[0] 62 63 var main_a: i64 = 0 - 1 64 var mm_a: i64 = 0 - 1 65 var k: i64 = 0 66 while k < n_lab { 67 if lab_sec[k] == 1 { lab_addr[k] = lab_addr[k] + text_size } 68 if axc_tok_is(src, lab_off[k], lab_len[k], "main") == 1 { main_a = lab_addr[k] } 69 if axc_tok_is(src, lab_off[k], lab_len[k], "sys_mmap") == 1 { mm_a = lab_addr[k] } 70 k = k + 1 71 } 72 axc_lh_build(src, lab_off, lab_len, n_lab, lh) 73 74 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) 75 if total < 0 { return total } 76 if total > out_cap { return 0 - 200 } 77 p_main[0] = main_a 78 p_mm[0] = mm_a 79 return total 80} 81 82func nxh_emit_pe(buf: *u8, code: *u8, code_len: i64, main_off: i64, mm_off: i64) -> i64 { 83 if (buf as i64) == 0 { return 0 - NX_PE_BAD_INPUT } 84 if mm_off < 0 { return 0 - NX_PE_BAD_INPUT } 85 let thunk_off: i64 = NXH_STUB_LEN + code_len 86 let text_vsize: i64 = thunk_off + NXH_THUNK_LEN 87 if text_vsize > NXH_TEXT_CAP { return 0 - NX_PE_BAD_INPUT } 88 89 _w16(buf, 0, 0x5A4D) 90 _w32(buf, 0x3C, FOFF_PE_SIG) 91 _w32(buf, FOFF_PE_SIG, 0x00004550) 92 _w16(buf, FOFF_COFF + 0, PE_MACHINE_AMD64) 93 _w16(buf, FOFF_COFF + 2, 2) 94 _w16(buf, FOFF_COFF + 16, 0xF0) 95 _w16(buf, FOFF_COFF + 18, PE_CHAR_EXEC | PE_CHAR_LARGE_ADDR) 96 _w16(buf, FOFF_OPT + 0, PE_OH_MAGIC_PEPLUS) 97 _w8(buf, FOFF_OPT + 2, 1) 98 _w32(buf, FOFF_OPT + 4, 0x200) 99 _w32(buf, FOFF_OPT + 8, 0x200) 100 _w32(buf, FOFF_OPT + 16, NXH_RVA_TEXT) 101 _w32(buf, FOFF_OPT + 20, NXH_RVA_TEXT) 102 _w64(buf, FOFF_OPT + 24, IMG_BASE_LO, IMG_BASE_HI) 103 _w32(buf, FOFF_OPT + 32, 0x1000) 104 _w32(buf, FOFF_OPT + 36, 0x200) 105 _w16(buf, FOFF_OPT + 40, 6) 106 _w16(buf, FOFF_OPT + 48, 6) 107 _w32(buf, FOFF_OPT + 56, 0x3000) 108 _w32(buf, FOFF_OPT + 60, 0x200) 109 _w16(buf, FOFF_OPT + 68, PE_SUBSYSTEM_CONSOLE) 110 _w64(buf, FOFF_OPT + 72, 0x100000, 0) 111 _w64(buf, FOFF_OPT + 80, 0x1000, 0) 112 _w64(buf, FOFF_OPT + 88, 0x100000, 0) 113 _w64(buf, FOFF_OPT + 96, 0x1000, 0) 114 _w32(buf, FOFF_OPT + 108, 16) 115 _w32(buf, FOFF_OPT + 112 + 8, NXH_RVA_IDATA) 116 _w32(buf, FOFF_OPT + 112 + 12, 0x28) 117 _emit_section_header(buf, FOFF_SECT_TBL, 46, 116, 101, 120, 116, 0, 0, 0, text_vsize, NXH_RVA_TEXT, 0x200, NXH_FOFF_TEXT, PE_SECT_CODE_X_R) 118 _emit_section_header(buf, FOFF_SECT_TBL + 40, 46, 105, 100, 97, 116, 97, 0, 0, 0xA1, NXH_RVA_IDATA, 0x200, NXH_FOFF_IDATA, PE_SECT_DATA_R) 119 120 // ===== .text: entry stub ===== 121 let t: i64 = NXH_FOFF_TEXT 122 _w8(buf, t+0, 0x48); _w8(buf, t+1, 0x83); _w8(buf, t+2, 0xEC); _w8(buf, t+3, 0x28) 123 _w8(buf, t+4, 0xE8); _w32(buf, t+5, 0x9 + main_off) 124 _w8(buf, t+9, 0x89); _w8(buf, t+10, 0xC1) 125 _w8(buf, t+11, 0xFF); _w8(buf, t+12, 0x15); _w32(buf, t+13, NXH_IAT_EXIT - (NXH_RVA_TEXT + 17)) 126 _w8(buf, t+17, 0xCC) 127 var i: i64 = 0 128 while i < code_len { buf[t + NXH_STUB_LEN + i] = code[i]; i = i + 1 } 129 130 // ===== LINKER STEP: GetProcessHeap+HeapAlloc thunk + redirect sys_mmap ===== 131 let thunk_rva: i64 = NXH_RVA_TEXT + thunk_off 132 let th: i64 = NXH_FOFF_TEXT + thunk_off 133 _w8(buf, th+0, 0x48); _w8(buf, th+1, 0x83); _w8(buf, th+2, 0xEC); _w8(buf, th+3, 0x28) // sub rsp,0x28 134 _w8(buf, th+4, 0xFF); _w8(buf, th+5, 0x15); _w32(buf, th+6, NXH_IAT_GPH - (thunk_rva + 10)) // call [rip] GetProcessHeap 135 _w8(buf, th+10, 0x48); _w8(buf, th+11, 0x89); _w8(buf, th+12, 0xC1) // mov rcx,rax (hHeap) 136 _w8(buf, th+13, 0x31); _w8(buf, th+14, 0xD2) // xor edx,edx (flags=0) 137 _w8(buf, th+15, 0x49); _w8(buf, th+16, 0x89); _w8(buf, th+17, 0xF8) // mov r8,rdi (size) 138 _w8(buf, th+18, 0xFF); _w8(buf, th+19, 0x15); _w32(buf, th+20, NXH_IAT_HA - (thunk_rva + 24)) // call [rip] HeapAlloc 139 _w8(buf, th+24, 0x48); _w8(buf, th+25, 0x83); _w8(buf, th+26, 0xC4); _w8(buf, th+27, 0x28) // add rsp,0x28 140 _w8(buf, th+28, 0xC3) // ret 141 // OVERWRITE sys_mmap's first 5 bytes with `jmp rel32` -> thunk. 142 let mm_rva: i64 = NXH_RVA_TEXT + NXH_STUB_LEN + mm_off 143 let mm: i64 = NXH_FOFF_TEXT + NXH_STUB_LEN + mm_off 144 _w8(buf, mm+0, 0xE9); _w32(buf, mm+1, thunk_rva - (mm_rva + 5)) 145 146 // ===== .idata: 3 kernel32 imports (GetProcessHeap, HeapAlloc, ExitProcess) ===== 147 let d: i64 = NXH_FOFF_IDATA 148 _w32(buf, d + 0, 0x2028) // OriginalFirstThunk = INT RVA 149 _w32(buf, d + 12, 0x2094) // Name = "kernel32.dll" RVA 150 _w32(buf, d + 16, 0x2048) // FirstThunk = IAT RVA 151 // INT (RVA 0x2028) 152 _w64(buf, d + 0x28, 0x2068, 0) // -> GetProcessHeap 153 _w64(buf, d + 0x30, 0x207A, 0) // -> HeapAlloc 154 _w64(buf, d + 0x38, 0x2086, 0) // -> ExitProcess 155 _w64(buf, d + 0x40, 0, 0) // null 156 // IAT (RVA 0x2048) 157 _w64(buf, d + 0x48, 0x2068, 0) 158 _w64(buf, d + 0x50, 0x207A, 0) 159 _w64(buf, d + 0x58, 0x2086, 0) 160 _w64(buf, d + 0x60, 0, 0) 161 // IMAGE_IMPORT_BY_NAME GetProcessHeap (RVA 0x2068) 162 _w16(buf, d + 0x68, 0) 163 _w8(buf,d+0x6A,71);_w8(buf,d+0x6B,101);_w8(buf,d+0x6C,116);_w8(buf,d+0x6D,80) // GetP 164 _w8(buf,d+0x6E,114);_w8(buf,d+0x6F,111);_w8(buf,d+0x70,99);_w8(buf,d+0x71,101) // roce 165 _w8(buf,d+0x72,115);_w8(buf,d+0x73,115);_w8(buf,d+0x74,72);_w8(buf,d+0x75,101) // ssHe 166 _w8(buf,d+0x76,97);_w8(buf,d+0x77,112);_w8(buf,d+0x78,0);_w8(buf,d+0x79,0) // ap\0 pad 167 // IMAGE_IMPORT_BY_NAME HeapAlloc (RVA 0x207A) 168 _w16(buf, d + 0x7A, 0) 169 _w8(buf,d+0x7C,72);_w8(buf,d+0x7D,101);_w8(buf,d+0x7E,97);_w8(buf,d+0x7F,112) // Heap 170 _w8(buf,d+0x80,65);_w8(buf,d+0x81,108);_w8(buf,d+0x82,108);_w8(buf,d+0x83,111) // Allo 171 _w8(buf,d+0x84,99);_w8(buf,d+0x85,0) // c\0 172 // IMAGE_IMPORT_BY_NAME ExitProcess (RVA 0x2086) 173 _w16(buf, d + 0x86, 0) 174 _w8(buf,d+0x88,69);_w8(buf,d+0x89,120);_w8(buf,d+0x8A,105);_w8(buf,d+0x8B,116) // Exit 175 _w8(buf,d+0x8C,80);_w8(buf,d+0x8D,114);_w8(buf,d+0x8E,111);_w8(buf,d+0x8F,99) // Proc 176 _w8(buf,d+0x90,101);_w8(buf,d+0x91,115);_w8(buf,d+0x92,115);_w8(buf,d+0x93,0) // ess\0 177 // "kernel32.dll\0" (RVA 0x2094) 178 _w8(buf,d+0x94,107);_w8(buf,d+0x95,101);_w8(buf,d+0x96,114);_w8(buf,d+0x97,110) // kern 179 _w8(buf,d+0x98,101);_w8(buf,d+0x99,108);_w8(buf,d+0x9A,51);_w8(buf,d+0x9B,50) // el32 180 _w8(buf,d+0x9C,46);_w8(buf,d+0x9D,100);_w8(buf,d+0x9E,108);_w8(buf,d+0x9F,108);_w8(buf,d+0xA0,0) // .dll\0 181 182 return NX_PE_OK 183} 184 185func main() -> i64 { 186 let lenbox: *i64 = sys_mmap(16) as *i64 187 let src: *u8 = sys_read_file("/tmp/nxwin.s" as *u8, lenbox) 188 if (src as i64) == 0 { return 1 } 189 let n: i64 = lenbox[0] 190 if n <= 0 { return 2 } 191 192 let code: *u8 = sys_mmap(NXH_CODE_CAP) 193 let mainbox: *i64 = sys_mmap(16) as *i64 194 let mmbox: *i64 = sys_mmap(16) as *i64 195 mainbox[0] = 0 - 1 196 mmbox[0] = 0 - 1 197 let code_len: i64 = nxh_assemble(src, n, code, NXH_CODE_CAP, mainbox, mmbox) 198 if code_len < 0 { return 3 } 199 let main_off: i64 = mainbox[0] 200 if main_off < 0 { return 4 } 201 let mm_off: i64 = mmbox[0] 202 if mm_off < 0 { return 6 } 203 204 let buf: *u8 = sys_mmap(NXH_FILE_SIZE) 205 let rc: i64 = nxh_emit_pe(buf, code, code_len, main_off, mm_off) 206 if rc != NX_PE_OK { return 5 } 207 208 let outp: *u8 = "/mnt/c/Users/elder/nishi-core/nxc2/_offc/nx_win_compiled_halloc.exe" as *u8 209 if nx_pe_write_to_file(outp, buf, NXH_FILE_SIZE) != NX_PE_OK { return 70 } 210 211 let msg: *u8 = "[substrate] nxc2-compiled native HeapAlloc PE written: nx_win_compiled_halloc.exe\n" as *u8 212 sys_write(1, msg, 82) 213 return 0 214}