nx_pe_compile_win_nishi.nx source
↩ module page · 714 lines · 47033 B
1// nx_pe_compile_win_nish.nx -- W-NISH: the SOVEREIGN NISHI BROWSER (nish), native Windows PE.
2//
3// Keystone-linker: takes nxc2-compiled nish (runtime/_hdl_build/nish.nx -> /tmp/nxwin.s via
4// nx_compile_x86_native) and emits a NATIVE Windows .exe whose Linux syscalls are redirected to
5// Win32 (kernel32 + ws2_32) thunks -- no WSL, no gcc, no curl/openssl/zlib. Mirrors the proven
6// nx_pe_compile_win_uiserve.nx pattern, sized for nish's ~0.8MB position-independent .text.
7//
8// STAGE 3 (live): real thunks for the whole reached syscall surface so nish performs a live HTTPS
9// fetch + render natively. WSAStartup is injected into the entry stub (nish never calls it -- it is
10// a Linux program). Stage 2a (kernel32-only, network neutralized) already proved the PE layout +
11// 1.34MB CA-bundle read + clock + console on the real nish; this stage adds the network layer.
12//
13// nish's reached syscall surface (measured from its .s, call-site counts):
14// sys_mmap x471 -> HeapAlloc; sys_write x14 -> WriteFile(fd<=2) | send(socket);
15// sys_read x7 -> stdin ReadFile(fd==0) | recv(socket); sys_close x12 -> closesocket;
16// sys_exit x4 -> ExitProcess; sys_socket x3 -> socket(honor args, TCP+UDP);
17// sys_connect x2 -> connect; sys_sendto/sys_recvfrom -> UDP DNS; sys_openat_rd x2 ->
18// CreateFileA (/dev/urandom -> fail -> deterministic entropy fallback); sys_read_file -> CA bundle;
19// sys_now_realtime_sec -> GetSystemTimeAsFileTime; sys_setsockopt x2 -> 0 (blocking sockets).
20// Everything else compiled-in but never called.
21//
22// ABI bridge: compiled nish is SysV; Win32 callees are MS-x64. The intersection of registers a SysV
23// caller assumes survive a call {rbx,rbp,rsp,r12-r15} is preserved by every Win64 callee, so the
24// thunks just remap args (rdi/rsi/rdx/rcx/r8/r9 -> rcx/rdx/r8/r9/[rsp+20]/[rsp+28]) and return in rax.
25// Image has no .reloc -> loads at preferred base 0x140000000, so absolute VAs in the url/argv blob
26// are computable; the compiled code itself is fully RIP-relative (75 leaq .L..(%rip), 0 absolute).
27//
28// lineage_id: substrate_pe_compile_win_nish_v2 (tutor-scaffold; back-fill = data-driven N-from-spec).
29
30import "nx_syscalls.nx"
31import "nxasm_x86.nx"
32import "nx_pe_writer.nx"
33
34const NSH_CODE_CAP: i64 = 0x300000 // 3 MiB assembled-code ceiling
35const NSH_STUB_LEN: i64 = 40 // sub/call WSAStartup/call build_argv/mov edi/mov rsi/call main/mov ecx/call exit/int3
36const NSH_THUNK_SLACK: i64 = 0x1000 // reserved after code for thunks + url/argv blob
37const NSH_FOFF_TEXT: i64 = 0x200
38const NSH_RVA_TEXT: i64 = 0x1000
39const NSH_IDATA_FSZ: i64 = 0x400
40
41// intra-.idata layout (2 DLLs: kernel32 [10 funcs] + ws2_32 [8 funcs])
42const NSH_DESC_K: i64 = 0x00 // kernel32 import descriptor (20)
43const NSH_DESC_W: i64 = 0x14 // ws2_32 import descriptor (20)
44const NSH_DESC_N: i64 = 0x28 // null descriptor (20)
45const NSH_INT_K: i64 = 0x3C // kernel32 INT: 11+null (96)
46const NSH_INT_W: i64 = 0x9C // ws2_32 INT: 8+null (72)
47const NSH_IAT_K: i64 = 0xE4 // kernel32 IAT: 11+null (96)
48const NSH_IAT_W: i64 = 0x144 // ws2_32 IAT: 8+null (72)
49const NSH_NAMES: i64 = 0x18C // IMAGE_IMPORT_BY_NAME entries, then dll names
50
51// kernel32 indices (iat = rva_idata + NSH_IAT_K + k*8)
52const KF_EXIT: i64 = 0
53const KF_GPH: i64 = 1
54const KF_HA: i64 = 2
55const KF_GSH: i64 = 3
56const KF_WF: i64 = 4
57const KF_RF: i64 = 5
58const KF_CFA: i64 = 6
59const KF_GFS: i64 = 7
60const KF_GST: i64 = 8
61const KF_CH: i64 = 9
62const KF_GCL: i64 = 10 // GetCommandLineA
63// ws2_32 indices (iat = rva_idata + NSH_IAT_W + j*8)
64const WF_WSA: i64 = 0
65const WF_SOCK: i64 = 1
66const WF_CONN: i64 = 2
67const WF_SEND: i64 = 3
68const WF_RECV: i64 = 4
69const WF_STO: i64 = 5
70const WF_RFROM: i64 = 6
71const WF_CLOSE: i64 = 7
72
73const NSH_IMGBASE_LO: i64 = 0x40000000
74const NSH_IMGBASE_HI: i64 = 0x1
75const NSH_IMGBASE: i64 = 5368709120 // 0x1_4000_0000
76// fd handed back by the OPENAT thunk for "/dev/urandom" (which CreateFileA can't open on Windows). A
77// sys_read on this fd is routed to the RDRAND hardware-CSPRNG fill in the READ thunk -> real TLS entropy.
78const NSH_RAND_SENTINEL: i64 = 0x52414E44 // "RAND"
79
80func _nsh_align(x: i64, a: i64) -> i64 { return ((x + a - 1) / a) * a }
81func _nsh_rva(foff: i64) -> i64 { return NSH_RVA_TEXT + (foff - NSH_FOFF_TEXT) }
82
83// emit `call qword ptr [rip+disp32]` (FF 15) at foff targeting absolute RVA iat_rva; returns foff+6
84func _nsh_ec(buf: *u8, foff: i64, iat_rva: i64) -> i64 {
85 _w8(buf, foff, 0xFF); _w8(buf, foff + 1, 0x15)
86 _w32(buf, foff + 2, iat_rva - _nsh_rva(foff + 6))
87 return foff + 6
88}
89
90// overwrite a shim's first 5 bytes with `jmp rel32` -> thunk_rva (shim follows the entry stub)
91func _nsh_redir(buf: *u8, shim_off: i64, thunk_rva: i64) -> i64 {
92 if shim_off < 0 { return 0 }
93 let sf: i64 = NSH_FOFF_TEXT + NSH_STUB_LEN + shim_off
94 let sr: i64 = NSH_RVA_TEXT + NSH_STUB_LEN + shim_off
95 _w8(buf, sf, 0xE9)
96 _w32(buf, sf + 1, thunk_rva - (sr + 5))
97 return 0
98}
99
100// place an IMAGE_IMPORT_BY_NAME (hint u16=0 + name + NUL), padded even; returns next foff
101func _nsh_put_name(buf: *u8, foff: i64, s: *u8) -> i64 {
102 _w16(buf, foff, 0)
103 var i: i64 = 0
104 while s[i] != (0 as u8) { buf[foff + 2 + i] = s[i]; i = i + 1 }
105 buf[foff + 2 + i] = 0 as u8
106 var nx: i64 = foff + 2 + i + 1
107 if (nx & 1) == 1 { nx = nx + 1 }
108 return nx
109}
110
111func _nsh_put_str(buf: *u8, foff: i64, s: *u8) -> i64 {
112 var i: i64 = 0
113 while s[i] != (0 as u8) { buf[foff + i] = s[i]; i = i + 1 }
114 buf[foff + i] = 0 as u8
115 return foff + i + 1
116}
117
118func nsh_lbloff(src: *u8, lo: *i64, ll: *i64, la: *i64, n: i64, nm: *u8) -> i64 {
119 var k: i64 = 0
120 while k < n { if axc_tok_is(src, lo[k], ll[k], nm) == 1 { return la[k] } k = k + 1 }
121 return 0 - 1
122}
123
124func nsh_assemble(src: *u8, n: i64, out: *u8, out_cap: i64, off: *i64) -> i64 {
125 let lo: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64
126 let ll: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64
127 let la: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64
128 let ls: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64
129 let op0: *i64 = sys_mmap(72) as *i64 // 9 slots for SIB (matches nxasm_x86)
130 let op1: *i64 = sys_mmap(72) as *i64
131 let op2: *i64 = sys_mmap(72) as *i64 // API DRIFT FIX: axc_pass gained op2
132 let scratch: *u8 = sys_mmap(64)
133 let posbox: *i64 = sys_mmap(16) as *i64
134 let nlb: *i64 = sys_mmap(16) as *i64
135 nlb[0] = 0
136 let lh: *i64 = sys_mmap(ASM_LH_SIZE * 8) as *i64
137
138 let text_size: i64 = axc_pass(src, n, out, 0, 0, lo, ll, la, ls, nlb, lh, op0, op1, op2, scratch, posbox)
139 if text_size < 0 { return text_size }
140 let nl: i64 = nlb[0]
141 var k: i64 = 0
142 while k < nl { if ls[k] == 1 { la[k] = la[k] + text_size } k = k + 1 }
143 axc_lh_build(src, lo, ll, nl, lh)
144 let total: i64 = axc_pass(src, n, out, text_size, 1, lo, ll, la, ls, nlb, lh, op0, op1, op2, scratch, posbox)
145 if total < 0 { return total }
146 if total > out_cap { return 0 - 200 }
147
148 off[0] = nsh_lbloff(src, lo, ll, la, nl, "main")
149 off[1] = nsh_lbloff(src, lo, ll, la, nl, "sys_mmap")
150 off[2] = nsh_lbloff(src, lo, ll, la, nl, "sys_write")
151 off[3] = nsh_lbloff(src, lo, ll, la, nl, "sys_exit")
152 off[4] = nsh_lbloff(src, lo, ll, la, nl, "sys_read_file")
153 off[5] = nsh_lbloff(src, lo, ll, la, nl, "sys_now_realtime_sec")
154 off[6] = nsh_lbloff(src, lo, ll, la, nl, "sys_openat_rd")
155 off[7] = nsh_lbloff(src, lo, ll, la, nl, "sys_read")
156 off[8] = nsh_lbloff(src, lo, ll, la, nl, "sys_close")
157 off[9] = nsh_lbloff(src, lo, ll, la, nl, "sys_socket")
158 off[10] = nsh_lbloff(src, lo, ll, la, nl, "sys_connect")
159 off[11] = nsh_lbloff(src, lo, ll, la, nl, "sys_sendto")
160 off[12] = nsh_lbloff(src, lo, ll, la, nl, "sys_recvfrom")
161 off[13] = nsh_lbloff(src, lo, ll, la, nl, "sys_setsockopt")
162 off[14] = nsh_lbloff(src, lo, ll, la, nl, "sys_clock_gettime_real")
163 off[15] = nsh_lbloff(src, lo, ll, la, nl, "sys_clock_gettime_mono")
164 return total
165}
166
167func nsh_emit_pe(buf: *u8, code: *u8, code_len: i64, off: *i64,
168 file_size: i64, text_fsz: i64, rva_idata: i64) -> i64 {
169 if (buf as i64) == 0 { return 0 - NX_PE_BAD_INPUT }
170 if off[0] < 0 { return 0 - NX_PE_BAD_INPUT }
171
172 let foff_idata: i64 = NSH_FOFF_TEXT + text_fsz
173 let size_of_image: i64 = rva_idata + 0x1000
174
175 // IAT slot RVAs
176 let iat_exit: i64 = rva_idata + NSH_IAT_K + KF_EXIT * 8
177 let iat_gph: i64 = rva_idata + NSH_IAT_K + KF_GPH * 8
178 let iat_ha: i64 = rva_idata + NSH_IAT_K + KF_HA * 8
179 let iat_gsh: i64 = rva_idata + NSH_IAT_K + KF_GSH * 8
180 let iat_wf: i64 = rva_idata + NSH_IAT_K + KF_WF * 8
181 let iat_rf: i64 = rva_idata + NSH_IAT_K + KF_RF * 8
182 let iat_cfa: i64 = rva_idata + NSH_IAT_K + KF_CFA * 8
183 let iat_gfs: i64 = rva_idata + NSH_IAT_K + KF_GFS * 8
184 let iat_gst: i64 = rva_idata + NSH_IAT_K + KF_GST * 8
185 let iat_ch: i64 = rva_idata + NSH_IAT_K + KF_CH * 8
186 let iat_gcl: i64 = rva_idata + NSH_IAT_K + KF_GCL * 8
187 let wiat_wsa: i64 = rva_idata + NSH_IAT_W + WF_WSA * 8
188 let wiat_sock: i64 = rva_idata + NSH_IAT_W + WF_SOCK * 8
189 let wiat_conn: i64 = rva_idata + NSH_IAT_W + WF_CONN * 8
190 let wiat_send: i64 = rva_idata + NSH_IAT_W + WF_SEND * 8
191 let wiat_recv: i64 = rva_idata + NSH_IAT_W + WF_RECV * 8
192 let wiat_sto: i64 = rva_idata + NSH_IAT_W + WF_STO * 8
193 let wiat_rfrom: i64 = rva_idata + NSH_IAT_W + WF_RFROM * 8
194 let wiat_close: i64 = rva_idata + NSH_IAT_W + WF_CLOSE * 8
195
196 // ===== headers =====
197 _w16(buf, 0, 0x5A4D); _w32(buf, 0x3C, FOFF_PE_SIG); _w32(buf, FOFF_PE_SIG, 0x00004550)
198 _w16(buf, FOFF_COFF + 0, PE_MACHINE_AMD64); _w16(buf, FOFF_COFF + 2, 2)
199 _w16(buf, FOFF_COFF + 16, 0xF0); _w16(buf, FOFF_COFF + 18, PE_CHAR_EXEC | PE_CHAR_LARGE_ADDR)
200 _w16(buf, FOFF_OPT + 0, PE_OH_MAGIC_PEPLUS); _w8(buf, FOFF_OPT + 2, 1)
201 _w32(buf, FOFF_OPT + 4, text_fsz); _w32(buf, FOFF_OPT + 8, NSH_IDATA_FSZ)
202 _w32(buf, FOFF_OPT + 16, NSH_RVA_TEXT); _w32(buf, FOFF_OPT + 20, NSH_RVA_TEXT)
203 _w64(buf, FOFF_OPT + 24, NSH_IMGBASE_LO, NSH_IMGBASE_HI)
204 _w32(buf, FOFF_OPT + 32, 0x1000); _w32(buf, FOFF_OPT + 36, 0x200)
205 _w16(buf, FOFF_OPT + 40, 6); _w16(buf, FOFF_OPT + 48, 6)
206 _w32(buf, FOFF_OPT + 56, size_of_image); _w32(buf, FOFF_OPT + 60, 0x200)
207 _w16(buf, FOFF_OPT + 68, PE_SUBSYSTEM_CONSOLE)
208 _w64(buf, FOFF_OPT + 72, 0x100000, 0); _w64(buf, FOFF_OPT + 80, 0x1000, 0)
209 _w64(buf, FOFF_OPT + 88, 0x100000, 0); _w64(buf, FOFF_OPT + 96, 0x1000, 0)
210 _w32(buf, FOFF_OPT + 108, 16)
211 _w32(buf, FOFF_OPT + 112 + 8, rva_idata); _w32(buf, FOFF_OPT + 112 + 12, 0x3C)
212
213 // .text is CODE|EXECUTE|READ|WRITE (0xE0000020): the blob holds nish's WRITABLE statics (scratch-
214 // arena state NX_SCRATCH_NBLOCKS/cursor/block-ptrs, rand_fd_slot) inline -- on Linux these live in
215 // a writable .bss, but here code+data share one section, so it MUST be writable or the crypto's first
216 // static write (e.g. NX_SCRATCH_NBLOCKS=1) faults (0xC0000005). [refine later: split a .data section]
217 _emit_section_header(buf, FOFF_SECT_TBL, 46, 116, 101, 120, 116, 0, 0, 0, text_fsz, NSH_RVA_TEXT, text_fsz, NSH_FOFF_TEXT, 0xE0000020)
218 _emit_section_header(buf, FOFF_SECT_TBL + 40, 46, 105, 100, 97, 116, 97, 0, 0, 0x380, rva_idata, NSH_IDATA_FSZ, foff_idata, PE_SECT_DATA_R)
219
220 // ===== copy compiled nish code after the entry stub =====
221 var i: i64 = 0
222 while i < code_len { buf[NSH_FOFF_TEXT + NSH_STUB_LEN + i] = code[i]; i = i + 1 }
223
224 // ===== writable argv array (build_argv fills it from GetCommandLineA at runtime). Placed FIRST,
225 // before the thunks, so its RVA is known to build_argv + the entry stub. 8 slots, zeroed. =====
226 var a: i64 = NSH_FOFF_TEXT + NSH_STUB_LEN + code_len
227 let r_argv: i64 = _nsh_rva(a)
228 var zq: i64 = 0
229 while zq < 64 { buf[a + zq] = 0 as u8; zq = zq + 1 }
230 a = a + 64
231
232 // HALLOC: sys_mmap(size=rdi) -> HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size). rdi is
233 // Win64-nonvolatile. HEAP_ZERO_MEMORY (0x8) is REQUIRED: nish was written for Linux mmap, which
234 // returns zero-filled pages; bare HeapAlloc returns uninitialized memory -> garbage in zero-assumed
235 // struct/buffer fields -> access violation in the TLS path.
236 let r_halloc: i64 = _nsh_rva(a)
237 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x28); a = a + 4
238 a = _nsh_ec(buf, a, iat_gph)
239 _w8(buf,a+0,0x48);_w8(buf,a+1,0x89);_w8(buf,a+2,0xC1) // mov rcx,rax
240 _w8(buf,a+3,0xBA);_w32(buf,a+4,8) // mov edx,8 (HEAP_ZERO_MEMORY)
241 _w8(buf,a+8,0x49);_w8(buf,a+9,0x89);_w8(buf,a+10,0xF8); a = a + 11 // mov r8,rdi
242 a = _nsh_ec(buf, a, iat_ha)
243 _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
244
245 // WRITE: sys_write(fd=rdi,buf=rsi,n=rdx) -> fd<=2 ? WriteFile : send
246 let r_write: i64 = _nsh_rva(a)
247 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xFF);_w8(buf,a+3,0x02) // cmp rdi,2
248 _w8(buf,a+4,0x7F) // jg L_SEND (patched)
249 let wjg: i64 = a + 5
250 a = a + 6
251 let wcs: i64 = a
252 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x38) // sub rsp,0x38
253 _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)
254 _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)
255 _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) // mov ecx,-11
256 _w8(buf,a+19,0x83);_w8(buf,a+20,0xFF);_w8(buf,a+21,0x02) // cmp edi,2
257 _w8(buf,a+22,0x75);_w8(buf,a+23,0x05) // jne +5
258 _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) // mov ecx,-12
259 a = a + 29
260 a = _nsh_ec(buf, a, iat_gsh)
261 _w8(buf,a+0,0x48);_w8(buf,a+1,0x89);_w8(buf,a+2,0xC1)
262 _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)
263 _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)
264 _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)
265 _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)
266 a = a + 27
267 a = _nsh_ec(buf, a, iat_wf)
268 _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
269 _w8(buf, wjg, a - wcs) // patch jg rel8
270 // L_SEND: send(fd,buf,n,0)
271 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x28)
272 _w8(buf,a+4,0x49);_w8(buf,a+5,0x89);_w8(buf,a+6,0xD0) // mov r8,rdx
273 _w8(buf,a+7,0x48);_w8(buf,a+8,0x89);_w8(buf,a+9,0xF2) // mov rdx,rsi
274 _w8(buf,a+10,0x48);_w8(buf,a+11,0x89);_w8(buf,a+12,0xF9) // mov rcx,rdi
275 _w8(buf,a+13,0x45);_w8(buf,a+14,0x31);_w8(buf,a+15,0xC9) // xor r9d,r9d
276 a = a + 16
277 a = _nsh_ec(buf, a, wiat_send)
278 _w8(buf,a+0,0x48);_w8(buf,a+1,0x98); a = a + 2 // cdqe: int->i64
279 _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
280
281 // URAND: sys_read(fd==NSH_RAND_SENTINEL) -> fill rsi[0..rdx) with hardware entropy, return rdx.
282 // CPUID-GUARDED so it never #UD-faults: leaf-1 ECX bit 30 = RDRAND support. Present -> on-die RDRAND
283 // (NIST SP800-90 hw CSPRNG; TLS client_random + ephemeral x25519/ECDHE keys UNPREDICTABLE). ABSENT
284 // (pre-2012 CPU) -> RDTSC-seeded fallback = GRACEFUL DEGRADATION (weak but non-constant, no crash)
285 // instead of an illegal-instruction fault. Replaces nishi's old deterministic 0xC0../0xA0.. fixed-key
286 // fallback (predictable keys = MITM-able). rbx is pushed (cpuid clobbers it); bl carries the support flag.
287 let r_urand: i64 = _nsh_rva(a)
288 _w8(buf,a+0,0x53) // push rbx
289 _w8(buf,a+1,0x49);_w8(buf,a+2,0x89);_w8(buf,a+3,0xF1) // mov r9,rsi (dst ptr)
290 _w8(buf,a+4,0x49);_w8(buf,a+5,0x89);_w8(buf,a+6,0xD0) // mov r8,rdx (count)
291 _w8(buf,a+7,0xB8);_w32(buf,a+8,1) // mov eax,1
292 _w8(buf,a+12,0x0F);_w8(buf,a+13,0xA2) // cpuid (clobbers eax/ebx/ecx/edx)
293 _w8(buf,a+14,0x0F);_w8(buf,a+15,0xBA);_w8(buf,a+16,0xE1);_w8(buf,a+17,0x1E) // bt ecx,30 (RDRAND bit)
294 _w8(buf,a+18,0x0F);_w8(buf,a+19,0x92);_w8(buf,a+20,0xC3) // setc bl (bl = RDRAND supported)
295 _w8(buf,a+21,0x4D);_w8(buf,a+22,0x85);_w8(buf,a+23,0xC0) // L_LOOP: test r8,r8
296 _w8(buf,a+24,0x74);_w8(buf,a+25,0x20) // jz L_DONE (+0x20)
297 _w8(buf,a+26,0x84);_w8(buf,a+27,0xDB) // test bl,bl
298 _w8(buf,a+28,0x74);_w8(buf,a+29,0x0F) // jz L_TSC (no RDRAND -> fallback)
299 _w8(buf,a+30,0xB9);_w32(buf,a+31,16) // mov ecx,16 (RDRAND retry budget)
300 _w8(buf,a+35,0x48);_w8(buf,a+36,0x0F);_w8(buf,a+37,0xC7);_w8(buf,a+38,0xF0) // L_RETRY: rdrand rax
301 _w8(buf,a+39,0x72);_w8(buf,a+40,0x06) // jc L_HAVE (CF=1 -> got entropy)
302 _w8(buf,a+41,0xFF);_w8(buf,a+42,0xC9) // dec ecx
303 _w8(buf,a+43,0x75);_w8(buf,a+44,0xF6) // jnz L_RETRY (-10; exhausted -> L_TSC)
304 _w8(buf,a+45,0x0F);_w8(buf,a+46,0x31) // L_TSC: rdtsc (eax = low TSC)
305 _w8(buf,a+47,0x41);_w8(buf,a+48,0x88);_w8(buf,a+49,0x01) // L_HAVE: mov [r9],al
306 _w8(buf,a+50,0x49);_w8(buf,a+51,0xFF);_w8(buf,a+52,0xC1) // inc r9
307 _w8(buf,a+53,0x49);_w8(buf,a+54,0xFF);_w8(buf,a+55,0xC8) // dec r8
308 _w8(buf,a+56,0xEB);_w8(buf,a+57,0xDB) // jmp L_LOOP (-37)
309 _w8(buf,a+58,0x48);_w8(buf,a+59,0x89);_w8(buf,a+60,0xD0) // L_DONE: mov rax,rdx (return n)
310 _w8(buf,a+61,0x5B) // pop rbx
311 _w8(buf,a+62,0xC3); a = a + 63 // ret
312
313 // READ: sys_read(fd=rdi,buf=rsi,n=rdx) -> fd==SENTINEL ? rdrand-fill : fd==0 ? stdin : (fd<3 ? -1 : recv)
314 let r_read: i64 = _nsh_rva(a)
315 _w8(buf,a+0,0x48);_w8(buf,a+1,0x81);_w8(buf,a+2,0xFF);_w32(buf,a+3,NSH_RAND_SENTINEL) // cmp rdi,sentinel
316 _w8(buf,a+7,0x0F);_w8(buf,a+8,0x84);_w32(buf,a+9, r_urand - _nsh_rva(a+13)) // je r_urand
317 a = a + 13
318 _w8(buf,a+0,0x48);_w8(buf,a+1,0x85);_w8(buf,a+2,0xFF) // test rdi,rdi
319 _w8(buf,a+3,0x75) // jnz L_NOTSTDIN (patched)
320 let rjn: i64 = a + 4
321 a = a + 5
322 let rsd: i64 = a
323 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x38) // sub rsp,0x38
324 _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)
325 _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)
326 _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
327 a = a + 19
328 a = _nsh_ec(buf, a, iat_gsh)
329 _w8(buf,a+0,0x48);_w8(buf,a+1,0x89);_w8(buf,a+2,0xC1)
330 _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)
331 _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)
332 _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)
333 _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)
334 a = a + 27
335 a = _nsh_ec(buf, a, iat_rf)
336 _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)
337 _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
338 _w8(buf, rjn, a - rsd) // patch jnz rel8
339 // L_NOTSTDIN: fd<3 -> -1
340 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xFF);_w8(buf,a+3,0x02) // cmp rdi,2
341 _w8(buf,a+4,0x7F) // jg L_RECV (patched)
342 let rjg: i64 = a + 5
343 a = a + 6
344 let rf12: i64 = a
345 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xC8);_w8(buf,a+3,0xFF);_w8(buf,a+4,0xC3); a = a + 5 // or rax,-1; ret
346 _w8(buf, rjg, a - rf12) // patch jg rel8
347 // L_RECV: recv(fd,buf,n,0)
348 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x28)
349 _w8(buf,a+4,0x49);_w8(buf,a+5,0x89);_w8(buf,a+6,0xD0)
350 _w8(buf,a+7,0x48);_w8(buf,a+8,0x89);_w8(buf,a+9,0xF2)
351 _w8(buf,a+10,0x48);_w8(buf,a+11,0x89);_w8(buf,a+12,0xF9)
352 _w8(buf,a+13,0x45);_w8(buf,a+14,0x31);_w8(buf,a+15,0xC9)
353 a = a + 16
354 a = _nsh_ec(buf, a, wiat_recv)
355 _w8(buf,a+0,0x48);_w8(buf,a+1,0x98); a = a + 2 // cdqe: int->i64
356 _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
357
358 // CLOSE: sys_close(fd=rdi) -> closesocket
359 let r_close: i64 = _nsh_rva(a)
360 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x28)
361 _w8(buf,a+4,0x48);_w8(buf,a+5,0x89);_w8(buf,a+6,0xF9); a = a + 7
362 a = _nsh_ec(buf, a, wiat_close)
363 _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
364
365 // EXIT: sys_exit(code=rdi) -> ExitProcess
366 let r_exit: i64 = _nsh_rva(a)
367 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x28)
368 _w8(buf,a+4,0x89);_w8(buf,a+5,0xF9); a = a + 6
369 a = _nsh_ec(buf, a, iat_exit)
370 _w8(buf,a+0,0xCC); a = a + 1
371
372 // NOWSEC: sys_now_realtime_sec() -> GetSystemTimeAsFileTime -> unix seconds
373 let r_nowsec: i64 = _nsh_rva(a)
374 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x28)
375 _w8(buf,a+4,0x48);_w8(buf,a+5,0x8D);_w8(buf,a+6,0x4C);_w8(buf,a+7,0x24);_w8(buf,a+8,0x20)
376 a = a + 9
377 a = _nsh_ec(buf, a, iat_gst)
378 _w8(buf,a+0,0x48);_w8(buf,a+1,0x8B);_w8(buf,a+2,0x44);_w8(buf,a+3,0x24);_w8(buf,a+4,0x20)
379 _w8(buf,a+5,0x31);_w8(buf,a+6,0xD2)
380 _w8(buf,a+7,0x48);_w8(buf,a+8,0xC7);_w8(buf,a+9,0xC1);_w32(buf,a+10,10000000)
381 _w8(buf,a+14,0x48);_w8(buf,a+15,0xF7);_w8(buf,a+16,0xF1)
382 _w8(buf,a+17,0x48);_w8(buf,a+18,0xB9);_w32(buf,a+19,3054539008);_w32(buf,a+23,2)
383 _w8(buf,a+27,0x48);_w8(buf,a+28,0x29);_w8(buf,a+29,0xC8)
384 _w8(buf,a+30,0x48);_w8(buf,a+31,0x83);_w8(buf,a+32,0xC4);_w8(buf,a+33,0x28);_w8(buf,a+34,0xC3); a = a + 35
385
386 // MONO: sys_clock_gettime_mono(ts=rdi) -> GetSystemTimeAsFileTime -> fill ts[0]=sec ts[1]=nsec
387 // (native ms/us clock for sovereign perf timing; reuses the GetSystemTimeAsFileTime import).
388 let r_mono: i64 = _nsh_rva(a)
389 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x28) // sub rsp,0x28
390 _w8(buf,a+4,0x48);_w8(buf,a+5,0x8D);_w8(buf,a+6,0x4C);_w8(buf,a+7,0x24);_w8(buf,a+8,0x20) // lea rcx,[rsp+0x20]
391 a = a + 9
392 a = _nsh_ec(buf, a, iat_gst) // call [GetSystemTimeAsFileTime]
393 _w8(buf,a+0,0x48);_w8(buf,a+1,0x8B);_w8(buf,a+2,0x44);_w8(buf,a+3,0x24);_w8(buf,a+4,0x20) // mov rax,[rsp+0x20]
394 _w8(buf,a+5,0x31);_w8(buf,a+6,0xD2) // xor edx,edx
395 _w8(buf,a+7,0x48);_w8(buf,a+8,0xC7);_w8(buf,a+9,0xC1);_w32(buf,a+10,10000000) // mov rcx,10000000
396 _w8(buf,a+14,0x48);_w8(buf,a+15,0xF7);_w8(buf,a+16,0xF1) // div rcx -> rax=sec rdx=rem
397 _w8(buf,a+17,0x48);_w8(buf,a+18,0x89);_w8(buf,a+19,0x07) // mov [rdi],rax
398 _w8(buf,a+20,0x48);_w8(buf,a+21,0x89);_w8(buf,a+22,0xD0) // mov rax,rdx
399 _w8(buf,a+23,0x48);_w8(buf,a+24,0xC7);_w8(buf,a+25,0xC1);_w32(buf,a+26,100) // mov rcx,100
400 _w8(buf,a+30,0x48);_w8(buf,a+31,0xF7);_w8(buf,a+32,0xE9) // imul rcx -> rax=nsec
401 _w8(buf,a+33,0x48);_w8(buf,a+34,0x89);_w8(buf,a+35,0x47);_w8(buf,a+36,0x08) // mov [rdi+8],rax
402 _w8(buf,a+37,0x31);_w8(buf,a+38,0xC0) // xor eax,eax
403 _w8(buf,a+39,0x48);_w8(buf,a+40,0x83);_w8(buf,a+41,0xC4);_w8(buf,a+42,0x28) // add rsp,0x28
404 _w8(buf,a+43,0xC3); a = a + 44 // ret
405
406 // OPENAT: sys_openat_rd(path=rdi) -> CreateFileA(...) -> handle or -1
407 let r_openat: i64 = _nsh_rva(a)
408 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x48)
409 _w8(buf,a+4,0x48);_w8(buf,a+5,0x89);_w8(buf,a+6,0xF9)
410 _w8(buf,a+7,0xBA);_w8(buf,a+8,0x00);_w8(buf,a+9,0x00);_w8(buf,a+10,0x00);_w8(buf,a+11,0x80)
411 _w8(buf,a+12,0x41);_w8(buf,a+13,0xB8);_w32(buf,a+14,1)
412 _w8(buf,a+18,0x45);_w8(buf,a+19,0x31);_w8(buf,a+20,0xC9)
413 _w8(buf,a+21,0xC7);_w8(buf,a+22,0x44);_w8(buf,a+23,0x24);_w8(buf,a+24,0x20);_w32(buf,a+25,3)
414 _w8(buf,a+29,0xC7);_w8(buf,a+30,0x44);_w8(buf,a+31,0x24);_w8(buf,a+32,0x28);_w32(buf,a+33,0x80)
415 _w8(buf,a+37,0x48);_w8(buf,a+38,0xC7);_w8(buf,a+39,0x44);_w8(buf,a+40,0x24);_w8(buf,a+41,0x30);_w32(buf,a+42,0)
416 a = a + 46
417 a = _nsh_ec(buf, a, iat_cfa)
418 // /dev/urandom (and any path Windows can't open) -> CreateFileA returns -1; hand back the RAND
419 // SENTINEL fd instead, so nishi's `ufd >= 0` urandom branch runs and sys_read rdrand-fills it
420 // (was: -1 -> nishi's DETERMINISTIC fixed-key fallback = the MITM-able hole this closes).
421 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xF8);_w8(buf,a+3,0xFF) // cmp rax,-1
422 _w8(buf,a+4,0x75);_w8(buf,a+5,0x05) // jne +5 (real handle -> keep)
423 _w8(buf,a+6,0xB8);_w32(buf,a+7,NSH_RAND_SENTINEL); a = a + 11 // mov eax,sentinel
424 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xC4);_w8(buf,a+3,0x48);_w8(buf,a+4,0xC3); a = a + 5
425
426 // READFILE: sys_read_file(path=rdi, out_len=rsi) -> buf
427 let r_readfile: i64 = _nsh_rva(a)
428 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x58)
429 _w8(buf,a+4,0x48);_w8(buf,a+5,0x89);_w8(buf,a+6,0x74);_w8(buf,a+7,0x24);_w8(buf,a+8,0x48)
430 _w8(buf,a+9,0x48);_w8(buf,a+10,0x89);_w8(buf,a+11,0xF9)
431 _w8(buf,a+12,0xBA);_w8(buf,a+13,0x00);_w8(buf,a+14,0x00);_w8(buf,a+15,0x00);_w8(buf,a+16,0x80)
432 _w8(buf,a+17,0x41);_w8(buf,a+18,0xB8);_w32(buf,a+19,1)
433 _w8(buf,a+23,0x45);_w8(buf,a+24,0x31);_w8(buf,a+25,0xC9)
434 _w8(buf,a+26,0xC7);_w8(buf,a+27,0x44);_w8(buf,a+28,0x24);_w8(buf,a+29,0x20);_w32(buf,a+30,3)
435 _w8(buf,a+34,0xC7);_w8(buf,a+35,0x44);_w8(buf,a+36,0x24);_w8(buf,a+37,0x28);_w32(buf,a+38,0x80)
436 _w8(buf,a+42,0x48);_w8(buf,a+43,0xC7);_w8(buf,a+44,0x44);_w8(buf,a+45,0x24);_w8(buf,a+46,0x30);_w32(buf,a+47,0)
437 a = a + 51
438 a = _nsh_ec(buf, a, iat_cfa)
439 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xF8);_w8(buf,a+3,0xFF) // cmp rax,-1
440 _w8(buf,a+4,0x75);_w8(buf,a+5,0x13) // jne +0x13
441 _w8(buf,a+6,0x48);_w8(buf,a+7,0x8B);_w8(buf,a+8,0x44);_w8(buf,a+9,0x24);_w8(buf,a+10,0x48)
442 _w8(buf,a+11,0x48);_w8(buf,a+12,0xC7);_w8(buf,a+13,0x00);_w32(buf,a+14,0)
443 _w8(buf,a+18,0x31);_w8(buf,a+19,0xC0)
444 _w8(buf,a+20,0x48);_w8(buf,a+21,0x83);_w8(buf,a+22,0xC4);_w8(buf,a+23,0x58);_w8(buf,a+24,0xC3)
445 a = a + 25
446 _w8(buf,a+0,0x48);_w8(buf,a+1,0x89);_w8(buf,a+2,0x44);_w8(buf,a+3,0x24);_w8(buf,a+4,0x40)
447 _w8(buf,a+5,0x48);_w8(buf,a+6,0x89);_w8(buf,a+7,0xC1)
448 _w8(buf,a+8,0x48);_w8(buf,a+9,0x8D);_w8(buf,a+10,0x54);_w8(buf,a+11,0x24);_w8(buf,a+12,0x50)
449 a = a + 13
450 a = _nsh_ec(buf, a, iat_gfs)
451 a = _nsh_ec(buf, a, iat_gph)
452 _w8(buf,a+0,0x48);_w8(buf,a+1,0x89);_w8(buf,a+2,0xC1)
453 _w8(buf,a+3,0x31);_w8(buf,a+4,0xD2)
454 _w8(buf,a+5,0x4C);_w8(buf,a+6,0x8B);_w8(buf,a+7,0x44);_w8(buf,a+8,0x24);_w8(buf,a+9,0x50)
455 _w8(buf,a+10,0x49);_w8(buf,a+11,0x83);_w8(buf,a+12,0xC0);_w8(buf,a+13,0x10)
456 a = a + 14
457 a = _nsh_ec(buf, a, iat_ha)
458 _w8(buf,a+0,0x48);_w8(buf,a+1,0x89);_w8(buf,a+2,0x44);_w8(buf,a+3,0x24);_w8(buf,a+4,0x38)
459 _w8(buf,a+5,0x48);_w8(buf,a+6,0x8B);_w8(buf,a+7,0x4C);_w8(buf,a+8,0x24);_w8(buf,a+9,0x40)
460 _w8(buf,a+10,0x48);_w8(buf,a+11,0x89);_w8(buf,a+12,0xC2)
461 _w8(buf,a+13,0x4C);_w8(buf,a+14,0x8B);_w8(buf,a+15,0x44);_w8(buf,a+16,0x24);_w8(buf,a+17,0x50)
462 _w8(buf,a+18,0x4C);_w8(buf,a+19,0x8D);_w8(buf,a+20,0x4C);_w8(buf,a+21,0x24);_w8(buf,a+22,0x28)
463 _w8(buf,a+23,0x48);_w8(buf,a+24,0xC7);_w8(buf,a+25,0x44);_w8(buf,a+26,0x24);_w8(buf,a+27,0x20);_w32(buf,a+28,0)
464 a = a + 32
465 a = _nsh_ec(buf, a, iat_rf)
466 _w8(buf,a+0,0x48);_w8(buf,a+1,0x8B);_w8(buf,a+2,0x4C);_w8(buf,a+3,0x24);_w8(buf,a+4,0x40)
467 a = a + 5
468 a = _nsh_ec(buf, a, iat_ch)
469 _w8(buf,a+0,0x48);_w8(buf,a+1,0x8B);_w8(buf,a+2,0x44);_w8(buf,a+3,0x24);_w8(buf,a+4,0x48)
470 _w8(buf,a+5,0x48);_w8(buf,a+6,0x8B);_w8(buf,a+7,0x54);_w8(buf,a+8,0x24);_w8(buf,a+9,0x50)
471 _w8(buf,a+10,0x48);_w8(buf,a+11,0x89);_w8(buf,a+12,0x10)
472 _w8(buf,a+13,0x48);_w8(buf,a+14,0x8B);_w8(buf,a+15,0x44);_w8(buf,a+16,0x24);_w8(buf,a+17,0x38)
473 _w8(buf,a+18,0x48);_w8(buf,a+19,0x8B);_w8(buf,a+20,0x4C);_w8(buf,a+21,0x24);_w8(buf,a+22,0x50)
474 _w8(buf,a+23,0xC6);_w8(buf,a+24,0x04);_w8(buf,a+25,0x08);_w8(buf,a+26,0x00)
475 _w8(buf,a+27,0x48);_w8(buf,a+28,0x8B);_w8(buf,a+29,0x44);_w8(buf,a+30,0x24);_w8(buf,a+31,0x38)
476 _w8(buf,a+32,0x48);_w8(buf,a+33,0x83);_w8(buf,a+34,0xC4);_w8(buf,a+35,0x58);_w8(buf,a+36,0xC3)
477 a = a + 37
478
479 // WSAINIT: WSAStartup(0x0202, &wsadata) -- called from entry stub
480 let r_wsa: i64 = _nsh_rva(a)
481 _w8(buf,a+0,0x48);_w8(buf,a+1,0x81);_w8(buf,a+2,0xEC);_w32(buf,a+3,0x228)
482 _w8(buf,a+7,0xB9);_w32(buf,a+8,0x0202)
483 _w8(buf,a+12,0x48);_w8(buf,a+13,0x8D);_w8(buf,a+14,0x54);_w8(buf,a+15,0x24);_w8(buf,a+16,0x28)
484 a = a + 17
485 a = _nsh_ec(buf, a, wiat_wsa)
486 _w8(buf,a+0,0x48);_w8(buf,a+1,0x81);_w8(buf,a+2,0xC4);_w32(buf,a+3,0x228);_w8(buf,a+7,0xC3); a = a + 8
487
488 // SOCKET: sys_socket(domain=rdi,type=rsi,proto=rdx) -> socket(af,type,proto)
489 let r_socket: i64 = _nsh_rva(a)
490 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x28)
491 _w8(buf,a+4,0x49);_w8(buf,a+5,0x89);_w8(buf,a+6,0xD0) // mov r8,rdx
492 _w8(buf,a+7,0x48);_w8(buf,a+8,0x89);_w8(buf,a+9,0xF2) // mov rdx,rsi
493 _w8(buf,a+10,0x48);_w8(buf,a+11,0x89);_w8(buf,a+12,0xF9) // mov rcx,rdi
494 a = a + 13
495 a = _nsh_ec(buf, a, wiat_sock)
496 _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
497
498 // CONNECT: sys_connect(fd=rdi,addr=rsi,addrlen=rdx) -> connect(s,name,namelen)
499 let r_connect: i64 = _nsh_rva(a)
500 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x28)
501 _w8(buf,a+4,0x49);_w8(buf,a+5,0x89);_w8(buf,a+6,0xD0)
502 _w8(buf,a+7,0x48);_w8(buf,a+8,0x89);_w8(buf,a+9,0xF2)
503 _w8(buf,a+10,0x48);_w8(buf,a+11,0x89);_w8(buf,a+12,0xF9)
504 a = a + 13
505 a = _nsh_ec(buf, a, wiat_conn)
506 _w8(buf,a+0,0x48);_w8(buf,a+1,0x98); a = a + 2 // cdqe: int->i64
507 _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
508
509 // SENDTO: sys_sendto(fd,buf,n,flags,addr,addrlen) -> sendto(s,buf,len,flags,to,tolen)
510 let r_sendto: i64 = _nsh_rva(a)
511 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x38)
512 _w8(buf,a+4,0x4C);_w8(buf,a+5,0x89);_w8(buf,a+6,0x4C);_w8(buf,a+7,0x24);_w8(buf,a+8,0x28) // mov [rsp+0x28],r9 (tolen)
513 _w8(buf,a+9,0x4C);_w8(buf,a+10,0x89);_w8(buf,a+11,0x44);_w8(buf,a+12,0x24);_w8(buf,a+13,0x20) // mov [rsp+0x20],r8 (to)
514 _w8(buf,a+14,0x49);_w8(buf,a+15,0x89);_w8(buf,a+16,0xC9) // mov r9,rcx (flags)
515 _w8(buf,a+17,0x49);_w8(buf,a+18,0x89);_w8(buf,a+19,0xD0) // mov r8,rdx (len)
516 _w8(buf,a+20,0x48);_w8(buf,a+21,0x89);_w8(buf,a+22,0xF2) // mov rdx,rsi (buf)
517 _w8(buf,a+23,0x48);_w8(buf,a+24,0x89);_w8(buf,a+25,0xF9) // mov rcx,rdi (s)
518 a = a + 26
519 a = _nsh_ec(buf, a, wiat_sto)
520 _w8(buf,a+0,0x48);_w8(buf,a+1,0x98); a = a + 2 // cdqe: int->i64
521 _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
522
523 // RECVFROM: sys_recvfrom(fd,buf,n,flags,addr,addrlen) -> recvfrom(s,buf,len,flags,from,fromlen)
524 let r_recvfrom: i64 = _nsh_rva(a)
525 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x38)
526 _w8(buf,a+4,0x4C);_w8(buf,a+5,0x89);_w8(buf,a+6,0x4C);_w8(buf,a+7,0x24);_w8(buf,a+8,0x28)
527 _w8(buf,a+9,0x4C);_w8(buf,a+10,0x89);_w8(buf,a+11,0x44);_w8(buf,a+12,0x24);_w8(buf,a+13,0x20)
528 _w8(buf,a+14,0x49);_w8(buf,a+15,0x89);_w8(buf,a+16,0xC9)
529 _w8(buf,a+17,0x49);_w8(buf,a+18,0x89);_w8(buf,a+19,0xD0)
530 _w8(buf,a+20,0x48);_w8(buf,a+21,0x89);_w8(buf,a+22,0xF2)
531 _w8(buf,a+23,0x48);_w8(buf,a+24,0x89);_w8(buf,a+25,0xF9)
532 a = a + 26
533 a = _nsh_ec(buf, a, wiat_rfrom)
534 _w8(buf,a+0,0x48);_w8(buf,a+1,0x98); a = a + 2 // cdqe: int->i64
535 _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
536
537 // ZERO stub: xor eax,eax; ret (sys_setsockopt -> 0; unreached clock helpers -> 0)
538 let r_zero: i64 = _nsh_rva(a)
539 _w8(buf,a+0,0x31);_w8(buf,a+1,0xC0);_w8(buf,a+2,0xC3); a = a + 3
540
541 // build_argv: GetCommandLineA() -> tokenize into ARGV_ARR(r_argv, writable). Skips the exe path
542 // (quoted or unquoted), takes the next token as argv[1] (the URL). Returns argc in eax (1 = no URL
543 // -> nish interactive; 2 = one-shot URL). r10=scan ptr, r9=ARGV_ARR, ecx=byte. All-volatile regs.
544 let r_build_argv: i64 = _nsh_rva(a)
545 let av_lo: i64 = (NSH_IMGBASE + r_argv) & 0xFFFFFFFF
546 let av_hi: i64 = (NSH_IMGBASE + r_argv) / 4294967296
547 _w8(buf,a+0,0x48);_w8(buf,a+1,0x83);_w8(buf,a+2,0xEC);_w8(buf,a+3,0x28) // sub rsp,0x28
548 _w8(buf,a+4,0xFF);_w8(buf,a+5,0x15);_w32(buf,a+6, iat_gcl - _nsh_rva(a+10)) // call [GetCommandLineA]
549 _w8(buf,a+10,0x49);_w8(buf,a+11,0x89);_w8(buf,a+12,0xC2) // mov r10,rax
550 _w8(buf,a+13,0x49);_w8(buf,a+14,0xB9);_w32(buf,a+15,av_lo);_w32(buf,a+19,av_hi) // mov r9,ARGV_VA
551 _w8(buf,a+23,0x49);_w8(buf,a+24,0x89);_w8(buf,a+25,0x01) // mov [r9],rax (argv0)
552 _w8(buf,a+26,0x41);_w8(buf,a+27,0x0F);_w8(buf,a+28,0xB6);_w8(buf,a+29,0x0A) // movzx ecx,[r10]
553 _w8(buf,a+30,0x80);_w8(buf,a+31,0xF9);_w8(buf,a+32,0x22) // cmp cl,'"'
554 _w8(buf,a+33,0x75);_w8(buf,a+34,0x1A) // jne U0 (+26)
555 _w8(buf,a+35,0x49);_w8(buf,a+36,0xFF);_w8(buf,a+37,0xC2) // inc r10
556 _w8(buf,a+38,0x41);_w8(buf,a+39,0x0F);_w8(buf,a+40,0xB6);_w8(buf,a+41,0x0A) // Q0: movzx ecx,[r10]
557 _w8(buf,a+42,0x84);_w8(buf,a+43,0xC9) // test cl,cl
558 _w8(buf,a+44,0x74);_w8(buf,a+45,0x55) // je DONE1 (+85)
559 _w8(buf,a+46,0x80);_w8(buf,a+47,0xF9);_w8(buf,a+48,0x22) // cmp cl,'"'
560 _w8(buf,a+49,0x74);_w8(buf,a+50,0x05) // je Q0E (+5)
561 _w8(buf,a+51,0x49);_w8(buf,a+52,0xFF);_w8(buf,a+53,0xC2) // inc r10
562 _w8(buf,a+54,0xEB);_w8(buf,a+55,0xEE) // jmp Q0 (-18)
563 _w8(buf,a+56,0x49);_w8(buf,a+57,0xFF);_w8(buf,a+58,0xC2) // Q0E: inc r10
564 _w8(buf,a+59,0xEB);_w8(buf,a+60,0x17) // jmp WS (+23)
565 _w8(buf,a+61,0x41);_w8(buf,a+62,0x0F);_w8(buf,a+63,0xB6);_w8(buf,a+64,0x0A) // U0: movzx ecx,[r10]
566 _w8(buf,a+65,0x84);_w8(buf,a+66,0xC9) // test cl,cl
567 _w8(buf,a+67,0x74);_w8(buf,a+68,0x3E) // je DONE1 (+62)
568 _w8(buf,a+69,0x80);_w8(buf,a+70,0xF9);_w8(buf,a+71,0x20) // cmp cl,' '
569 _w8(buf,a+72,0x74);_w8(buf,a+73,0x0A) // je WS (+10)
570 _w8(buf,a+74,0x80);_w8(buf,a+75,0xF9);_w8(buf,a+76,0x09) // cmp cl,tab
571 _w8(buf,a+77,0x74);_w8(buf,a+78,0x05) // je WS (+5)
572 _w8(buf,a+79,0x49);_w8(buf,a+80,0xFF);_w8(buf,a+81,0xC2) // inc r10
573 _w8(buf,a+82,0xEB);_w8(buf,a+83,0xE9) // jmp U0 (-23)
574 _w8(buf,a+84,0x41);_w8(buf,a+85,0x0F);_w8(buf,a+86,0xB6);_w8(buf,a+87,0x0A) // WS: movzx ecx,[r10]
575 _w8(buf,a+88,0x80);_w8(buf,a+89,0xF9);_w8(buf,a+90,0x20) // cmp cl,' '
576 _w8(buf,a+91,0x74);_w8(buf,a+92,0x07) // je WSADV (+7)
577 _w8(buf,a+93,0x80);_w8(buf,a+94,0xF9);_w8(buf,a+95,0x09) // cmp cl,tab
578 _w8(buf,a+96,0x74);_w8(buf,a+97,0x02) // je WSADV (+2)
579 _w8(buf,a+98,0xEB);_w8(buf,a+99,0x05) // jmp TOK (+5)
580 _w8(buf,a+100,0x49);_w8(buf,a+101,0xFF);_w8(buf,a+102,0xC2) // WSADV: inc r10
581 _w8(buf,a+103,0xEB);_w8(buf,a+104,0xEB) // jmp WS (-21)
582 _w8(buf,a+105,0x84);_w8(buf,a+106,0xC9) // TOK: test cl,cl
583 _w8(buf,a+107,0x74);_w8(buf,a+108,0x16) // je DONE1 (+22)
584 _w8(buf,a+109,0x4D);_w8(buf,a+110,0x89);_w8(buf,a+111,0x51);_w8(buf,a+112,0x08) // mov [r9+8],r10 (argv1)
585 _w8(buf,a+113,0x49);_w8(buf,a+114,0xC7);_w8(buf,a+115,0x41);_w8(buf,a+116,0x10);_w32(buf,a+117,0) // mov qword[r9+16],0
586 _w8(buf,a+121,0xB8);_w32(buf,a+122,2) // mov eax,2
587 _w8(buf,a+126,0x48);_w8(buf,a+127,0x83);_w8(buf,a+128,0xC4);_w8(buf,a+129,0x28) // add rsp,0x28
588 _w8(buf,a+130,0xC3) // ret
589 _w8(buf,a+131,0x49);_w8(buf,a+132,0xC7);_w8(buf,a+133,0x41);_w8(buf,a+134,0x08);_w32(buf,a+135,0) // DONE1: mov qword[r9+8],0
590 _w8(buf,a+139,0xB8);_w32(buf,a+140,1) // mov eax,1
591 _w8(buf,a+144,0x48);_w8(buf,a+145,0x83);_w8(buf,a+146,0xC4);_w8(buf,a+147,0x28) // add rsp,0x28
592 _w8(buf,a+148,0xC3) // ret
593 a = a + 149
594
595 if (a - NSH_FOFF_TEXT) > text_fsz { return 0 - 201 }
596
597 // ===== entry stub @ FOFF_TEXT (40 bytes): init Winsock, build argv from the command line,
598 // then call main(argc, argv) =====
599 let argv_lo: i64 = (NSH_IMGBASE + r_argv) & 0xFFFFFFFF
600 let argv_hi: i64 = (NSH_IMGBASE + r_argv) / 4294967296
601 let t: i64 = NSH_FOFF_TEXT
602 _w8(buf,t+0,0x48);_w8(buf,t+1,0x83);_w8(buf,t+2,0xEC);_w8(buf,t+3,0x28) // sub rsp,0x28
603 _w8(buf,t+4,0xE8);_w32(buf,t+5, r_wsa - _nsh_rva(t + 9)) // call WSAStartup-init
604 _w8(buf,t+9,0xE8);_w32(buf,t+10, r_build_argv - _nsh_rva(t + 14)) // call build_argv -> eax=argc
605 _w8(buf,t+14,0x89);_w8(buf,t+15,0xC7) // mov edi,eax (argc)
606 _w8(buf,t+16,0x48);_w8(buf,t+17,0xBE);_w32(buf,t+18,argv_lo);_w32(buf,t+22,argv_hi) // mov rsi,ARGV_ARR
607 _w8(buf,t+26,0xE8);_w32(buf,t+27,9 + off[0]) // call main
608 _w8(buf,t+31,0x89);_w8(buf,t+32,0xC1) // mov ecx,eax
609 _w8(buf,t+33,0xFF);_w8(buf,t+34,0x15);_w32(buf,t+35, iat_exit - _nsh_rva(t + 39)) // call ExitProcess
610 _w8(buf,t+39,0xCC) // int3
611
612 // ===== redirects =====
613 _nsh_redir(buf, off[1], r_halloc) // sys_mmap
614 _nsh_redir(buf, off[2], r_write) // sys_write (console | send)
615 _nsh_redir(buf, off[3], r_exit) // sys_exit
616 _nsh_redir(buf, off[4], r_readfile) // sys_read_file
617 _nsh_redir(buf, off[5], r_nowsec) // sys_now_realtime_sec
618 _nsh_redir(buf, off[6], r_openat) // sys_openat_rd
619 _nsh_redir(buf, off[7], r_read) // sys_read (stdin | recv)
620 _nsh_redir(buf, off[8], r_close) // sys_close -> closesocket
621 _nsh_redir(buf, off[9], r_socket) // sys_socket
622 _nsh_redir(buf, off[10], r_connect) // sys_connect
623 _nsh_redir(buf, off[11], r_sendto) // sys_sendto
624 _nsh_redir(buf, off[12], r_recvfrom) // sys_recvfrom
625 _nsh_redir(buf, off[13], r_zero) // sys_setsockopt -> 0
626 _nsh_redir(buf, off[14], r_zero) // sys_clock_gettime_real -> 0 (unreached)
627 _nsh_redir(buf, off[15], r_mono) // sys_clock_gettime_mono -> GetSystemTimeAsFileTime (native ms clock)
628
629 // ===== .idata (kernel32{10} + ws2_32{8}) =====
630 let d: i64 = foff_idata
631 var nf: i64 = d + NSH_NAMES
632 let rn_exit: i64 = rva_idata + (nf - d); nf = _nsh_put_name(buf, nf, "ExitProcess")
633 let rn_gph: i64 = rva_idata + (nf - d); nf = _nsh_put_name(buf, nf, "GetProcessHeap")
634 let rn_ha: i64 = rva_idata + (nf - d); nf = _nsh_put_name(buf, nf, "HeapAlloc")
635 let rn_gsh: i64 = rva_idata + (nf - d); nf = _nsh_put_name(buf, nf, "GetStdHandle")
636 let rn_wf: i64 = rva_idata + (nf - d); nf = _nsh_put_name(buf, nf, "WriteFile")
637 let rn_rf: i64 = rva_idata + (nf - d); nf = _nsh_put_name(buf, nf, "ReadFile")
638 let rn_cfa: i64 = rva_idata + (nf - d); nf = _nsh_put_name(buf, nf, "CreateFileA")
639 let rn_gfs: i64 = rva_idata + (nf - d); nf = _nsh_put_name(buf, nf, "GetFileSizeEx")
640 let rn_gst: i64 = rva_idata + (nf - d); nf = _nsh_put_name(buf, nf, "GetSystemTimeAsFileTime")
641 let rn_ch: i64 = rva_idata + (nf - d); nf = _nsh_put_name(buf, nf, "CloseHandle")
642 let rn_gcl: i64 = rva_idata + (nf - d); nf = _nsh_put_name(buf, nf, "GetCommandLineA")
643 let rn_wsa: i64 = rva_idata + (nf - d); nf = _nsh_put_name(buf, nf, "WSAStartup")
644 let rn_sock: i64 = rva_idata + (nf - d); nf = _nsh_put_name(buf, nf, "socket")
645 let rn_conn: i64 = rva_idata + (nf - d); nf = _nsh_put_name(buf, nf, "connect")
646 let rn_send: i64 = rva_idata + (nf - d); nf = _nsh_put_name(buf, nf, "send")
647 let rn_recv: i64 = rva_idata + (nf - d); nf = _nsh_put_name(buf, nf, "recv")
648 let rn_sto: i64 = rva_idata + (nf - d); nf = _nsh_put_name(buf, nf, "sendto")
649 let rn_rfr: i64 = rva_idata + (nf - d); nf = _nsh_put_name(buf, nf, "recvfrom")
650 let rn_cls: i64 = rva_idata + (nf - d); nf = _nsh_put_name(buf, nf, "closesocket")
651 let rn_kdll: i64 = rva_idata + (nf - d); nf = _nsh_put_str(buf, nf, "kernel32.dll")
652 let rn_wdll: i64 = rva_idata + (nf - d); nf = _nsh_put_str(buf, nf, "ws2_32.dll")
653
654 // descriptors
655 _w32(buf, d + NSH_DESC_K + 0, rva_idata + NSH_INT_K); _w32(buf, d + NSH_DESC_K + 12, rn_kdll); _w32(buf, d + NSH_DESC_K + 16, rva_idata + NSH_IAT_K)
656 _w32(buf, d + NSH_DESC_W + 0, rva_idata + NSH_INT_W); _w32(buf, d + NSH_DESC_W + 12, rn_wdll); _w32(buf, d + NSH_DESC_W + 16, rva_idata + NSH_IAT_W)
657 // kernel32 INT + IAT
658 _w64(buf, d+NSH_INT_K+0, rn_exit,0); _w64(buf, d+NSH_IAT_K+0, rn_exit,0)
659 _w64(buf, d+NSH_INT_K+8, rn_gph,0); _w64(buf, d+NSH_IAT_K+8, rn_gph,0)
660 _w64(buf, d+NSH_INT_K+16, rn_ha,0); _w64(buf, d+NSH_IAT_K+16, rn_ha,0)
661 _w64(buf, d+NSH_INT_K+24, rn_gsh,0); _w64(buf, d+NSH_IAT_K+24, rn_gsh,0)
662 _w64(buf, d+NSH_INT_K+32, rn_wf,0); _w64(buf, d+NSH_IAT_K+32, rn_wf,0)
663 _w64(buf, d+NSH_INT_K+40, rn_rf,0); _w64(buf, d+NSH_IAT_K+40, rn_rf,0)
664 _w64(buf, d+NSH_INT_K+48, rn_cfa,0); _w64(buf, d+NSH_IAT_K+48, rn_cfa,0)
665 _w64(buf, d+NSH_INT_K+56, rn_gfs,0); _w64(buf, d+NSH_IAT_K+56, rn_gfs,0)
666 _w64(buf, d+NSH_INT_K+64, rn_gst,0); _w64(buf, d+NSH_IAT_K+64, rn_gst,0)
667 _w64(buf, d+NSH_INT_K+72, rn_ch,0); _w64(buf, d+NSH_IAT_K+72, rn_ch,0)
668 _w64(buf, d+NSH_INT_K+80, rn_gcl,0); _w64(buf, d+NSH_IAT_K+80, rn_gcl,0)
669 _w64(buf, d+NSH_INT_K+88, 0,0); _w64(buf, d+NSH_IAT_K+88, 0,0)
670 // ws2_32 INT + IAT
671 _w64(buf, d+NSH_INT_W+0, rn_wsa,0); _w64(buf, d+NSH_IAT_W+0, rn_wsa,0)
672 _w64(buf, d+NSH_INT_W+8, rn_sock,0); _w64(buf, d+NSH_IAT_W+8, rn_sock,0)
673 _w64(buf, d+NSH_INT_W+16, rn_conn,0); _w64(buf, d+NSH_IAT_W+16, rn_conn,0)
674 _w64(buf, d+NSH_INT_W+24, rn_send,0); _w64(buf, d+NSH_IAT_W+24, rn_send,0)
675 _w64(buf, d+NSH_INT_W+32, rn_recv,0); _w64(buf, d+NSH_IAT_W+32, rn_recv,0)
676 _w64(buf, d+NSH_INT_W+40, rn_sto,0); _w64(buf, d+NSH_IAT_W+40, rn_sto,0)
677 _w64(buf, d+NSH_INT_W+48, rn_rfr,0); _w64(buf, d+NSH_IAT_W+48, rn_rfr,0)
678 _w64(buf, d+NSH_INT_W+56, rn_cls,0); _w64(buf, d+NSH_IAT_W+56, rn_cls,0)
679 _w64(buf, d+NSH_INT_W+64, 0,0); _w64(buf, d+NSH_IAT_W+64, 0,0)
680
681 return NX_PE_OK
682}
683
684func main() -> i64 {
685 let lenbox: *i64 = sys_mmap(16) as *i64
686 let src: *u8 = sys_read_file("/tmp/nxwin.s" as *u8, lenbox)
687 if (src as i64) == 0 { return 1 }
688 let n: i64 = lenbox[0]
689 if n <= 0 { return 2 }
690
691 let code: *u8 = sys_mmap(NSH_CODE_CAP)
692 let off: *i64 = sys_mmap(32 * 8) as *i64
693 var z: i64 = 0
694 while z < 16 { off[z] = 0 - 1; z = z + 1 }
695 let code_len: i64 = nsh_assemble(src, n, code, NSH_CODE_CAP, off)
696 if code_len < 0 { return 3 }
697 if off[0] < 0 { return 4 }
698
699 let span: i64 = NSH_STUB_LEN + code_len + NSH_THUNK_SLACK
700 let text_fsz: i64 = _nsh_align(span, 0x200)
701 let rva_idata: i64 = 0x1000 + _nsh_align(span, 0x1000)
702 let file_size: i64 = NSH_FOFF_TEXT + text_fsz + NSH_IDATA_FSZ
703
704 let buf: *u8 = sys_mmap(file_size)
705 let rc: i64 = nsh_emit_pe(buf, code, code_len, off, file_size, text_fsz, rva_idata)
706 if rc != NX_PE_OK { return 5 }
707
708 let outp: *u8 = "/mnt/c/Users/elder/nishi-core/nxc2/_offc/nishi_fetch.exe" as *u8
709 if nx_pe_write_to_file(outp, buf, file_size) != NX_PE_OK { return 70 }
710
711 let msg: *u8 = "[substrate] Nishi fetch-engine PE written: _offc/nishi_fetch.exe\n" as *u8
712 sys_write(1, msg, 65)
713 return 0
714}