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}