code wiki / (root) / nx_pe_compile_win.nx

nx_pe_compile_win.nx source

↩ module page · 168 lines · 8025 B

1// nx_pe_compile_win.nx -- W3 keystone: nxc2-compiled NishiLang -> NATIVE Windows PE. 2// 3// The docker-replacement ladder's compiler-backend rung (smallest gated slice). Proves a REAL 4// nxc2-compiled organ runs native on Windows -- not a hand-laid PE. Pipeline: 5// nx_compile_x86 (NishiLang -> AT&T x86_64 .s) [existing] 6// -> THIS organ: assemble the .s sovereignly (reuse nxasm_x86 axc_pass; resolve `main`'s offset) 7// then wrap the assembled machine code in a PE whose ENTRY STUB does: 8// sub rsp,0x28 ; call main ; mov ecx,eax ; call [ExitProcess] ; int3 9// -- bypassing the compiler's Linux-syscall `_start` (dead on Windows). 1 kernel32 import. 10// 11// Input (hardcoded, minimal slice): /tmp/nxwin.s (the .s from nx_compile_x86) 12// Output (hardcoded): _offc/nx_win_compiled.exe 13// 14// HONEST SCOPE: leaf `main` returning in rax is ABI-safe (SysV==MSx64 for the return value), so a 15// no-syscall organ works as-is. Organs that call Linux syscalls need the backend to emit kernel32 16// imports + MS x64 ABI for THEIR calls -- the next W3 sub-rungs. This proves the format+entry bridge. 17// Replicates ~15 lines of nxasm_x86_assemble (to also resolve `main`) WITHOUT touching the shared 18// assembler -- tutor-scaffold; back-fill = a named-entry assemble API folded into nxasm_x86. 19// lineage_id: substrate_pe_compile_win_v1 20 21import "nx_syscalls.nx" 22import "nxasm_x86.nx" 23import "nx_pe_writer.nx" 24 25const NXPCW_CODE_CAP: i64 = 1048576 // 1 MiB assembled code 26const NXPCW_FILE_SIZE: i64 = 0x600 // headers + .text(stub+code, <=0x200) + .idata 27const NXPCW_STUB_LEN: i64 = 18 28 29// Assemble AT&T x86_64 `.s` -> machine code in `out`; report `main`'s offset in p_main[0]. 30// Mirrors nxasm_x86_assemble but resolves `main` instead of `_start` (additive, no shared edit). 31func nxpcw_assemble_main(src: *u8, n: i64, out: *u8, out_cap: i64, p_main: *i64) -> i64 { 32 let lab_off: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 33 let lab_len: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 34 let lab_addr: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 35 let lab_sec: *i64 = sys_mmap(ASM_MAX_LABELS * 8) as *i64 36 let op0: *i64 = sys_mmap(72) as *i64 // 9 slots for SIB (matches nxasm_x86) 37 let op1: *i64 = sys_mmap(72) as *i64 38 let op2: *i64 = sys_mmap(72) as *i64 // API DRIFT FIX: axc_pass gained op2 39 let scratch: *u8 = sys_mmap(64) 40 let posbox: *i64 = sys_mmap(16) as *i64 41 let n_lab_box: *i64 = sys_mmap(16) as *i64 42 n_lab_box[0] = 0 43 let lh: *i64 = sys_mmap(ASM_LH_SIZE * 8) as *i64 44 45 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) 46 if text_size < 0 { return text_size } 47 let n_lab: i64 = n_lab_box[0] 48 49 var main_a: i64 = 0 - 1 50 var k: i64 = 0 51 while k < n_lab { 52 if lab_sec[k] == 1 { lab_addr[k] = lab_addr[k] + text_size } 53 if axc_tok_is(src, lab_off[k], lab_len[k], "main") == 1 { main_a = lab_addr[k] } 54 k = k + 1 55 } 56 axc_lh_build(src, lab_off, lab_len, n_lab, lh) 57 58 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) 59 if total < 0 { return total } 60 if total > out_cap { return 0 - 200 } 61 p_main[0] = main_a 62 return total 63} 64 65// Wrap assembled `code` (len `code_len`, with `main` at `main_off`) in a native PE. 66// .text = [18B entry stub][code]; .idata = ExitProcess (exit42 layout). Entry = stub @ RVA 0x1000. 67func nxpcw_emit_pe(buf: *u8, code: *u8, code_len: i64, main_off: i64) -> i64 { 68 if (buf as i64) == 0 { return 0 - NX_PE_BAD_INPUT } 69 let text_vsize: i64 = NXPCW_STUB_LEN + code_len 70 if text_vsize > 0x200 { return 0 - NX_PE_BAD_INPUT } // minimal-slice cap (one .text file chunk) 71 72 // DOS + PE sig 73 _w16(buf, 0, 0x5A4D) 74 _w32(buf, 0x3C, FOFF_PE_SIG) 75 _w32(buf, FOFF_PE_SIG, 0x00004550) 76 // COFF (2 sections) 77 _w16(buf, FOFF_COFF + 0, PE_MACHINE_AMD64) 78 _w16(buf, FOFF_COFF + 2, 2) 79 _w16(buf, FOFF_COFF + 16, 0xF0) 80 _w16(buf, FOFF_COFF + 18, PE_CHAR_EXEC | PE_CHAR_LARGE_ADDR) 81 // Optional Header (PE32+) -- exit42 values 82 _w16(buf, FOFF_OPT + 0, PE_OH_MAGIC_PEPLUS) 83 _w8(buf, FOFF_OPT + 2, 1) 84 _w32(buf, FOFF_OPT + 4, 0x200) 85 _w32(buf, FOFF_OPT + 8, 0x200) 86 _w32(buf, FOFF_OPT + 16, RVA_TEXT) 87 _w32(buf, FOFF_OPT + 20, RVA_TEXT) 88 _w64(buf, FOFF_OPT + 24, IMG_BASE_LO, IMG_BASE_HI) 89 _w32(buf, FOFF_OPT + 32, 0x1000) 90 _w32(buf, FOFF_OPT + 36, 0x200) 91 _w16(buf, FOFF_OPT + 40, 6) 92 _w16(buf, FOFF_OPT + 48, 6) 93 _w32(buf, FOFF_OPT + 56, 0x3000) 94 _w32(buf, FOFF_OPT + 60, 0x200) 95 _w16(buf, FOFF_OPT + 68, PE_SUBSYSTEM_CONSOLE) 96 _w64(buf, FOFF_OPT + 72, 0x100000, 0) 97 _w64(buf, FOFF_OPT + 80, 0x1000, 0) 98 _w64(buf, FOFF_OPT + 88, 0x100000, 0) 99 _w64(buf, FOFF_OPT + 96, 0x1000, 0) 100 _w32(buf, FOFF_OPT + 108, 16) 101 _w32(buf, FOFF_OPT + 112 + 8, RVA_IMP_DESC) 102 _w32(buf, FOFF_OPT + 112 + 12, 0x28) 103 // Section headers: .text (dynamic vsize) + .idata 104 _emit_section_header(buf, FOFF_SECT_TBL, 46, 116, 101, 120, 116, 0, 0, 0, text_vsize, RVA_TEXT, 0x200, FOFF_TEXT, PE_SECT_CODE_X_R) 105 _emit_section_header(buf, FOFF_SECT_TBL + 40, 46, 105, 100, 97, 116, 97, 0, 0, 0x63, RVA_IDATA, 0x200, FOFF_IDATA, PE_SECT_DATA_R) 106 107 // ===== .text: entry stub ===== 108 let t: i64 = FOFF_TEXT 109 // sub rsp,0x28 110 _w8(buf, t+0, 0x48); _w8(buf, t+1, 0x83); _w8(buf, t+2, 0xEC); _w8(buf, t+3, 0x28) 111 // call main (E8 rel32); call ends RVA 0x1009; main RVA = 0x1012+main_off; rel = 0x9+main_off 112 _w8(buf, t+4, 0xE8); _w32(buf, t+5, 0x9 + main_off) 113 // mov ecx,eax 114 _w8(buf, t+9, 0x89); _w8(buf, t+10, 0xC1) 115 // call [rip+ExitProcess] (FF 15 disp32); ends RVA 0x1011; IAT 0x2038; disp 0x1027 116 _w8(buf, t+11, 0xFF); _w8(buf, t+12, 0x15); _w32(buf, t+13, 0x1027) 117 // int3 118 _w8(buf, t+17, 0xCC) 119 // copy assembled code after the stub 120 var i: i64 = 0 121 while i < code_len { buf[t + NXPCW_STUB_LEN + i] = code[i]; i = i + 1 } 122 123 // ===== .idata: single ExitProcess import (exit42 layout) ===== 124 let d: i64 = FOFF_IDATA 125 _w32(buf, d + 0, RVA_INT) 126 _w32(buf, d + 12, RVA_DLL_STR) 127 _w32(buf, d + 16, RVA_IAT) 128 _w64(buf, d + 0x28, RVA_IMP_NAME, 0) 129 _w64(buf, d + 0x30, 0, 0) 130 _w64(buf, d + 0x38, RVA_IMP_NAME, 0) 131 _w64(buf, d + 0x40, 0, 0) 132 _w16(buf, d + 0x48, 0) 133 _w8(buf,d+0x4A,69);_w8(buf,d+0x4B,120);_w8(buf,d+0x4C,105);_w8(buf,d+0x4D,116) // Exit 134 _w8(buf,d+0x4E,80);_w8(buf,d+0x4F,114);_w8(buf,d+0x50,111);_w8(buf,d+0x51,99) // Proc 135 _w8(buf,d+0x52,101);_w8(buf,d+0x53,115);_w8(buf,d+0x54,115);_w8(buf,d+0x55,0) // ess\0 136 _w8(buf,d+0x56,107);_w8(buf,d+0x57,101);_w8(buf,d+0x58,114);_w8(buf,d+0x59,110) // kern 137 _w8(buf,d+0x5A,101);_w8(buf,d+0x5B,108);_w8(buf,d+0x5C,51);_w8(buf,d+0x5D,50) // el32 138 _w8(buf,d+0x5E,46);_w8(buf,d+0x5F,100);_w8(buf,d+0x60,108);_w8(buf,d+0x61,108);_w8(buf,d+0x62,0) // .dll\0 139 140 return NX_PE_OK 141} 142 143func main() -> i64 { 144 let lenbox: *i64 = sys_mmap(16) as *i64 145 let src: *u8 = sys_read_file("/tmp/nxwin.s" as *u8, lenbox) 146 if (src as i64) == 0 { return 1 } 147 let n: i64 = lenbox[0] 148 if n <= 0 { return 2 } 149 150 let code: *u8 = sys_mmap(NXPCW_CODE_CAP) 151 let mainbox: *i64 = sys_mmap(16) as *i64 152 mainbox[0] = 0 - 1 153 let code_len: i64 = nxpcw_assemble_main(src, n, code, NXPCW_CODE_CAP, mainbox) 154 if code_len < 0 { return 3 } 155 let main_off: i64 = mainbox[0] 156 if main_off < 0 { return 4 } // no `main` label found 157 158 let buf: *u8 = sys_mmap(NXPCW_FILE_SIZE) 159 let rc: i64 = nxpcw_emit_pe(buf, code, code_len, main_off) 160 if rc != NX_PE_OK { return 5 } 161 162 let outp: *u8 = "/mnt/c/Users/elder/nishi-core/nxc2/_offc/nx_win_compiled.exe" as *u8 163 if nx_pe_write_to_file(outp, buf, NXPCW_FILE_SIZE) != NX_PE_OK { return 70 } 164 165 let msg: *u8 = "[substrate] nxc2-compiled native PE written: nx_win_compiled.exe\n" as *u8 166 sys_write(1, msg, 64) 167 return 0 168}