code wiki / (root) / nx_pe_compile_win_gui.nx

nx_pe_compile_win_gui.nx source

↩ module page · 375 lines · 24386 B

1// nx_pe_compile_win_gui.nx -- GUI-R0 keystone: emits nish_gui.nx as a native Windows PE with a 2// sovereign Win32 FFI so NishiLang can drive user32/gdi32 (open a window, push a framebuffer). 3// 4// Imports (ONE contiguous IAT, indexed by FFI id = slot index; per-DLL counts come from each 5// descriptor's OriginalFirstThunk/INT, so the IAT needs no inter-DLL null splits): 6// kernel32 slots 0-6: ExitProcess GetProcessHeap HeapAlloc GetStdHandle WriteFile Sleep GetModuleHandleA 7// user32 slots 7-17: RegisterClassExA CreateWindowExA ShowWindow PeekMessageA TranslateMessage 8// DispatchMessageA DefWindowProcA GetDC ReleaseDC IsWindow LoadCursorA 9// gdi32 slot 18: SetDIBitsToDevice 10// Redirected shims: sys_mmap->HeapAlloc(zeroed), sys_write->WriteFile, sys_exit->ExitProcess, 11// sys_sleep_ms->Sleep, win32->FFI-marshalled-call, win32addr->IAT-address-of. 12// 13// Same proven PE machinery as nx_pe_compile_win_nish.nx (writable .text for statics, RIP-relative 14// code at the image base, MS-x64 thunks). lineage_id: substrate_pe_compile_win_gui_v1 15 16import "nx_syscalls.nx" 17import "nxasm_x86.nx" 18import "nx_pe_writer.nx" 19 20const GI_CODE_CAP: i64 = 0x300000 21const GI_STUB_LEN: i64 = 18 // sub/call main/mov ecx/call exit/int3 22const GI_THUNK_SLACK: i64 = 0x1000 23const GI_FOFF_TEXT: i64 = 0x200 24const GI_RVA_TEXT: i64 = 0x1000 25const GI_IDATA_FSZ: i64 = 0x600 26 27// intra-.idata layout 28const GI_DESC_K: i64 = 0x00 // kernel32 import descriptor (20) 29const GI_DESC_U: i64 = 0x14 // user32 (20) 30const GI_DESC_G: i64 = 0x28 // gdi32 (20) 31const GI_DESC_K2: i64 = 0x3C // 2nd kernel32 descriptor (ReadFile @ slot 19, additive) 32const GI_DESC_K3: i64 = 0x50 // 3rd kernel32 descriptor (CreateFileA/CreateProcessA/WaitForSingleObject/CloseHandle @ slots 20-23) 33const GI_DESC_U2: i64 = 0x64 // 2nd user32 descriptor (GetClientRect @ slot 24, additive for live resize) 34const GI_DESC_N: i64 = 0x78 // null (20) 35const GI_IAT: i64 = 0x8C // 25 slots contiguous (200B) -> ends 0x154 36const GI_INT_K: i64 = 0x154 // kernel32 INT: 7+null (64) 37const GI_INT_U: i64 = 0x194 // user32 INT: 11+null (96) 38const GI_INT_G: i64 = 0x1F4 // gdi32 INT: 1+null (16) 39const GI_INT_K2: i64 = 0x204 // kernel32#2 INT: 1+null (16) 40const GI_INT_K3: i64 = 0x214 // kernel32#3 INT: 4+null (40) 41const GI_INT_U2: i64 = 0x23C // user32#2 INT: 1+null (16) 42const GI_NAMES: i64 = 0x24C 43 44const GI_IMGBASE_LO: i64 = 0x40000000 45const GI_IMGBASE_HI: i64 = 0x1 46const GI_IMGBASE: i64 = 5368709120 // 0x1_4000_0000 47 48func gi_align(x: i64, a: i64) -> i64 { return ((x + a - 1) / a) * a } 49func gi_rva(foff: i64) -> i64 { return GI_RVA_TEXT + (foff - GI_FOFF_TEXT) } 50func gi_ec(buf: *u8, foff: i64, iat_rva: i64) -> i64 { 51 _w8(buf, foff, 0xFF); _w8(buf, foff + 1, 0x15) 52 _w32(buf, foff + 2, iat_rva - gi_rva(foff + 6)) 53 return foff + 6 54} 55func gi_redir(buf: *u8, shim_off: i64, thunk_rva: i64) -> i64 { 56 if shim_off < 0 { return 0 } 57 let sf: i64 = GI_FOFF_TEXT + GI_STUB_LEN + shim_off 58 let sr: i64 = GI_RVA_TEXT + GI_STUB_LEN + shim_off 59 _w8(buf, sf, 0xE9); _w32(buf, sf + 1, thunk_rva - (sr + 5)) 60 return 0 61} 62func gi_put_name(buf: *u8, foff: i64, s: *u8) -> i64 { 63 _w16(buf, foff, 0) 64 var i: i64 = 0 65 while s[i] != (0 as u8) { buf[foff + 2 + i] = s[i]; i = i + 1 } 66 buf[foff + 2 + i] = 0 as u8 67 var nx: i64 = foff + 2 + i + 1 68 if (nx & 1) == 1 { nx = nx + 1 } 69 return nx 70} 71func gi_put_str(buf: *u8, foff: i64, s: *u8) -> i64 { 72 var i: i64 = 0 73 while s[i] != (0 as u8) { buf[foff + i] = s[i]; i = i + 1 } 74 buf[foff + i] = 0 as u8 75 return foff + i + 1 76} 77func gi_lbloff(src: *u8, lo: *i64, ll: *i64, la: *i64, n: i64, nm: *u8) -> i64 { 78 var k: i64 = 0 79 while k < n { if axc_tok_is(src, lo[k], ll[k], nm) == 1 { return la[k] } k = k + 1 } 80 return 0 - 1 81} 82func gi_assemble(src: *u8, n: i64, out: *u8, out_cap: i64, off: *i64) -> i64 { 83 let lo: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 84 let ll: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 85 let la: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 86 let ls: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 87 let op0: *i64 = sys_mmap(72) as *i64 // 9 slots for SIB (matches nxasm_x86) 88 let op1: *i64 = sys_mmap(72) as *i64 89 let op2: *i64 = sys_mmap(72) as *i64 // API DRIFT FIX: axc_pass gained op2 90 let scratch: *u8 = sys_mmap(64) 91 let posbox: *i64 = sys_mmap(16) as *i64 92 let nlb: *i64 = sys_mmap(16) as *i64 93 nlb[0] = 0 94 let lh: *i64 = sys_mmap(ASM_LH_SIZE * 8) as *i64 95 let text_size: i64 = axc_pass(src, n, out, 0, 0, lo, ll, la, ls, nlb, lh, op0, op1, op2, scratch, posbox) 96 if text_size < 0 { return text_size } 97 let nl: i64 = nlb[0] 98 var k: i64 = 0 99 while k < nl { if ls[k] == 1 { la[k] = la[k] + text_size } k = k + 1 } 100 axc_lh_build(src, lo, ll, nl, lh) 101 let total: i64 = axc_pass(src, n, out, text_size, 1, lo, ll, la, ls, nlb, lh, op0, op1, op2, scratch, posbox) 102 if total < 0 { return total } 103 if total > out_cap { return 0 - 200 } 104 off[0] = gi_lbloff(src, lo, ll, la, nl, "main") 105 off[1] = gi_lbloff(src, lo, ll, la, nl, "sys_mmap") 106 off[2] = gi_lbloff(src, lo, ll, la, nl, "sys_write") 107 off[3] = gi_lbloff(src, lo, ll, la, nl, "sys_exit") 108 off[4] = gi_lbloff(src, lo, ll, la, nl, "sys_sleep_ms") 109 off[5] = gi_lbloff(src, lo, ll, la, nl, "win32") 110 off[6] = gi_lbloff(src, lo, ll, la, nl, "win32addr") 111 off[7] = gi_lbloff(src, lo, ll, la, nl, "sys_read") 112 return total 113} 114 115func gi_emit_pe(buf: *u8, code: *u8, code_len: i64, off: *i64, text_fsz: i64, rva_idata: i64) -> i64 { 116 if (buf as i64) == 0 { return 0 - NX_PE_BAD_INPUT } 117 if off[0] < 0 { return 0 - NX_PE_BAD_INPUT } 118 let foff_idata: i64 = GI_FOFF_TEXT + text_fsz 119 let size_of_image: i64 = rva_idata + 0x1000 120 let iat0: i64 = rva_idata + GI_IAT // IAT slot 0 RVA 121 let iat_exit: i64 = iat0 + 0 * 8 122 let iat_gph: i64 = iat0 + 1 * 8 123 let iat_ha: i64 = iat0 + 2 * 8 124 let iat_gsh: i64 = iat0 + 3 * 8 125 let iat_wf: i64 = iat0 + 4 * 8 126 let iat_sleep:i64 = iat0 + 5 * 8 127 let iat_rf: i64 = iat0 + 19 * 8 // ReadFile (2nd kernel32 descriptor) 128 let iat_base_va: i64 = GI_IMGBASE + iat0 // absolute VA of IAT slot 0 (for FFI) 129 130 // headers 131 _w16(buf, 0, 0x5A4D); _w32(buf, 0x3C, FOFF_PE_SIG); _w32(buf, FOFF_PE_SIG, 0x00004550) 132 _w16(buf, FOFF_COFF + 0, PE_MACHINE_AMD64); _w16(buf, FOFF_COFF + 2, 2) 133 _w16(buf, FOFF_COFF + 16, 0xF0); _w16(buf, FOFF_COFF + 18, PE_CHAR_EXEC | PE_CHAR_LARGE_ADDR) 134 _w16(buf, FOFF_OPT + 0, PE_OH_MAGIC_PEPLUS); _w8(buf, FOFF_OPT + 2, 1) 135 _w32(buf, FOFF_OPT + 4, text_fsz); _w32(buf, FOFF_OPT + 8, GI_IDATA_FSZ) 136 _w32(buf, FOFF_OPT + 16, GI_RVA_TEXT); _w32(buf, FOFF_OPT + 20, GI_RVA_TEXT) 137 _w64(buf, FOFF_OPT + 24, GI_IMGBASE_LO, GI_IMGBASE_HI) 138 _w32(buf, FOFF_OPT + 32, 0x1000); _w32(buf, FOFF_OPT + 36, 0x200) 139 _w16(buf, FOFF_OPT + 40, 6); _w16(buf, FOFF_OPT + 48, 6) 140 _w32(buf, FOFF_OPT + 56, size_of_image); _w32(buf, FOFF_OPT + 60, 0x200) 141 _w16(buf, FOFF_OPT + 68, 2) // IMAGE_SUBSYSTEM_WINDOWS_GUI -- clean graphical app, no console 142 _w64(buf, FOFF_OPT + 72, 0x100000, 0); _w64(buf, FOFF_OPT + 80, 0x1000, 0) 143 _w64(buf, FOFF_OPT + 88, 0x100000, 0); _w64(buf, FOFF_OPT + 96, 0x1000, 0) 144 _w32(buf, FOFF_OPT + 108, 16) 145 _w32(buf, FOFF_OPT + 112 + 8, rva_idata); _w32(buf, FOFF_OPT + 112 + 12, 0x8C) // 7 descriptors (K,U,G,K2,K3,U2,null) 146 // .text (writable: holds nish_gui's statics inline) + .idata 147 _emit_section_header(buf, FOFF_SECT_TBL, 46, 116, 101, 120, 116, 0, 0, 0, text_fsz, GI_RVA_TEXT, text_fsz, GI_FOFF_TEXT, 0xE0000020) 148 _emit_section_header(buf, FOFF_SECT_TBL + 40, 46, 105, 100, 97, 116, 97, 0, 0, 0x580, rva_idata, GI_IDATA_FSZ, foff_idata, PE_SECT_DATA_R) 149 150 // copy compiled code after the stub 151 var i: i64 = 0 152 while i < code_len { buf[GI_FOFF_TEXT + GI_STUB_LEN + i] = code[i]; i = i + 1 } 153 154 var a: i64 = GI_FOFF_TEXT + GI_STUB_LEN + code_len 155 156 // MMAP: sys_mmap(size=rdi) -> VirtualAlloc(NULL, size, MEM_COMMIT|MEM_RESERVE=0x3000, PAGE_READWRITE=0x04). 157 // Switched HeapAlloc -> VirtualAlloc so large image/decode buffers get ISOLATED page-granular regions 158 // instead of corrupting the SHARED process heap (0xC0000374 after multi-MB allocs). VirtualAlloc zero- 159 // fills committed pages, preserving the old HEAP_ZERO_MEMORY semantics. SAME 32-byte thunk length; the 160 // slot-2 import (iat_ha) is renamed HeapAlloc->VirtualAlloc below, so iat_gph (GetProcessHeap) goes unused. 161 let r_halloc: i64 = gi_rva(a) 162 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x28); a = a + 4 // sub rsp,0x28 163 _w8(buf,a+0,0x31);_w8(buf,a+1,0xC9); a = a + 2 // xor ecx,ecx (lpAddress=NULL) 164 _w8(buf,a+0,0x48);_w8(buf,a+1,0x89);_w8(buf,a+2,0xFA); a = a + 3 // mov rdx,rdi (dwSize) 165 _w8(buf,a+0,0x41);_w8(buf,a+1,0xB8);_w8(buf,a+2,0x00);_w8(buf,a+3,0x30);_w8(buf,a+4,0x00);_w8(buf,a+5,0x00); a = a + 6 // mov r8d,0x3000 166 _w8(buf,a+0,0x41);_w8(buf,a+1,0xB9);_w8(buf,a+2,0x04);_w8(buf,a+3,0x00);_w8(buf,a+4,0x00);_w8(buf,a+5,0x00); a = a + 6 // mov r9d,0x04 167 a = gi_ec(buf, a, iat_ha) // call [rip+VirtualAlloc] 168 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xC4);_w8(buf,a+3,0x28);_w8(buf,a+4,0xC3); a = a + 5 // add rsp,0x28; ret 169 170 // WRITE (console): sys_write(fd,buf,n) -> GetStdHandle(fd==2?-12:-11)+WriteFile 171 let r_write: i64 = gi_rva(a) 172 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x38) 173 _w8(buf,a+4,0x48);_w8(buf,a+5,0x89);_w8(buf,a+6,0x74);_w8(buf,a+7,0x24);_w8(buf,a+8,0x28) 174 _w8(buf,a+9,0x48);_w8(buf,a+10,0x89);_w8(buf,a+11,0x54);_w8(buf,a+12,0x24);_w8(buf,a+13,0x30) 175 _w8(buf,a+14,0xB9);_w8(buf,a+15,0xF5);_w8(buf,a+16,0xFF);_w8(buf,a+17,0xFF);_w8(buf,a+18,0xFF) 176 _w8(buf,a+19,0x83);_w8(buf,a+20,0xFF);_w8(buf,a+21,0x02) 177 _w8(buf,a+22,0x75);_w8(buf,a+23,0x05) 178 _w8(buf,a+24,0xB9);_w8(buf,a+25,0xF4);_w8(buf,a+26,0xFF);_w8(buf,a+27,0xFF);_w8(buf,a+28,0xFF) 179 a = a + 29 180 a = gi_ec(buf, a, iat_gsh) 181 _w8(buf,a+0,0x48);_w8(buf,a+1,0x89);_w8(buf,a+2,0xC1) 182 _w8(buf,a+3,0x48);_w8(buf,a+4,0x8B);_w8(buf,a+5,0x54);_w8(buf,a+6,0x24);_w8(buf,a+7,0x28) 183 _w8(buf,a+8,0x4C);_w8(buf,a+9,0x8B);_w8(buf,a+10,0x44);_w8(buf,a+11,0x24);_w8(buf,a+12,0x30) 184 _w8(buf,a+13,0x4C);_w8(buf,a+14,0x8D);_w8(buf,a+15,0x4C);_w8(buf,a+16,0x24);_w8(buf,a+17,0x28) 185 _w8(buf,a+18,0x48);_w8(buf,a+19,0xC7);_w8(buf,a+20,0x44);_w8(buf,a+21,0x24);_w8(buf,a+22,0x20);_w32(buf,a+23,0) 186 a = a + 27 187 a = gi_ec(buf, a, iat_wf) 188 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xC4);_w8(buf,a+3,0x38);_w8(buf,a+4,0xC3); a = a + 5 189 190 // EXIT: sys_exit(code) -> ExitProcess 191 let r_exit: i64 = gi_rva(a) 192 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x28) 193 _w8(buf,a+4,0x89);_w8(buf,a+5,0xF9); a = a + 6 194 a = gi_ec(buf, a, iat_exit) 195 _w8(buf,a+0,0xCC); a = a + 1 196 197 // SLEEP: sys_sleep_ms(ms=rdi) -> Sleep(ms) 198 let r_sleep: i64 = gi_rva(a) 199 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x28) 200 _w8(buf,a+4,0x48);_w8(buf,a+5,0x89);_w8(buf,a+6,0xF9); a = a + 7 201 a = gi_ec(buf, a, iat_sleep) 202 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xC4);_w8(buf,a+3,0x28);_w8(buf,a+4,0xC3); a = a + 5 203 204 // READ (stdin): sys_read(fd,buf,n) -> ReadFile(GetStdHandle(STD_INPUT), buf, n, &got); returns got. 205 // nish_gui only reads fd 0 (its piped page text), so no fd dispatch needed. 206 let r_read: i64 = gi_rva(a) 207 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x38) 208 _w8(buf,a+4,0x48);_w8(buf,a+5,0x89);_w8(buf,a+6,0x74);_w8(buf,a+7,0x24);_w8(buf,a+8,0x28) // mov [rsp+0x28],rsi 209 _w8(buf,a+9,0x48);_w8(buf,a+10,0x89);_w8(buf,a+11,0x54);_w8(buf,a+12,0x24);_w8(buf,a+13,0x30) // mov [rsp+0x30],rdx 210 _w8(buf,a+14,0xB9);_w8(buf,a+15,0xF6);_w8(buf,a+16,0xFF);_w8(buf,a+17,0xFF);_w8(buf,a+18,0xFF) // mov ecx,-10 (STD_INPUT) 211 a = a + 19 212 a = gi_ec(buf, a, iat_gsh) 213 _w8(buf,a+0,0x48);_w8(buf,a+1,0x89);_w8(buf,a+2,0xC1) // mov rcx,rax 214 _w8(buf,a+3,0x48);_w8(buf,a+4,0x8B);_w8(buf,a+5,0x54);_w8(buf,a+6,0x24);_w8(buf,a+7,0x28) // mov rdx,[rsp+0x28] 215 _w8(buf,a+8,0x4C);_w8(buf,a+9,0x8B);_w8(buf,a+10,0x44);_w8(buf,a+11,0x24);_w8(buf,a+12,0x30) // mov r8,[rsp+0x30] 216 _w8(buf,a+13,0x4C);_w8(buf,a+14,0x8D);_w8(buf,a+15,0x4C);_w8(buf,a+16,0x24);_w8(buf,a+17,0x28) // lea r9,[rsp+0x28] 217 _w8(buf,a+18,0x48);_w8(buf,a+19,0xC7);_w8(buf,a+20,0x44);_w8(buf,a+21,0x24);_w8(buf,a+22,0x20);_w32(buf,a+23,0) 218 a = a + 27 219 a = gi_ec(buf, a, iat_rf) 220 _w8(buf,a+0,0x8B);_w8(buf,a+1,0x44);_w8(buf,a+2,0x24);_w8(buf,a+3,0x28) // mov eax,[rsp+0x28] (got) 221 _w8(buf,a+4,0x48);_w8(buf,a+5,0x83);_w8(buf,a+6,0xC4);_w8(buf,a+7,0x38);_w8(buf,a+8,0xC3); a = a + 9 222 223 // WIN32ADDR: win32addr(id=rdi) -> [IAT_BASE + id*8] 224 let r_win32addr: i64 = gi_rva(a) 225 _w8(buf,a+0,0x48);_w8(buf,a+1,0xB8);_w32(buf,a+2, iat_base_va & 0xFFFFFFFF);_w32(buf,a+6, iat_base_va / 4294967296) // mov rax,IAT_BASE 226 _w8(buf,a+10,0x48);_w8(buf,a+11,0x8B);_w8(buf,a+12,0x04);_w8(buf,a+13,0xF8) // mov rax,[rax+rdi*8] 227 _w8(buf,a+14,0xC3); a = a + 15 228 229 // WIN32: win32(id=rdi, args=rsi) -> call IAT[id] with up to 12 args marshalled (MS-x64) 230 let r_win32: i64 = gi_rva(a) 231 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x68) // sub rsp,0x68 232 _w8(buf,a+4,0x48);_w8(buf,a+5,0xB8);_w32(buf,a+6, iat_base_va & 0xFFFFFFFF);_w32(buf,a+10, iat_base_va / 4294967296) // mov rax,IAT_BASE 233 _w8(buf,a+14,0x4C);_w8(buf,a+15,0x8B);_w8(buf,a+16,0x1C);_w8(buf,a+17,0xF8) // mov r11,[rax+rdi*8] (REX.WR, X=0!) 234 a = a + 18 235 // copy args[4..11] -> [rsp+0x20..0x58] 236 _w8(buf,a+0,0x48);_w8(buf,a+1,0x8B);_w8(buf,a+2,0x46);_w8(buf,a+3,0x20); _w8(buf,a+4,0x48);_w8(buf,a+5,0x89);_w8(buf,a+6,0x44);_w8(buf,a+7,0x24);_w8(buf,a+8,0x20); a=a+9 237 _w8(buf,a+0,0x48);_w8(buf,a+1,0x8B);_w8(buf,a+2,0x46);_w8(buf,a+3,0x28); _w8(buf,a+4,0x48);_w8(buf,a+5,0x89);_w8(buf,a+6,0x44);_w8(buf,a+7,0x24);_w8(buf,a+8,0x28); a=a+9 238 _w8(buf,a+0,0x48);_w8(buf,a+1,0x8B);_w8(buf,a+2,0x46);_w8(buf,a+3,0x30); _w8(buf,a+4,0x48);_w8(buf,a+5,0x89);_w8(buf,a+6,0x44);_w8(buf,a+7,0x24);_w8(buf,a+8,0x30); a=a+9 239 _w8(buf,a+0,0x48);_w8(buf,a+1,0x8B);_w8(buf,a+2,0x46);_w8(buf,a+3,0x38); _w8(buf,a+4,0x48);_w8(buf,a+5,0x89);_w8(buf,a+6,0x44);_w8(buf,a+7,0x24);_w8(buf,a+8,0x38); a=a+9 240 _w8(buf,a+0,0x48);_w8(buf,a+1,0x8B);_w8(buf,a+2,0x46);_w8(buf,a+3,0x40); _w8(buf,a+4,0x48);_w8(buf,a+5,0x89);_w8(buf,a+6,0x44);_w8(buf,a+7,0x24);_w8(buf,a+8,0x40); a=a+9 241 _w8(buf,a+0,0x48);_w8(buf,a+1,0x8B);_w8(buf,a+2,0x46);_w8(buf,a+3,0x48); _w8(buf,a+4,0x48);_w8(buf,a+5,0x89);_w8(buf,a+6,0x44);_w8(buf,a+7,0x24);_w8(buf,a+8,0x48); a=a+9 242 _w8(buf,a+0,0x48);_w8(buf,a+1,0x8B);_w8(buf,a+2,0x46);_w8(buf,a+3,0x50); _w8(buf,a+4,0x48);_w8(buf,a+5,0x89);_w8(buf,a+6,0x44);_w8(buf,a+7,0x24);_w8(buf,a+8,0x50); a=a+9 243 _w8(buf,a+0,0x48);_w8(buf,a+1,0x8B);_w8(buf,a+2,0x46);_w8(buf,a+3,0x58); _w8(buf,a+4,0x48);_w8(buf,a+5,0x89);_w8(buf,a+6,0x44);_w8(buf,a+7,0x24);_w8(buf,a+8,0x58); a=a+9 244 // registers: rcx=args[0], rdx=args[1], r8=args[2], r9=args[3] 245 _w8(buf,a+0,0x48);_w8(buf,a+1,0x8B);_w8(buf,a+2,0x0E); a=a+3 // mov rcx,[rsi] 246 _w8(buf,a+0,0x48);_w8(buf,a+1,0x8B);_w8(buf,a+2,0x56);_w8(buf,a+3,0x08); a=a+4 // mov rdx,[rsi+8] 247 _w8(buf,a+0,0x4C);_w8(buf,a+1,0x8B);_w8(buf,a+2,0x46);_w8(buf,a+3,0x10); a=a+4 // mov r8,[rsi+0x10] 248 _w8(buf,a+0,0x4C);_w8(buf,a+1,0x8B);_w8(buf,a+2,0x4E);_w8(buf,a+3,0x18); a=a+4 // mov r9,[rsi+0x18] 249 _w8(buf,a+0,0x41);_w8(buf,a+1,0xFF);_w8(buf,a+2,0xD3); a=a+3 // call r11 250 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xC4);_w8(buf,a+3,0x68);_w8(buf,a+4,0xC3); a=a+5 // add rsp,0x68; ret 251 252 if (a - GI_FOFF_TEXT) > text_fsz { return 0 - 201 } 253 254 // entry stub @ FOFF_TEXT (18B): call main, then ExitProcess(eax) 255 let t: i64 = GI_FOFF_TEXT 256 _w8(buf,t+0,0x48);_w8(buf,t+1,0x83);_w8(buf,t+2,0xEC);_w8(buf,t+3,0x28) 257 _w8(buf,t+4,0xE8);_w32(buf,t+5, 9 + off[0]) // call main 258 _w8(buf,t+9,0x89);_w8(buf,t+10,0xC1) // mov ecx,eax 259 _w8(buf,t+11,0xFF);_w8(buf,t+12,0x15);_w32(buf,t+13, iat_exit - gi_rva(t + 17)) 260 _w8(buf,t+17,0xCC) 261 262 // redirects 263 gi_redir(buf, off[1], r_halloc) // sys_mmap 264 gi_redir(buf, off[2], r_write) // sys_write 265 gi_redir(buf, off[3], r_exit) // sys_exit 266 gi_redir(buf, off[4], r_sleep) // sys_sleep_ms 267 gi_redir(buf, off[5], r_win32) // win32 268 gi_redir(buf, off[6], r_win32addr) // win32addr 269 gi_redir(buf, off[7], r_read) // sys_read (stdin) 270 271 // .idata 272 let d: i64 = foff_idata 273 var nf: i64 = d + GI_NAMES 274 // 19 function names in slot order 275 let n0: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "ExitProcess") 276 let n1: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "GetProcessHeap") 277 let n2: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "VirtualAlloc") 278 let n3: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "GetStdHandle") 279 let n4: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "WriteFile") 280 let n5: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "Sleep") 281 let n6: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "GetModuleHandleA") 282 let n7: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "RegisterClassExA") 283 let n8: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "CreateWindowExA") 284 let n9: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "ShowWindow") 285 let n10: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "PeekMessageA") 286 let n11: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "TranslateMessage") 287 let n12: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "DispatchMessageA") 288 let n13: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "DefWindowProcA") 289 let n14: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "GetDC") 290 let n15: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "ReleaseDC") 291 let n16: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "IsWindow") 292 let n17: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "LoadCursorA") 293 let n18: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "SetDIBitsToDevice") 294 let nrf: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "ReadFile") 295 let ncf: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "CreateFileA") 296 let ncp: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "CreateProcessA") 297 let nws: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "WaitForSingleObject") 298 let nch: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "CloseHandle") 299 let ngcr: i64 = rva_idata + (nf - d); nf = gi_put_name(buf, nf, "GetClientRect") 300 let nk: i64 = rva_idata + (nf - d); nf = gi_put_str(buf, nf, "kernel32.dll") 301 let nu: i64 = rva_idata + (nf - d); nf = gi_put_str(buf, nf, "user32.dll") 302 let ng: i64 = rva_idata + (nf - d); nf = gi_put_str(buf, nf, "gdi32.dll") 303 304 // descriptors: OFT=INT, Name=dll, FT=IAT slice 305 _w32(buf, d+GI_DESC_K+0, rva_idata+GI_INT_K); _w32(buf, d+GI_DESC_K+12, nk); _w32(buf, d+GI_DESC_K+16, rva_idata+GI_IAT+0) 306 _w32(buf, d+GI_DESC_U+0, rva_idata+GI_INT_U); _w32(buf, d+GI_DESC_U+12, nu); _w32(buf, d+GI_DESC_U+16, rva_idata+GI_IAT+7*8) 307 _w32(buf, d+GI_DESC_G+0, rva_idata+GI_INT_G); _w32(buf, d+GI_DESC_G+12, ng); _w32(buf, d+GI_DESC_G+16, rva_idata+GI_IAT+18*8) 308 _w32(buf, d+GI_DESC_K2+0, rva_idata+GI_INT_K2); _w32(buf, d+GI_DESC_K2+12, nk); _w32(buf, d+GI_DESC_K2+16, rva_idata+GI_IAT+19*8) 309 _w32(buf, d+GI_DESC_K3+0, rva_idata+GI_INT_K3); _w32(buf, d+GI_DESC_K3+12, nk); _w32(buf, d+GI_DESC_K3+16, rva_idata+GI_IAT+20*8) 310 _w32(buf, d+GI_DESC_U2+0, rva_idata+GI_INT_U2); _w32(buf, d+GI_DESC_U2+12, nu); _w32(buf, d+GI_DESC_U2+16, rva_idata+GI_IAT+24*8) 311 312 // IAT (contiguous, slots 0-18) + parallel INTs (each null-terminated per DLL) 313 _w64(buf, d+GI_IAT+0, n0,0); _w64(buf, d+GI_INT_K+0, n0,0) 314 _w64(buf, d+GI_IAT+8, n1,0); _w64(buf, d+GI_INT_K+8, n1,0) 315 _w64(buf, d+GI_IAT+16, n2,0); _w64(buf, d+GI_INT_K+16, n2,0) 316 _w64(buf, d+GI_IAT+24, n3,0); _w64(buf, d+GI_INT_K+24, n3,0) 317 _w64(buf, d+GI_IAT+32, n4,0); _w64(buf, d+GI_INT_K+32, n4,0) 318 _w64(buf, d+GI_IAT+40, n5,0); _w64(buf, d+GI_INT_K+40, n5,0) 319 _w64(buf, d+GI_IAT+48, n6,0); _w64(buf, d+GI_INT_K+48, n6,0) 320 _w64(buf, d+GI_INT_K+56, 0,0) // kernel32 INT null 321 _w64(buf, d+GI_IAT+56, n7,0); _w64(buf, d+GI_INT_U+0, n7,0) 322 _w64(buf, d+GI_IAT+64, n8,0); _w64(buf, d+GI_INT_U+8, n8,0) 323 _w64(buf, d+GI_IAT+72, n9,0); _w64(buf, d+GI_INT_U+16, n9,0) 324 _w64(buf, d+GI_IAT+80, n10,0); _w64(buf, d+GI_INT_U+24, n10,0) 325 _w64(buf, d+GI_IAT+88, n11,0); _w64(buf, d+GI_INT_U+32, n11,0) 326 _w64(buf, d+GI_IAT+96, n12,0); _w64(buf, d+GI_INT_U+40, n12,0) 327 _w64(buf, d+GI_IAT+104, n13,0); _w64(buf, d+GI_INT_U+48, n13,0) 328 _w64(buf, d+GI_IAT+112, n14,0); _w64(buf, d+GI_INT_U+56, n14,0) 329 _w64(buf, d+GI_IAT+120, n15,0); _w64(buf, d+GI_INT_U+64, n15,0) 330 _w64(buf, d+GI_IAT+128, n16,0); _w64(buf, d+GI_INT_U+72, n16,0) 331 _w64(buf, d+GI_IAT+136, n17,0); _w64(buf, d+GI_INT_U+80, n17,0) 332 _w64(buf, d+GI_INT_U+88, 0,0) // user32 INT null 333 _w64(buf, d+GI_IAT+144, n18,0); _w64(buf, d+GI_INT_G+0, n18,0) 334 _w64(buf, d+GI_INT_G+8, 0,0) // gdi32 INT null 335 _w64(buf, d+GI_IAT+152, nrf,0); _w64(buf, d+GI_INT_K2+0, nrf,0) // slot 19 = ReadFile (kernel32#2) 336 _w64(buf, d+GI_INT_K2+8, 0,0) // kernel32#2 INT null 337 _w64(buf, d+GI_IAT+160, ncf,0); _w64(buf, d+GI_INT_K3+0, ncf,0) // slot 20 = CreateFileA (kernel32#3) 338 _w64(buf, d+GI_IAT+168, ncp,0); _w64(buf, d+GI_INT_K3+8, ncp,0) // slot 21 = CreateProcessA 339 _w64(buf, d+GI_IAT+176, nws,0); _w64(buf, d+GI_INT_K3+16, nws,0) // slot 22 = WaitForSingleObject 340 _w64(buf, d+GI_IAT+184, nch,0); _w64(buf, d+GI_INT_K3+24, nch,0) // slot 23 = CloseHandle 341 _w64(buf, d+GI_INT_K3+32, 0,0) // kernel32#3 INT null 342 _w64(buf, d+GI_IAT+192, ngcr,0); _w64(buf, d+GI_INT_U2+0, ngcr,0) // slot 24 = GetClientRect (user32#2) 343 _w64(buf, d+GI_INT_U2+8, 0,0) // user32#2 INT null 344 // (no IAT terminator: per-DLL OFT/INT nulls give the loader its counts; a terminator at 345 // slot 19 = offset 0xE8 would collide with INT_K and zero kernel32's first import.) 346 347 return NX_PE_OK 348} 349 350func main() -> i64 { 351 let lenbox: *i64 = sys_mmap(16) as *i64 352 let src: *u8 = sys_read_file("/tmp/nxwin.s" as *u8, lenbox) 353 if (src as i64) == 0 { return 1 } 354 let n: i64 = lenbox[0] 355 if n <= 0 { return 2 } 356 let code: *u8 = sys_mmap(GI_CODE_CAP) 357 let off: *i64 = sys_mmap(16 * 8) as *i64 358 var z: i64 = 0 359 while z < 7 { off[z] = 0 - 1; z = z + 1 } 360 let code_len: i64 = gi_assemble(src, n, code, GI_CODE_CAP, off) 361 if code_len < 0 { return 3 } 362 if off[0] < 0 { return 4 } 363 let span: i64 = GI_STUB_LEN + code_len + GI_THUNK_SLACK 364 let text_fsz: i64 = gi_align(span, 0x200) 365 let rva_idata: i64 = 0x1000 + gi_align(span, 0x1000) 366 let file_size: i64 = GI_FOFF_TEXT + text_fsz + GI_IDATA_FSZ 367 let buf: *u8 = sys_mmap(file_size) 368 let rc: i64 = gi_emit_pe(buf, code, code_len, off, text_fsz, rva_idata) 369 if rc != NX_PE_OK { return 5 } 370 let outp: *u8 = "/mnt/c/Users/elder/nishi-core/nxc2/_offc/nishi.exe" as *u8 371 if nx_pe_write_to_file(outp, buf, file_size) != NX_PE_OK { return 70 } 372 let msg: *u8 = "[substrate] Nishi browser PE written\n" as *u8 373 sys_write(1, msg, 37) 374 return 0 375}