code wiki / _hdl_build / nx_pe_dll_emit_gate.nx

nx_pe_dll_emit_gate.nx source

↩ module page · 148 lines · 8463 B

1// nx_pe_dll_emit_gate.nx -- WINDOWS-NATIVE CRUX: emit a sovereign PE *DLL with a named export* that a FOREIGN Windows 2// process can LoadLibrary + GetProcAddress + call. This is the mechanism a Steam game needs: it loads d3d11.dll and 3// calls its exports, so our lib must be a loadable DLL a foreign binary can bind. Here we prove the loadability path 4// end to end with a minimal export (nishi_add(a,b)=a+b). No DllMain (AddressOfEntryPoint=0 -> loader binds directly). 5// Based at 0x180000000 so it never collides with the harness .exe (default 0x140000000); code is position-independent. 6// 100% NishiLang PE bytes; a C harness (benchmark oracle, like WARP/fxc) is the external verifier. license_tier: ORIGINAL expect_exit: 0 7import "nx_syscalls.nx" 8 9func hw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 10func pn(v: i64) -> i64 { let b: *u8=sys_mmap(32) as *u8; var x: i64=v; var ng: i64=0; if x<0{ng=1;x=0-x} var i: i64=31; if x==0{b[i]=48 as u8;i=i-1} while x>0{b[i]=(48+x%10) as u8;x=x/10;i=i-1} if ng==1{b[i]=45 as u8;i=i-1} sys_write(1,(b as i64+i+1) as *u8,31-i); return 0 } 11func px2(v: i64) -> i64 { let d: *u8="0123456789abcdef" as *u8; let b: *u8=sys_mmap(4) as *u8; b[0]=d[(v>>4)&15]; b[1]=d[v&15]; sys_write(1,b,2); return 0 } 12 13func w8(buf: *u8, off: i64, v: i64) -> i64 { buf[off] = (v & 255) as u8; return 0 } 14func w16(buf: *u8, off: i64, v: i64) -> i64 { buf[off]=(v&255) as u8; buf[off+1]=((v>>8)&255) as u8; return 0 } 15func w32(buf: *u8, off: i64, v: i64) -> i64 { buf[off]=(v&255) as u8; buf[off+1]=((v>>8)&255) as u8; buf[off+2]=((v>>16)&255) as u8; buf[off+3]=((v>>24)&255) as u8; return 0 } 16func wstr(buf: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){ buf[off+i]=s[i]; i=i+1 } buf[off+i]=0 as u8; return i+1 } 17 18func main() -> i64 { 19 hw("=== nx_pe_dll_emit_gate -- emit a sovereign PE DLL with a named export (Windows-native loadability crux) ===\n" as *u8) 20 var fails: i64 = 0 21 let N: i64 = 0x600 22 let b: *u8 = sys_mmap(N) // zeroed 23 24 // ---- DOS header ---- 25 w16(b, 0, 0x5A4D) // 'MZ' 26 w32(b, 0x3C, 0x80) // e_lfanew -> PE sig at 0x80 27 28 // ---- PE signature ---- 29 w32(b, 0x80, 0x00004550) // 'PE\0\0' 30 31 // ---- COFF file header @ 0x84 ---- 32 w16(b, 0x84 + 0, 0x8664) // Machine AMD64 33 w16(b, 0x84 + 2, 2) // NumberOfSections (.text + .edata) 34 w32(b, 0x84 + 4, 0) // TimeDateStamp 35 w32(b, 0x84 + 8, 0) // PointerToSymbolTable 36 w32(b, 0x84 + 12, 0) // NumberOfSymbols 37 w16(b, 0x84 + 16, 0xF0) // SizeOfOptionalHeader 38 w16(b, 0x84 + 18, 0x2022) // Characteristics: EXECUTABLE(0x02)|LARGE_ADDR(0x20)|DLL(0x2000) 39 40 // ---- Optional header (PE32+) @ 0x98 ---- 41 let O: i64 = 0x98 42 w16(b, O + 0, 0x020B) // Magic PE32+ 43 w8(b, O + 2, 1) // MajorLinkerVersion 44 w32(b, O + 4, 0x200) // SizeOfCode 45 w32(b, O + 8, 0x200) // SizeOfInitializedData 46 w32(b, O + 12, 0) // SizeOfUninitializedData 47 w32(b, O + 16, 0) // AddressOfEntryPoint = 0 -> no DllMain, loader binds directly 48 w32(b, O + 20, 0x1000) // BaseOfCode 49 w32(b, O + 24, 0x80000000) // ImageBase lo = 0x180000000 (out of the way of a default-based .exe) 50 w32(b, O + 28, 0x1) // ImageBase hi 51 w32(b, O + 32, 0x1000) // SectionAlignment 52 w32(b, O + 36, 0x200) // FileAlignment 53 w16(b, O + 40, 6) // MajorOSVersion 54 w16(b, O + 48, 6) // MajorSubsystemVersion 55 w32(b, O + 56, 0x3000) // SizeOfImage (headers + .text + .edata pages) 56 w32(b, O + 60, 0x200) // SizeOfHeaders 57 w16(b, O + 68, 3) // Subsystem CONSOLE 58 w16(b, O + 70, 0) // DllCharacteristics = 0 (no ASLR; code is base-independent, no reloc needed) 59 w32(b, O + 72, 0x100000) // SizeOfStackReserve lo 60 w32(b, O + 80, 0x1000) // SizeOfStackCommit lo 61 w32(b, O + 88, 0x100000) // SizeOfHeapReserve lo 62 w32(b, O + 96, 0x1000) // SizeOfHeapCommit lo 63 w32(b, O + 108, 16) // NumberOfRvaAndSizes 64 // DataDirectory[0] = Export table (RVA 0x2000, size 0x49) 65 w32(b, O + 112 + 0, 0x2000) 66 w32(b, O + 112 + 4, 0x49) 67 // [1] Import = 0 (no imports needed) 68 69 // ---- Section table @ 0x188 ---- 70 let S: i64 = 0x188 71 // .text 72 wstr(b, S + 0, ".text\x00" as *u8) 73 w32(b, S + 8, 0x10) // VirtualSize 74 w32(b, S + 12, 0x1000) // VirtualAddress 75 w32(b, S + 16, 0x200) // SizeOfRawData 76 w32(b, S + 20, 0x200) // PointerToRawData 77 w32(b, S + 36, 0x60000020) // CODE|EXEC|READ 78 // .edata 79 wstr(b, S + 40 + 0, ".edata\x00" as *u8) 80 w32(b, S + 40 + 8, 0x49) // VirtualSize 81 w32(b, S + 40 + 12, 0x2000) // VirtualAddress 82 w32(b, S + 40 + 16, 0x200) // SizeOfRawData 83 w32(b, S + 40 + 20, 0x400) // PointerToRawData 84 w32(b, S + 40 + 36, 0x40000040) // INITIALIZED_DATA|READ 85 86 // ---- .text @ file 0x200 (RVA 0x1000): int nishi_add(int a,int b){return a+b;} ---- 87 // MS x64: a=ecx, b=edx, ret in eax. mov eax,ecx ; add eax,edx ; ret 88 w8(b, 0x200, 0x89); w8(b, 0x201, 0xC8) // mov eax, ecx 89 w8(b, 0x202, 0x01); w8(b, 0x203, 0xD0) // add eax, edx 90 w8(b, 0x204, 0xC3) // ret 91 92 // ---- .edata @ file 0x400 (RVA 0x2000): IMAGE_EXPORT_DIRECTORY + tables + strings ---- 93 let E: i64 = 0x400 94 // Export Directory (40 bytes) 95 w32(b, E + 0, 0) // Characteristics 96 w32(b, E + 4, 0) // TimeDateStamp 97 w16(b, E + 8, 0) // MajorVersion 98 w16(b, E + 10, 0) // MinorVersion 99 w32(b, E + 12, 0x203C) // Name RVA -> "nishigpu.dll" 100 w32(b, E + 16, 1) // Base (ordinal base) 101 w32(b, E + 20, 1) // NumberOfFunctions 102 w32(b, E + 24, 1) // NumberOfNames 103 w32(b, E + 28, 0x2028) // AddressOfFunctions RVA 104 w32(b, E + 32, 0x202C) // AddressOfNames RVA 105 w32(b, E + 36, 0x2030) // AddressOfNameOrdinals RVA 106 // AddressOfFunctions[0] @ 0x2028 -> function RVA 0x1000 107 w32(b, E + 0x28, 0x1000) 108 // AddressOfNames[0] @ 0x202C -> funcname RVA 0x2032 109 w32(b, E + 0x2C, 0x2032) 110 // AddressOfNameOrdinals[0] @ 0x2030 -> 0 (index into function table) 111 w16(b, E + 0x30, 0) 112 // funcname "nishi_add\0" @ 0x2032 113 wstr(b, E + 0x32, "nishi_add\x00" as *u8) 114 // dllname "nishigpu.dll\0" @ 0x203C 115 wstr(b, E + 0x3C, "nishigpu.dll\x00" as *u8) 116 117 // ---- write the DLL ---- 118 let fd: i64 = sys_openat_wr("knowledge/nishigpu.dll\x00" as *u8, 0x1a4) 119 if fd < 0 { hw("FAIL cannot open output\n" as *u8); sys_exit(1); return 1 } 120 let wrote: i64 = sys_write(fd, b, N) 121 sys_close(fd) 122 123 // ---- self-checks ---- 124 hw(" wrote "); pn(wrote); hw(" bytes to knowledge/nishigpu.dll\n" as *u8) 125 hw(" MZ="); px2(b[0] as i64); px2(b[1] as i64) 126 hw(" PEsig="); px2(b[0x80] as i64); px2(b[0x81] as i64); px2(b[0x82] as i64); px2(b[0x83] as i64) 127 hw(" Char="); px2(b[0x84+19] as i64); px2(b[0x84+18] as i64) 128 hw(" export.name.rva=0x"); px2((0x203C>>8)&255); px2(0x203C&255); hw("\n" as *u8) 129 130 var t1: i64 = 0 131 if (b[0] as i64) == 0x4D { if (b[1] as i64) == 0x5A { t1 = 1 } } 132 if t1 == 1 { hw("T1 PASS MZ signature\n" as *u8) } else { fails=fails+1; hw("T1 FAIL\n" as *u8) } 133 var t2: i64 = 0 134 if (b[0x84+18] as i64) == 0x22 { if (b[0x84+19] as i64) == 0x20 { t2 = 1 } } // 0x2022 DLL bit set 135 if t2 == 1 { hw("T2 PASS IMAGE_FILE_DLL characteristic (0x2022)\n" as *u8) } else { fails=fails+1; hw("T2 FAIL char\n" as *u8) } 136 var t3: i64 = 0 137 if wrote == N { t3 = 1 } 138 if t3 == 1 { hw("T3 PASS full image written (0x600 bytes)\n" as *u8) } else { fails=fails+1; hw("T3 FAIL wrote="); pn(wrote); hw("\n" as *u8) } 139 // T4 export directory Name RVA + funcname bytes present 140 var t4: i64 = 0 141 if (b[0x400 + 0x32] as i64) == 110 { if (b[0x400 + 0x33] as i64) == 105 { t4 = 1 } } // 'n','i' of nishi_add 142 if t4 == 1 { hw("T4 PASS export name 'nishi_add' present in .edata\n" as *u8) } else { fails=fails+1; hw("T4 FAIL export name\n" as *u8) } 143 144 if fails == 0 { hw("NX-PE-DLL verdict=GREEN -- sovereign PE DLL with named export emitted; C harness must now load+call it\n" as *u8); sys_exit(0); return 0 } 145 hw("NX-PE-DLL RED fails="); pn(fails); hw("\n" as *u8) 146 sys_exit(1) 147 return 1 148}