nx_pe_natbw.nx source
↩ module page · 416 lines · 25575 B
1// nx_pe_natbw.nx -- GENERAL sovereign native-Windows PE emitter.
2// Wraps a /tmp/nxwin.s (from the FULL compiler _offc/nx_compile_x86.elf, which
3// supports func types/pointers) into a native .exe, bridging Linux syscalls to
4// kernel32 by PATCHING the shim entries (jmp -> thunk). Dynamic linker-style
5// layout (.text raw = roundup(VirtualSize,0x1000) so .idata is contiguous).
6//
7// HAL bridged (8 kernel32 imports):
8// sys_mmap -> VirtualAlloc(NULL,size,MEM_COMMIT|MEM_RESERVE,PAGE_RW) [lazy/large-alloc safe]
9// sys_write -> GetStdHandle + WriteFile (fd 1/2 routed)
10// sys_exit -> ExitProcess
11// sys_thread_create -> CreateThread + MS-ABI->SysV trampoline (reads {fn,ctx})
12// sys_openat_rd -> CreateFileA (GENERIC_READ, OPEN_EXISTING)
13// sys_read -> ReadFile (returns bytesRead)
14// sys_close -> CloseHandle
15// license_tier: ORIGINAL
16import "nx_syscalls.nx"
17import "nxasm_x86.nx"
18import "nx_pe_writer.nx"
19
20const NB_CODE_CAP: i64 = 4194304 // 4MB assembled-code scratch
21const NB_BUF_CAP: i64 = 8388608 // 8MB emit buffer (covers a ~1.5MB forward .text)
22const NB_STUB_LEN: i64 = 18
23const NB_RVA_TEXT: i64 = 0x1000
24const NB_FOFF_TEXT: i64 = 0x200
25// thunk lengths (bytes) -- see the emission below
26const NB_VALEN: i64 = 32 // VirtualAlloc (mmap)
27const NB_WLEN: i64 = 66 // GetStdHandle+WriteFile (write)
28const NB_ELEN: i64 = 13 // ExitProcess (exit)
29const NB_CTLEN: i64 = 47 // CreateThread
30const NB_TRLEN: i64 = 24 // trampoline
31const NB_CFLEN: i64 = 59 // CreateFileA (openat_rd)
32const NB_RFLEN: i64 = 55 // ReadFile (read) -- incl DWORD clamp of the count
33const NB_CHLEN: i64 = 18 // CloseHandle (close)
34const NB_MULEN: i64 = 3 // munmap -> no-op (xor eax,eax; ret); process exit frees all
35const NB_WALEN: i64 = 40 // sys_futex_wait -> WaitOnAddress(addr,&cmp,4,INFINITE)
36const NB_WKLEN: i64 = 18 // sys_futex_wake -> WakeByAddressAll(addr)
37const NB_NQLEN: i64 = 38 // sys_now_us -> QueryPerformanceCounter/10 (Win10/11 QPC = 10MHz = 10 ticks/us)
38
39// write hint(u16=0)+ASCII+NUL at buf[base+off], even-align, return next off.
40func nb_name(buf: *u8, base: i64, off: i64, s: *u8) -> i64 {
41 _w16(buf, base + off, 0)
42 var i: i64 = 0
43 while s[i] != (0 as u8) { _w8(buf, base + off + 2 + i, s[i] as i64); i = i + 1 }
44 _w8(buf, base + off + 2 + i, 0)
45 var nxt: i64 = off + 2 + i + 1
46 if (nxt - (nxt / 2) * 2) != 0 { nxt = nxt + 1 }
47 return nxt
48}
49// write plain ASCII+NUL, return next off.
50func nb_str(buf: *u8, base: i64, off: i64, s: *u8) -> i64 {
51 var i: i64 = 0
52 while s[i] != (0 as u8) { _w8(buf, base + off + i, s[i] as i64); i = i + 1 }
53 _w8(buf, base + off + i, 0)
54 return off + i + 1
55}
56
57func nb_assemble(src: *u8, n: i64, out: *u8, out_cap: i64, p: *i64) -> i64 {
58 // p[0..7] = main, mmap, write, exit, thread_create, openat_rd, read, close offsets
59 let lab_off: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64
60 let lab_len: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64
61 let lab_addr: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64
62 let lab_sec: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64
63 let op0: *i64 = sys_mmap(72) as *i64 // 9 slots for SIB (matches nxasm_x86)
64 let op1: *i64 = sys_mmap(72) as *i64
65 let op2: *i64 = sys_mmap(72) as *i64 // API DRIFT FIX: axc_pass gained op2
66 let scratch: *u8 = sys_mmap(64)
67 let posbox: *i64 = sys_mmap(16) as *i64
68 let n_lab_box: *i64 = sys_mmap(16) as *i64
69 n_lab_box[0] = 0
70 let lh: *i64 = sys_mmap(ASM_LH_SIZE * 8) as *i64
71
72 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)
73 if text_size < 0 { return text_size }
74 let n_lab: i64 = n_lab_box[0]
75
76 var q: i64 = 0
77 while q < 12 { p[q] = 0 - 1; q = q + 1 }
78 var k: i64 = 0
79 while k < n_lab {
80 if lab_sec[k] == 1 { lab_addr[k] = lab_addr[k] + text_size }
81 if axc_tok_is(src, lab_off[k], lab_len[k], "main") == 1 { p[0] = lab_addr[k] }
82 if axc_tok_is(src, lab_off[k], lab_len[k], "sys_mmap") == 1 { p[1] = lab_addr[k] }
83 if axc_tok_is(src, lab_off[k], lab_len[k], "sys_write") == 1 { p[2] = lab_addr[k] }
84 if axc_tok_is(src, lab_off[k], lab_len[k], "sys_exit") == 1 { p[3] = lab_addr[k] }
85 if axc_tok_is(src, lab_off[k], lab_len[k], "sys_thread_create") == 1 { p[4] = lab_addr[k] }
86 if axc_tok_is(src, lab_off[k], lab_len[k], "sys_openat_rd") == 1 { p[5] = lab_addr[k] }
87 if axc_tok_is(src, lab_off[k], lab_len[k], "sys_read") == 1 { p[6] = lab_addr[k] }
88 if axc_tok_is(src, lab_off[k], lab_len[k], "sys_close") == 1 { p[7] = lab_addr[k] }
89 if axc_tok_is(src, lab_off[k], lab_len[k], "sys_munmap") == 1 { p[8] = lab_addr[k] }
90 if axc_tok_is(src, lab_off[k], lab_len[k], "sys_futex_wait") == 1 { p[9] = lab_addr[k] }
91 if axc_tok_is(src, lab_off[k], lab_len[k], "sys_futex_wake") == 1 { p[10] = lab_addr[k] }
92 if axc_tok_is(src, lab_off[k], lab_len[k], "sys_now_us") == 1 { p[11] = lab_addr[k] }
93 k = k + 1
94 }
95 axc_lh_build(src, lab_off, lab_len, n_lab, lh)
96 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)
97 if total < 0 { return total }
98 if total > out_cap { return 0 - 200 }
99 return total
100}
101
102// patch a syscall shim entry (at code offset `off`) with jmp rel32 -> thunk at text offset `thunk_off`.
103func nb_patch(buf: *u8, off: i64, thunk_off: i64) -> i64 {
104 if off < 0 { return 0 }
105 let at: i64 = NB_FOFF_TEXT + NB_STUB_LEN + off
106 let at_rva: i64 = NB_RVA_TEXT + NB_STUB_LEN + off
107 _w8(buf, at + 0, 0xE9)
108 _w32(buf, at + 1, thunk_off - ((NB_STUB_LEN + off) + 5)) // rel to next instr (both text offsets; == thunk RVA - next RVA)
109 return 0
110}
111
112func nb_emit_pe(buf: *u8, code: *u8, code_len: i64, p: *i64, p_fsize: *i64) -> i64 {
113 if (buf as i64) == 0 { return 0 - NX_PE_BAD_INPUT }
114 if p[1] < 0 { return 0 - NX_PE_BAD_INPUT } // sys_mmap required
115 if p[2] < 0 { return 0 - NX_PE_BAD_INPUT } // sys_write required
116 if p[3] < 0 { return 0 - NX_PE_BAD_INPUT } // sys_exit required
117
118 // ---- .text thunk layout (after stub + code) ----
119 let voff: i64 = NB_STUB_LEN + code_len
120 let woff: i64 = voff + NB_VALEN
121 let eoff: i64 = woff + NB_WLEN
122 let ctoff: i64 = eoff + NB_ELEN
123 let troff: i64 = ctoff + NB_CTLEN
124 let cfoff: i64 = troff + NB_TRLEN
125 let rfoff: i64 = cfoff + NB_CFLEN
126 let choff: i64 = rfoff + NB_RFLEN
127 let muoff: i64 = choff + NB_CHLEN
128 let waoff: i64 = muoff + NB_MULEN
129 let wkoff: i64 = waoff + NB_WALEN
130 let nqoff: i64 = wkoff + NB_WKLEN
131 let text_vsize: i64 = nqoff + NB_NQLEN
132
133 let text_raw: i64 = ((text_vsize + 0xFFF) / 0x1000) * 0x1000
134 let rva_idata: i64 = NB_RVA_TEXT + text_raw
135 let foff_idata: i64 = NB_FOFF_TEXT + text_raw
136 let size_image: i64 = 0x1000 + text_raw + 0x1000
137 let file_size: i64 = foff_idata + 0x200
138 if file_size > NB_BUF_CAP { return 0 - NX_PE_BAD_INPUT }
139 // IAT slots (11 imports), IAT base = rva_idata + 0x88 (INT 0x28 = 12 qwords: 11 names + null)
140 let iat_va: i64 = rva_idata + 0x88
141 let iat_gsh: i64 = rva_idata + 0x90
142 let iat_wf: i64 = rva_idata + 0x98
143 let iat_exit: i64 = rva_idata + 0xA0
144 let iat_ct: i64 = rva_idata + 0xA8
145 let iat_cf: i64 = rva_idata + 0xB0
146 let iat_rf: i64 = rva_idata + 0xB8
147 let iat_ch: i64 = rva_idata + 0xC0
148 let iat_wa: i64 = rva_idata + 0xC8
149 let iat_wba: i64 = rva_idata + 0xD0
150 let iat_qpc: i64 = rva_idata + 0xD8
151
152 // ---- headers ----
153 _w16(buf, 0, 0x5A4D)
154 _w32(buf, 0x3C, FOFF_PE_SIG)
155 _w32(buf, FOFF_PE_SIG, 0x00004550)
156 _w16(buf, FOFF_COFF + 0, PE_MACHINE_AMD64)
157 _w16(buf, FOFF_COFF + 2, 2)
158 _w16(buf, FOFF_COFF + 16, 0xF0)
159 _w16(buf, FOFF_COFF + 18, PE_CHAR_EXEC | PE_CHAR_LARGE_ADDR)
160 _w16(buf, FOFF_OPT + 0, PE_OH_MAGIC_PEPLUS)
161 _w8(buf, FOFF_OPT + 2, 1)
162 _w32(buf, FOFF_OPT + 4, text_raw)
163 _w32(buf, FOFF_OPT + 8, 0x200)
164 _w32(buf, FOFF_OPT + 16, NB_RVA_TEXT)
165 _w32(buf, FOFF_OPT + 20, NB_RVA_TEXT)
166 _w64(buf, FOFF_OPT + 24, IMG_BASE_LO, IMG_BASE_HI)
167 _w32(buf, FOFF_OPT + 32, 0x1000)
168 _w32(buf, FOFF_OPT + 36, 0x200)
169 _w16(buf, FOFF_OPT + 40, 6)
170 _w16(buf, FOFF_OPT + 48, 6)
171 _w32(buf, FOFF_OPT + 56, size_image)
172 _w32(buf, FOFF_OPT + 60, 0x200)
173 _w16(buf, FOFF_OPT + 68, PE_SUBSYSTEM_CONSOLE)
174 _w64(buf, FOFF_OPT + 72, 0x1000000, 0) // SizeOfStackReserve = 16MB (Linux main stack is 8MB; 1MB overflows the deep forward call chain)
175 _w64(buf, FOFF_OPT + 80, 0x100000, 0) // SizeOfStackCommit = 1MB
176 _w64(buf, FOFF_OPT + 88, 0x100000, 0)
177 _w64(buf, FOFF_OPT + 96, 0x1000, 0)
178 _w32(buf, FOFF_OPT + 108, 16)
179 _w32(buf, FOFF_OPT + 112 + 8, rva_idata)
180 _w32(buf, FOFF_OPT + 112 + 12, 0x28)
181 _emit_section_header(buf, FOFF_SECT_TBL, 46, 116, 101, 120, 116, 0, 0, 0, text_vsize, NB_RVA_TEXT, text_raw, NB_FOFF_TEXT, PE_SECT_CODE_X_R | 0x80000000) // R+W+X: module-level mutable globals live in .text (e.g. g_q5repack_consts cache) -> must be writable
182 _emit_section_header(buf, FOFF_SECT_TBL + 40, 46, 105, 100, 97, 116, 97, 0, 0, 0x200, rva_idata, 0x200, foff_idata, PE_SECT_DATA_R)
183
184 // ---- .text entry stub: sub rsp,0x28; call main; mov ecx,eax; call [ExitProcess]; int3 ----
185 let t: i64 = NB_FOFF_TEXT
186 _w8(buf, t+0, 0x48); _w8(buf, t+1, 0x83); _w8(buf, t+2, 0xEC); _w8(buf, t+3, 0x28)
187 _w8(buf, t+4, 0xE8); _w32(buf, t+5, 0x9 + p[0])
188 _w8(buf, t+9, 0x89); _w8(buf, t+10, 0xC1)
189 _w8(buf, t+11, 0xFF); _w8(buf, t+12, 0x15); _w32(buf, t+13, iat_exit - (NB_RVA_TEXT + 17))
190 _w8(buf, t+17, 0xCC)
191 var i: i64 = 0
192 while i < code_len { buf[t + NB_STUB_LEN + i] = code[i]; i = i + 1 }
193
194 // ---- VirtualAlloc thunk (sys_mmap) ----
195 let vrva: i64 = NB_RVA_TEXT + voff
196 let v: i64 = NB_FOFF_TEXT + voff
197 _w8(buf, v+0, 0x48); _w8(buf, v+1, 0x83); _w8(buf, v+2, 0xEC); _w8(buf, v+3, 0x28) // sub rsp,0x28
198 _w8(buf, v+4, 0x31); _w8(buf, v+5, 0xC9) // xor ecx,ecx
199 _w8(buf, v+6, 0x48); _w8(buf, v+7, 0x89); _w8(buf, v+8, 0xFA) // mov rdx,rdi
200 _w8(buf, v+9, 0x41); _w8(buf, v+10, 0xB8); _w8(buf, v+11, 0x00); _w8(buf, v+12, 0x30); _w8(buf, v+13, 0x00); _w8(buf, v+14, 0x00) // mov r8d,0x3000
201 _w8(buf, v+15, 0x41); _w8(buf, v+16, 0xB9); _w8(buf, v+17, 0x04); _w8(buf, v+18, 0x00); _w8(buf, v+19, 0x00); _w8(buf, v+20, 0x00) // mov r9d,4
202 _w8(buf, v+21, 0xFF); _w8(buf, v+22, 0x15); _w32(buf, v+23, iat_va - (vrva + 27)) // call [VirtualAlloc]
203 _w8(buf, v+27, 0x48); _w8(buf, v+28, 0x83); _w8(buf, v+29, 0xC4); _w8(buf, v+30, 0x28) // add rsp,0x28
204 _w8(buf, v+31, 0xC3)
205
206 // ---- WRITE thunk (sys_write -> GetStdHandle + WriteFile) ----
207 let wrva: i64 = NB_RVA_TEXT + woff
208 let w: i64 = NB_FOFF_TEXT + woff
209 _w8(buf, w+0, 0x48); _w8(buf, w+1, 0x83); _w8(buf, w+2, 0xEC); _w8(buf, w+3, 0x38)
210 _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)
211 _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)
212 _w8(buf, w+14, 0x83); _w8(buf, w+15, 0xFF); _w8(buf, w+16, 0x02)
213 _w8(buf, w+17, 0x75); _w8(buf, w+18, 0x05)
214 _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)
215 _w8(buf, w+24, 0xFF); _w8(buf, w+25, 0x15); _w32(buf, w+26, iat_gsh - (wrva + 30))
216 _w8(buf, w+30, 0x48); _w8(buf, w+31, 0x89); _w8(buf, w+32, 0xC1)
217 _w8(buf, w+33, 0x48); _w8(buf, w+34, 0x89); _w8(buf, w+35, 0xF2)
218 _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)
219 _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)
220 _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)
221 _w8(buf, w+55, 0xFF); _w8(buf, w+56, 0x15); _w32(buf, w+57, iat_wf - (wrva + 61))
222 _w8(buf, w+61, 0x48); _w8(buf, w+62, 0x83); _w8(buf, w+63, 0xC4); _w8(buf, w+64, 0x38)
223 _w8(buf, w+65, 0xC3)
224
225 // ---- EXIT thunk (sys_exit -> ExitProcess) ----
226 let erva: i64 = NB_RVA_TEXT + eoff
227 let e: i64 = NB_FOFF_TEXT + eoff
228 _w8(buf, e+0, 0x89); _w8(buf, e+1, 0xF9)
229 _w8(buf, e+2, 0x48); _w8(buf, e+3, 0x83); _w8(buf, e+4, 0xEC); _w8(buf, e+5, 0x28)
230 _w8(buf, e+6, 0xFF); _w8(buf, e+7, 0x15); _w32(buf, e+8, iat_exit - (erva + 12))
231 _w8(buf, e+12, 0xCC)
232
233 // ---- CreateThread thunk (sys_thread_create) ----
234 let ctrva: i64 = NB_RVA_TEXT + ctoff
235 let ct: i64 = NB_FOFF_TEXT + ctoff
236 _w8(buf, ct+0, 0x48); _w8(buf, ct+1, 0x83); _w8(buf, ct+2, 0xEC); _w8(buf, ct+3, 0x38)
237 _w8(buf, ct+4, 0x31); _w8(buf, ct+5, 0xC9)
238 _w8(buf, ct+6, 0x31); _w8(buf, ct+7, 0xD2)
239 _w8(buf, ct+8, 0x4C); _w8(buf, ct+9, 0x8D); _w8(buf, ct+10, 0x05); _w32(buf, ct+11, troff - (ctoff + 15))
240 _w8(buf, ct+15, 0x49); _w8(buf, ct+16, 0x89); _w8(buf, ct+17, 0xF9)
241 _w8(buf, ct+18, 0x48); _w8(buf, ct+19, 0xC7); _w8(buf, ct+20, 0x44); _w8(buf, ct+21, 0x24); _w8(buf, ct+22, 0x20); _w32(buf, ct+23, 0)
242 _w8(buf, ct+27, 0x48); _w8(buf, ct+28, 0xC7); _w8(buf, ct+29, 0x44); _w8(buf, ct+30, 0x24); _w8(buf, ct+31, 0x28); _w32(buf, ct+32, 0)
243 _w8(buf, ct+36, 0xFF); _w8(buf, ct+37, 0x15); _w32(buf, ct+38, iat_ct - (ctrva + 42))
244 _w8(buf, ct+42, 0x48); _w8(buf, ct+43, 0x83); _w8(buf, ct+44, 0xC4); _w8(buf, ct+45, 0x38)
245 _w8(buf, ct+46, 0xC3)
246
247 // ---- trampoline (MS-ABI rcx={fn,ctx} -> SysV fn(ctx)) ----
248 let tr: i64 = NB_FOFF_TEXT + troff
249 _w8(buf, tr+0, 0x56)
250 _w8(buf, tr+1, 0x57)
251 _w8(buf, tr+2, 0x48); _w8(buf, tr+3, 0x83); _w8(buf, tr+4, 0xEC); _w8(buf, tr+5, 0x28)
252 _w8(buf, tr+6, 0x48); _w8(buf, tr+7, 0x8B); _w8(buf, tr+8, 0x01)
253 _w8(buf, tr+9, 0x48); _w8(buf, tr+10, 0x8B); _w8(buf, tr+11, 0x79); _w8(buf, tr+12, 0x08)
254 _w8(buf, tr+13, 0xFF); _w8(buf, tr+14, 0xD0)
255 _w8(buf, tr+15, 0x48); _w8(buf, tr+16, 0x83); _w8(buf, tr+17, 0xC4); _w8(buf, tr+18, 0x28)
256 _w8(buf, tr+19, 0x5F)
257 _w8(buf, tr+20, 0x5E)
258 _w8(buf, tr+21, 0x31); _w8(buf, tr+22, 0xC0)
259 _w8(buf, tr+23, 0xC3)
260
261 // ---- CreateFileA thunk (sys_openat_rd) ----
262 let cfrva: i64 = NB_RVA_TEXT + cfoff
263 let cf: i64 = NB_FOFF_TEXT + cfoff
264 _w8(buf, cf+0, 0x48); _w8(buf, cf+1, 0x83); _w8(buf, cf+2, 0xEC); _w8(buf, cf+3, 0x48) // sub rsp,0x48
265 _w8(buf, cf+4, 0x48); _w8(buf, cf+5, 0x89); _w8(buf, cf+6, 0xF9) // mov rcx,rdi (path)
266 _w8(buf, cf+7, 0xBA); _w8(buf, cf+8, 0x00); _w8(buf, cf+9, 0x00); _w8(buf, cf+10, 0x00); _w8(buf, cf+11, 0x80) // mov edx,0x80000000
267 _w8(buf, cf+12, 0x41); _w8(buf, cf+13, 0xB8); _w8(buf, cf+14, 0x01); _w8(buf, cf+15, 0x00); _w8(buf, cf+16, 0x00); _w8(buf, cf+17, 0x00) // mov r8d,1
268 _w8(buf, cf+18, 0x45); _w8(buf, cf+19, 0x31); _w8(buf, cf+20, 0xC9) // xor r9d,r9d
269 _w8(buf, cf+21, 0x48); _w8(buf, cf+22, 0xC7); _w8(buf, cf+23, 0x44); _w8(buf, cf+24, 0x24); _w8(buf, cf+25, 0x20); _w32(buf, cf+26, 3) // mov qword[rsp+0x20],3
270 _w8(buf, cf+30, 0x48); _w8(buf, cf+31, 0xC7); _w8(buf, cf+32, 0x44); _w8(buf, cf+33, 0x24); _w8(buf, cf+34, 0x28); _w32(buf, cf+35, 0x80) // mov qword[rsp+0x28],0x80
271 _w8(buf, cf+39, 0x48); _w8(buf, cf+40, 0xC7); _w8(buf, cf+41, 0x44); _w8(buf, cf+42, 0x24); _w8(buf, cf+43, 0x30); _w32(buf, cf+44, 0) // mov qword[rsp+0x30],0
272 _w8(buf, cf+48, 0xFF); _w8(buf, cf+49, 0x15); _w32(buf, cf+50, iat_cf - (cfrva + 54)) // call [CreateFileA]
273 _w8(buf, cf+54, 0x48); _w8(buf, cf+55, 0x83); _w8(buf, cf+56, 0xC4); _w8(buf, cf+57, 0x48)
274 _w8(buf, cf+58, 0xC3)
275
276 // ---- ReadFile thunk (sys_read) ----
277 let rfrva: i64 = NB_RVA_TEXT + rfoff
278 let rf: i64 = NB_FOFF_TEXT + rfoff
279 _w8(buf, rf+0, 0x48); _w8(buf, rf+1, 0x83); _w8(buf, rf+2, 0xEC); _w8(buf, rf+3, 0x38) // sub rsp,0x38
280 _w8(buf, rf+4, 0x49); _w8(buf, rf+5, 0x89); _w8(buf, rf+6, 0xD0) // mov r8,rdx (n)
281 _w8(buf, rf+7, 0xB8); _w8(buf, rf+8, 0xFF); _w8(buf, rf+9, 0xFF); _w8(buf, rf+10, 0xFF); _w8(buf, rf+11, 0x7F) // mov eax,0x7FFFFFFF
282 _w8(buf, rf+12, 0x49); _w8(buf, rf+13, 0x39); _w8(buf, rf+14, 0xC0) // cmp r8,rax
283 _w8(buf, rf+15, 0x4C); _w8(buf, rf+16, 0x0F); _w8(buf, rf+17, 0x47); _w8(buf, rf+18, 0xC0) // cmova r8,rax (clamp count to DWORD-safe)
284 _w8(buf, rf+19, 0x48); _w8(buf, rf+20, 0x89); _w8(buf, rf+21, 0xF2) // mov rdx,rsi (buf)
285 _w8(buf, rf+22, 0x48); _w8(buf, rf+23, 0x89); _w8(buf, rf+24, 0xF9) // mov rcx,rdi (fd)
286 _w8(buf, rf+25, 0x4C); _w8(buf, rf+26, 0x8D); _w8(buf, rf+27, 0x4C); _w8(buf, rf+28, 0x24); _w8(buf, rf+29, 0x30) // lea r9,[rsp+0x30]
287 _w8(buf, rf+30, 0x48); _w8(buf, rf+31, 0xC7); _w8(buf, rf+32, 0x44); _w8(buf, rf+33, 0x24); _w8(buf, rf+34, 0x20); _w32(buf, rf+35, 0) // mov qword[rsp+0x20],0
288 _w8(buf, rf+39, 0xFF); _w8(buf, rf+40, 0x15); _w32(buf, rf+41, iat_rf - (rfrva + 45)) // call [ReadFile]
289 _w8(buf, rf+45, 0x48); _w8(buf, rf+46, 0x8B); _w8(buf, rf+47, 0x44); _w8(buf, rf+48, 0x24); _w8(buf, rf+49, 0x30) // mov rax,[rsp+0x30]
290 _w8(buf, rf+50, 0x48); _w8(buf, rf+51, 0x83); _w8(buf, rf+52, 0xC4); _w8(buf, rf+53, 0x38)
291 _w8(buf, rf+54, 0xC3)
292
293 // ---- CloseHandle thunk (sys_close) ----
294 let chrva: i64 = NB_RVA_TEXT + choff
295 let ch: i64 = NB_FOFF_TEXT + choff
296 _w8(buf, ch+0, 0x48); _w8(buf, ch+1, 0x83); _w8(buf, ch+2, 0xEC); _w8(buf, ch+3, 0x28)
297 _w8(buf, ch+4, 0x48); _w8(buf, ch+5, 0x89); _w8(buf, ch+6, 0xF9)
298 _w8(buf, ch+7, 0xFF); _w8(buf, ch+8, 0x15); _w32(buf, ch+9, iat_ch - (chrva + 13))
299 _w8(buf, ch+13, 0x48); _w8(buf, ch+14, 0x83); _w8(buf, ch+15, 0xC4); _w8(buf, ch+16, 0x28)
300 _w8(buf, ch+17, 0xC3)
301
302 // ---- munmap no-op thunk (xor eax,eax; ret) ----
303 let mu: i64 = NB_FOFF_TEXT + muoff
304 _w8(buf, mu+0, 0x31); _w8(buf, mu+1, 0xC0) // xor eax,eax
305 _w8(buf, mu+2, 0xC3) // ret
306
307 // ---- WaitOnAddress thunk (sys_futex_wait: rdi=addr, rsi=seen) ----
308 let warva: i64 = NB_RVA_TEXT + waoff
309 let wa: i64 = NB_FOFF_TEXT + waoff
310 _w8(buf, wa+0, 0x48); _w8(buf, wa+1, 0x83); _w8(buf, wa+2, 0xEC); _w8(buf, wa+3, 0x38) // sub rsp,0x38
311 _w8(buf, wa+4, 0x48); _w8(buf, wa+5, 0x89); _w8(buf, wa+6, 0x74); _w8(buf, wa+7, 0x24); _w8(buf, wa+8, 0x28) // mov [rsp+0x28],rsi (compare value=seen)
312 _w8(buf, wa+9, 0x48); _w8(buf, wa+10, 0x89); _w8(buf, wa+11, 0xF9) // mov rcx,rdi (Address=addr)
313 _w8(buf, wa+12, 0x48); _w8(buf, wa+13, 0x8D); _w8(buf, wa+14, 0x54); _w8(buf, wa+15, 0x24); _w8(buf, wa+16, 0x28) // lea rdx,[rsp+0x28] (CompareAddress)
314 _w8(buf, wa+17, 0x41); _w8(buf, wa+18, 0xB8); _w8(buf, wa+19, 0x04); _w8(buf, wa+20, 0x00); _w8(buf, wa+21, 0x00); _w8(buf, wa+22, 0x00) // mov r8d,4 (AddressSize)
315 _w8(buf, wa+23, 0x41); _w8(buf, wa+24, 0xB9); _w8(buf, wa+25, 0xFF); _w8(buf, wa+26, 0xFF); _w8(buf, wa+27, 0xFF); _w8(buf, wa+28, 0xFF) // mov r9d,-1 (INFINITE)
316 _w8(buf, wa+29, 0xFF); _w8(buf, wa+30, 0x15); _w32(buf, wa+31, iat_wa - (warva + 35)) // call [WaitOnAddress]
317 _w8(buf, wa+35, 0x48); _w8(buf, wa+36, 0x83); _w8(buf, wa+37, 0xC4); _w8(buf, wa+38, 0x38) // add rsp,0x38
318 _w8(buf, wa+39, 0xC3)
319
320 // ---- WakeByAddressAll thunk (sys_futex_wake: rdi=addr) ----
321 let wkrva: i64 = NB_RVA_TEXT + wkoff
322 let wk: i64 = NB_FOFF_TEXT + wkoff
323 _w8(buf, wk+0, 0x48); _w8(buf, wk+1, 0x83); _w8(buf, wk+2, 0xEC); _w8(buf, wk+3, 0x28) // sub rsp,0x28
324 _w8(buf, wk+4, 0x48); _w8(buf, wk+5, 0x89); _w8(buf, wk+6, 0xF9) // mov rcx,rdi
325 _w8(buf, wk+7, 0xFF); _w8(buf, wk+8, 0x15); _w32(buf, wk+9, iat_wba - (wkrva + 13)) // call [WakeByAddressAll]
326 _w8(buf, wk+13, 0x48); _w8(buf, wk+14, 0x83); _w8(buf, wk+15, 0xC4); _w8(buf, wk+16, 0x28) // add rsp,0x28
327 _w8(buf, wk+17, 0xC3)
328
329 // ---- QueryPerformanceCounter thunk (sys_now_us -> QPC(&c)/10 = microseconds) ----
330 let nqrva: i64 = NB_RVA_TEXT + nqoff
331 let nq: i64 = NB_FOFF_TEXT + nqoff
332 _w8(buf, nq+0, 0x48); _w8(buf, nq+1, 0x83); _w8(buf, nq+2, 0xEC); _w8(buf, nq+3, 0x38) // sub rsp,0x38
333 _w8(buf, nq+4, 0x48); _w8(buf, nq+5, 0x8D); _w8(buf, nq+6, 0x4C); _w8(buf, nq+7, 0x24); _w8(buf, nq+8, 0x28) // lea rcx,[rsp+0x28]
334 _w8(buf, nq+9, 0xFF); _w8(buf, nq+10, 0x15); _w32(buf, nq+11, iat_qpc - (nqrva + 15)) // call [QueryPerformanceCounter]
335 _w8(buf, nq+15, 0x48); _w8(buf, nq+16, 0x8B); _w8(buf, nq+17, 0x44); _w8(buf, nq+18, 0x24); _w8(buf, nq+19, 0x28) // mov rax,[rsp+0x28] (100ns ticks)
336 _w8(buf, nq+20, 0x48); _w8(buf, nq+21, 0xC7); _w8(buf, nq+22, 0xC1); _w8(buf, nq+23, 0x0A); _w8(buf, nq+24, 0x00); _w8(buf, nq+25, 0x00); _w8(buf, nq+26, 0x00) // mov rcx,10
337 _w8(buf, nq+27, 0x48); _w8(buf, nq+28, 0x31); _w8(buf, nq+29, 0xD2) // xor rdx,rdx
338 _w8(buf, nq+30, 0x48); _w8(buf, nq+31, 0xF7); _w8(buf, nq+32, 0xF1) // div rcx (rax = ticks/10 = microseconds)
339 _w8(buf, nq+33, 0x48); _w8(buf, nq+34, 0x83); _w8(buf, nq+35, 0xC4); _w8(buf, nq+36, 0x38) // add rsp,0x38
340 _w8(buf, nq+37, 0xC3) // ret
341
342 // ---- patch shims -> thunks ----
343 nb_patch(buf, p[1], voff) // sys_mmap -> VirtualAlloc
344 nb_patch(buf, p[2], woff) // sys_write -> WriteFile
345 nb_patch(buf, p[3], eoff) // sys_exit -> ExitProcess
346 nb_patch(buf, p[4], ctoff) // sys_thread_create -> CreateThread
347 nb_patch(buf, p[5], cfoff) // sys_openat_rd -> CreateFileA
348 nb_patch(buf, p[6], rfoff) // sys_read -> ReadFile
349 nb_patch(buf, p[7], choff) // sys_close -> CloseHandle
350 nb_patch(buf, p[8], muoff) // sys_munmap -> no-op
351 nb_patch(buf, p[9], waoff) // sys_futex_wait -> WaitOnAddress
352 nb_patch(buf, p[10], wkoff) // sys_futex_wake -> WakeByAddressAll
353 nb_patch(buf, p[11], nqoff) // sys_now_us -> QueryPerformanceCounter (skipped if p[11]<0, i.e. gate doesn't call it)
354
355 // ---- .idata: 11 kernelbase imports (INT 0x28, IAT 0x88, names 0xE8) ----
356 let d: i64 = foff_idata
357 var no: i64 = 0xE8
358 let o_va: i64 = no; no = nb_name(buf, d, no, "VirtualAlloc")
359 let o_gsh: i64 = no; no = nb_name(buf, d, no, "GetStdHandle")
360 let o_wf: i64 = no; no = nb_name(buf, d, no, "WriteFile")
361 let o_ex: i64 = no; no = nb_name(buf, d, no, "ExitProcess")
362 let o_ct: i64 = no; no = nb_name(buf, d, no, "CreateThread")
363 let o_cf: i64 = no; no = nb_name(buf, d, no, "CreateFileA")
364 let o_rf: i64 = no; no = nb_name(buf, d, no, "ReadFile")
365 let o_ch: i64 = no; no = nb_name(buf, d, no, "CloseHandle")
366 let o_wa: i64 = no; no = nb_name(buf, d, no, "WaitOnAddress")
367 let o_wk: i64 = no; no = nb_name(buf, d, no, "WakeByAddressAll")
368 let o_qpc: i64 = no; no = nb_name(buf, d, no, "QueryPerformanceCounter")
369 let o_k32: i64 = no; no = nb_str(buf, d, no, "kernelbase.dll")
370 // descriptor
371 _w32(buf, d + 0, rva_idata + 0x28)
372 _w32(buf, d + 12, rva_idata + o_k32)
373 _w32(buf, d + 16, rva_idata + 0x88)
374 // INT (0x28) + IAT (0x80): 10 name RVAs each, then null
375 _w64(buf, d + 0x28, rva_idata + o_va, 0); _w64(buf, d + 0x88, rva_idata + o_va, 0)
376 _w64(buf, d + 0x30, rva_idata + o_gsh, 0); _w64(buf, d + 0x90, rva_idata + o_gsh, 0)
377 _w64(buf, d + 0x38, rva_idata + o_wf, 0); _w64(buf, d + 0x98, rva_idata + o_wf, 0)
378 _w64(buf, d + 0x40, rva_idata + o_ex, 0); _w64(buf, d + 0xA0, rva_idata + o_ex, 0)
379 _w64(buf, d + 0x48, rva_idata + o_ct, 0); _w64(buf, d + 0xA8, rva_idata + o_ct, 0)
380 _w64(buf, d + 0x50, rva_idata + o_cf, 0); _w64(buf, d + 0xB0, rva_idata + o_cf, 0)
381 _w64(buf, d + 0x58, rva_idata + o_rf, 0); _w64(buf, d + 0xB8, rva_idata + o_rf, 0)
382 _w64(buf, d + 0x60, rva_idata + o_ch, 0); _w64(buf, d + 0xC0, rva_idata + o_ch, 0)
383 _w64(buf, d + 0x68, rva_idata + o_wa, 0); _w64(buf, d + 0xC8, rva_idata + o_wa, 0)
384 _w64(buf, d + 0x70, rva_idata + o_wk, 0); _w64(buf, d + 0xD0, rva_idata + o_wk, 0)
385 _w64(buf, d + 0x78, rva_idata + o_qpc, 0); _w64(buf, d + 0xD8, rva_idata + o_qpc, 0)
386 _w64(buf, d + 0x80, 0, 0); _w64(buf, d + 0xE0, 0, 0)
387
388 p_fsize[0] = file_size
389 return NX_PE_OK
390}
391
392func main() -> i64 {
393 let lenbox: *i64 = sys_mmap(16) as *i64
394 let src: *u8 = sys_read_file("/tmp/nxwin.s" as *u8, lenbox)
395 if (src as i64) == 0 { return 1 }
396 let n: i64 = lenbox[0]
397 if n <= 0 { return 2 }
398
399 let code: *u8 = sys_mmap(NB_CODE_CAP)
400 let p: *i64 = sys_mmap(12 * 8) as *i64
401 let code_len: i64 = nb_assemble(src, n, code, NB_CODE_CAP, p)
402 if code_len < 0 { return 3 }
403 if p[0] < 0 { return 4 }
404
405 let buf: *u8 = sys_mmap(NB_BUF_CAP)
406 let fsizebox: *i64 = sys_mmap(16) as *i64
407 fsizebox[0] = 0
408 let rc: i64 = nb_emit_pe(buf, code, code_len, p, fsizebox)
409 if rc != NX_PE_OK { return 5 }
410 let fsize: i64 = fsizebox[0]
411
412 let outp: *u8 = "/mnt/c/Users/elder/nishi-core/nxc2/_offc/nx_natbw_probe.exe" as *u8
413 if nx_pe_write_to_file(outp, buf, fsize) != NX_PE_OK { return 70 }
414 sys_write(1, "[substrate] native PE (8-import HAL) written\n" as *u8, 45)
415 return 0
416}