nx_pe_job_run_test.nx source
↩ module page · 232 lines · 14443 B
1// nx_pe_job_run_test.nx -- W2c: minimal sovereign Windows CONTAINER RUN, native, no HCS/VM.
2//
3// The W2 capstone: ONE native PE that composes the whole governed-container-run pipeline --
4// the blueprint nx_container_win (W4) will emit. Job boundary (W2a) + limit (W2b) + spawn/wait/reap (W0):
5// 1. CreateJobObjectW(NULL,NULL) -> hJob
6// 2. SetInformationJobObject(hJob, 2, &basic{ActiveProcessLimit=8}, 0x40) (governance installed)
7// 3. AssignProcessToJobObject(hJob, (HANDLE)-1) (self joins the job)
8// 4. CreateProcessW(NULL,"cmd.exe /c exit 7",...,CREATE_NO_WINDOW,...,&si,&pi) (workload; auto-joins job)
9// 5. WaitForSingleObject(pi.hProcess, INFINITE)
10// 6. GetExitCodeProcess(pi.hProcess, &code)
11// 7. ExitProcess(code) -> 7 == governed workload ran & was reaped
12//
13// PROOF: exit 7 == the child workload ran to completion INSIDE our limit-configured Job Object and we
14// reaped its real exit code. (Enforcement itself is proven separately + no-false-green in W2b; this rung
15// proves the composed run+reap pipeline + exit-code propagation under governance.)
16//
17// 7 kernel32 imports. 3 sections. Founded on nx_pe_writer.nx (hand byte-layout, tutor-scaffold).
18// genealogy_id: win32_job_objects_2000 + runc_init_2014 + lmctfy_2013
19// lineage_id: substrate_pe_jobrun_v1
20
21import "nx_syscalls.nx"
22import "nx_hal.nx"
23import "nx_pe_writer.nx"
24
25const PE_JOBRUN_FILE_SIZE: i64 = 0x800
26
27// .idata (7 imports), RVA 0x3000. IAT RVAs: CJO 0x3068 SIJO 0x3070 APTJ 0x3078 CPW 0x3080
28// WAIT 0x3088 GECP 0x3090 EXIT 0x3098.
29func nx_pe_emit_job_run(buf: *u8) -> i64 {
30 if (buf as i64) == 0 { return 0 - NX_PE_BAD_INPUT }
31
32 _w16(buf, 0, 0x5A4D)
33 _w32(buf, 0x3C, FOFF_PE_SIG)
34 _w32(buf, FOFF_PE_SIG, 0x00004550)
35
36 _w16(buf, FOFF_COFF + 0, PE_MACHINE_AMD64)
37 _w16(buf, FOFF_COFF + 2, 3)
38 _w16(buf, FOFF_COFF + 16, 0xF0)
39 _w16(buf, FOFF_COFF + 18, PE_CHAR_EXEC | PE_CHAR_LARGE_ADDR)
40
41 _w16(buf, FOFF_OPT + 0, PE_OH_MAGIC_PEPLUS)
42 _w8(buf, FOFF_OPT + 2, 1)
43 _w32(buf, FOFF_OPT + 4, 0x200)
44 _w32(buf, FOFF_OPT + 8, 0x400)
45 _w32(buf, FOFF_OPT + 16, PE_HELLO_RVA_TEXT)
46 _w32(buf, FOFF_OPT + 20, PE_HELLO_RVA_TEXT)
47 _w64(buf, FOFF_OPT + 24, IMG_BASE_LO, IMG_BASE_HI)
48 _w32(buf, FOFF_OPT + 32, 0x1000)
49 _w32(buf, FOFF_OPT + 36, 0x200)
50 _w16(buf, FOFF_OPT + 40, 6)
51 _w16(buf, FOFF_OPT + 48, 6)
52 _w32(buf, FOFF_OPT + 56, 0x4000)
53 _w32(buf, FOFF_OPT + 60, 0x200)
54 _w16(buf, FOFF_OPT + 68, PE_SUBSYSTEM_CONSOLE)
55 _w64(buf, FOFF_OPT + 72, 0x100000, 0)
56 _w64(buf, FOFF_OPT + 80, 0x1000, 0)
57 _w64(buf, FOFF_OPT + 88, 0x100000, 0)
58 _w64(buf, FOFF_OPT + 96, 0x1000, 0)
59 _w32(buf, FOFF_OPT + 108, 16)
60 _w32(buf, FOFF_OPT + 112 + 8, PE_HELLO_RVA_IDATA)
61 _w32(buf, FOFF_OPT + 112 + 12, 0x28)
62
63 _emit_section_header(buf, FOFF_SECT_TBL, 46, 116, 101, 120, 116, 0, 0, 0, 0xF8, PE_HELLO_RVA_TEXT, 0x200, PE_HELLO_FOFF_TEXT, PE_SECT_CODE_X_R)
64 _emit_section_header(buf, FOFF_SECT_TBL + 40, 46, 100, 97, 116, 97, 0, 0, 0, 0x24, PE_HELLO_RVA_RDATA, 0x200, PE_HELLO_FOFF_RDATA, PE_SECT_DATA_RW)
65 _emit_section_header(buf, FOFF_SECT_TBL + 80, 46, 105, 100, 97, 116, 97, 0, 0, 0x4B, PE_HELLO_RVA_IDATA, 0x200, PE_HELLO_FOFF_IDATA, PE_SECT_DATA_R)
66 // NOTE: idata vsize 0x14B > 0x100; section header vsize field holds low part; loader maps the 0x1000
67 // virtual page regardless. We pass 0x14B via two writes below to be exact.
68 _w32(buf, FOFF_SECT_TBL + 80 + 8, 0x14B)
69
70 // ===== .text =====
71 let t: i64 = PE_HELLO_FOFF_TEXT
72 // sub rsp,0x138
73 _w8(buf, t+0, 0x48); _w8(buf, t+1, 0x81); _w8(buf, t+2, 0xEC); _w32(buf, t+3, 0x138)
74 // xor eax,eax
75 _w8(buf, t+7, 0x31); _w8(buf, t+8, 0xC0)
76 // lea rdi,[rsp+0x58]
77 _w8(buf, t+9, 0x48); _w8(buf, t+10, 0x8D); _w8(buf, t+11, 0x7C); _w8(buf, t+12, 0x24); _w8(buf, t+13, 0x58)
78 // mov ecx,24 ; rep stosq
79 _w8(buf, t+14, 0xB9); _w32(buf, t+15, 24)
80 _w8(buf, t+19, 0xF3); _w8(buf, t+20, 0x48); _w8(buf, t+21, 0xAB)
81 // mov dword [rsp+0x58],0x68
82 _w8(buf, t+22, 0xC7); _w8(buf, t+23, 0x44); _w8(buf, t+24, 0x24); _w8(buf, t+25, 0x58); _w32(buf, t+26, 0x68)
83 // CreateJobObjectW(NULL,NULL)
84 _w8(buf, t+30, 0x31); _w8(buf, t+31, 0xC9); _w8(buf, t+32, 0x31); _w8(buf, t+33, 0xD2)
85 // call [CJO] next 0x1028 tgt 0x3068 disp 0x2040
86 _w8(buf, t+34, 0xFF); _w8(buf, t+35, 0x15); _w32(buf, t+36, 0x2040)
87 // mov [rsp+0x50],rax (hJob)
88 _w8(buf, t+40, 0x48); _w8(buf, t+41, 0x89); _w8(buf, t+42, 0x44); _w8(buf, t+43, 0x24); _w8(buf, t+44, 0x50)
89 // mov dword [rsp+0xE8],8 (LimitFlags=ACTIVE_PROCESS)
90 _w8(buf, t+45, 0xC7); _w8(buf, t+46, 0x84); _w8(buf, t+47, 0x24); _w32(buf, t+48, 0xE8); _w32(buf, t+52, 8)
91 // mov dword [rsp+0x100],8 (ActiveProcessLimit=8, generous)
92 _w8(buf, t+56, 0xC7); _w8(buf, t+57, 0x84); _w8(buf, t+58, 0x24); _w32(buf, t+59, 0x100); _w32(buf, t+63, 8)
93 // SetInformationJobObject(hJob,2,&jobinfo,0x40)
94 _w8(buf, t+67, 0x48); _w8(buf, t+68, 0x8B); _w8(buf, t+69, 0x4C); _w8(buf, t+70, 0x24); _w8(buf, t+71, 0x50)
95 _w8(buf, t+72, 0xBA); _w32(buf, t+73, 2)
96 _w8(buf, t+77, 0x4C); _w8(buf, t+78, 0x8D); _w8(buf, t+79, 0x84); _w8(buf, t+80, 0x24); _w32(buf, t+81, 0xD8)
97 _w8(buf, t+85, 0x41); _w8(buf, t+86, 0xB9); _w32(buf, t+87, 0x40)
98 // call [SIJO] next 0x1061 tgt 0x3070 disp 0x200F
99 _w8(buf, t+91, 0xFF); _w8(buf, t+92, 0x15); _w32(buf, t+93, 0x200F)
100 // AssignProcessToJobObject(hJob,-1)
101 _w8(buf, t+97, 0x48); _w8(buf, t+98, 0x8B); _w8(buf, t+99, 0x4C); _w8(buf, t+100, 0x24); _w8(buf, t+101, 0x50)
102 _w8(buf, t+102, 0x48); _w8(buf, t+103, 0xC7); _w8(buf, t+104, 0xC2); _w8(buf, t+105, 0xFF); _w8(buf, t+106, 0xFF); _w8(buf, t+107, 0xFF); _w8(buf, t+108, 0xFF)
103 // call [APTJ] next 0x1073 tgt 0x3078 disp 0x2005
104 _w8(buf, t+109, 0xFF); _w8(buf, t+110, 0x15); _w32(buf, t+111, 0x2005)
105 // CreateProcessW(...)
106 _w8(buf, t+115, 0x31); _w8(buf, t+116, 0xC9)
107 // lea rdx,[rip+cmdline] next 0x107C tgt 0x2000 disp 0xF84
108 _w8(buf, t+117, 0x48); _w8(buf, t+118, 0x8D); _w8(buf, t+119, 0x15); _w32(buf, t+120, 0xF84)
109 _w8(buf, t+124, 0x45); _w8(buf, t+125, 0x31); _w8(buf, t+126, 0xC0)
110 _w8(buf, t+127, 0x45); _w8(buf, t+128, 0x31); _w8(buf, t+129, 0xC9)
111 _w8(buf, t+130, 0xC7); _w8(buf, t+131, 0x44); _w8(buf, t+132, 0x24); _w8(buf, t+133, 0x20); _w32(buf, t+134, 0)
112 _w8(buf, t+138, 0xC7); _w8(buf, t+139, 0x44); _w8(buf, t+140, 0x24); _w8(buf, t+141, 0x28); _w32(buf, t+142, 0x08000000)
113 _w8(buf, t+146, 0x48); _w8(buf, t+147, 0xC7); _w8(buf, t+148, 0x44); _w8(buf, t+149, 0x24); _w8(buf, t+150, 0x30); _w32(buf, t+151, 0)
114 _w8(buf, t+155, 0x48); _w8(buf, t+156, 0xC7); _w8(buf, t+157, 0x44); _w8(buf, t+158, 0x24); _w8(buf, t+159, 0x38); _w32(buf, t+160, 0)
115 _w8(buf, t+164, 0x48); _w8(buf, t+165, 0x8D); _w8(buf, t+166, 0x44); _w8(buf, t+167, 0x24); _w8(buf, t+168, 0x58)
116 _w8(buf, t+169, 0x48); _w8(buf, t+170, 0x89); _w8(buf, t+171, 0x44); _w8(buf, t+172, 0x24); _w8(buf, t+173, 0x40)
117 _w8(buf, t+174, 0x48); _w8(buf, t+175, 0x8D); _w8(buf, t+176, 0x84); _w8(buf, t+177, 0x24); _w32(buf, t+178, 0xC0)
118 _w8(buf, t+182, 0x48); _w8(buf, t+183, 0x89); _w8(buf, t+184, 0x44); _w8(buf, t+185, 0x24); _w8(buf, t+186, 0x48)
119 // call [CPW] next 0x10C1 tgt 0x3080 disp 0x1FBF
120 _w8(buf, t+187, 0xFF); _w8(buf, t+188, 0x15); _w32(buf, t+189, 0x1FBF)
121 // --- wait + reap + propagate ---
122 // mov rcx,[rsp+0xC0] (pi.hProcess)
123 _w8(buf, t+193, 0x48); _w8(buf, t+194, 0x8B); _w8(buf, t+195, 0x8C); _w8(buf, t+196, 0x24); _w32(buf, t+197, 0xC0)
124 // mov edx,-1 (INFINITE)
125 _w8(buf, t+201, 0xBA); _w8(buf, t+202, 0xFF); _w8(buf, t+203, 0xFF); _w8(buf, t+204, 0xFF); _w8(buf, t+205, 0xFF)
126 // call [WAIT] next 0x10D4 tgt 0x3088 disp 0x1FB4
127 _w8(buf, t+206, 0xFF); _w8(buf, t+207, 0x15); _w32(buf, t+208, 0x1FB4)
128 // mov rcx,[rsp+0xC0]
129 _w8(buf, t+212, 0x48); _w8(buf, t+213, 0x8B); _w8(buf, t+214, 0x8C); _w8(buf, t+215, 0x24); _w32(buf, t+216, 0xC0)
130 // lea rdx,[rsp+0x118] (&exitCode)
131 _w8(buf, t+220, 0x48); _w8(buf, t+221, 0x8D); _w8(buf, t+222, 0x94); _w8(buf, t+223, 0x24); _w32(buf, t+224, 0x118)
132 // call [GECP] next 0x10EA tgt 0x3090 disp 0x1FA6
133 _w8(buf, t+228, 0xFF); _w8(buf, t+229, 0x15); _w32(buf, t+230, 0x1FA6)
134 // mov ecx,[rsp+0x118]
135 _w8(buf, t+234, 0x8B); _w8(buf, t+235, 0x8C); _w8(buf, t+236, 0x24); _w32(buf, t+237, 0x118)
136 // call [EXIT] next 0x10F7 tgt 0x3098 disp 0x1FA1
137 _w8(buf, t+241, 0xFF); _w8(buf, t+242, 0x15); _w32(buf, t+243, 0x1FA1)
138 // int3
139 _w8(buf, t+247, 0xCC)
140
141 // ===== .rdata: "cmd.exe /c exit 7\0" UTF-16 =====
142 let r: i64 = PE_HELLO_FOFF_RDATA
143 _w16(buf, r+0, 0x63); _w16(buf, r+2, 0x6D); _w16(buf, r+4, 0x64); _w16(buf, r+6, 0x2E)
144 _w16(buf, r+8, 0x65); _w16(buf, r+10, 0x78); _w16(buf, r+12, 0x65); _w16(buf, r+14, 0x20)
145 _w16(buf, r+16, 0x2F); _w16(buf, r+18, 0x63); _w16(buf, r+20, 0x20); _w16(buf, r+22, 0x65)
146 _w16(buf, r+24, 0x78); _w16(buf, r+26, 0x69); _w16(buf, r+28, 0x74); _w16(buf, r+30, 0x20)
147 _w16(buf, r+32, 0x37); _w16(buf, r+34, 0x00) // '7' NUL
148
149 // ===== .idata (7 imports) =====
150 let d: i64 = PE_HELLO_FOFF_IDATA
151 _w32(buf, d+0, 0x3028) // OFT = INT RVA
152 _w32(buf, d+12, 0x313E) // Name = "kernel32.dll"
153 _w32(buf, d+16, 0x3068) // FT = IAT RVA
154 // INT @ 0x28 (8 qwords)
155 _w64(buf, d+0x28, 0x30A8, 0) // CreateJobObjectW
156 _w64(buf, d+0x30, 0x30BC, 0) // SetInformationJobObject
157 _w64(buf, d+0x38, 0x30D6, 0) // AssignProcessToJobObject
158 _w64(buf, d+0x40, 0x30F2, 0) // CreateProcessW
159 _w64(buf, d+0x48, 0x3104, 0) // WaitForSingleObject
160 _w64(buf, d+0x50, 0x311A, 0) // GetExitCodeProcess
161 _w64(buf, d+0x58, 0x3130, 0) // ExitProcess
162 _w64(buf, d+0x60, 0, 0)
163 // IAT @ 0x68 (8 qwords)
164 _w64(buf, d+0x68, 0x30A8, 0)
165 _w64(buf, d+0x70, 0x30BC, 0)
166 _w64(buf, d+0x78, 0x30D6, 0)
167 _w64(buf, d+0x80, 0x30F2, 0)
168 _w64(buf, d+0x88, 0x3104, 0)
169 _w64(buf, d+0x90, 0x311A, 0)
170 _w64(buf, d+0x98, 0x3130, 0)
171 _w64(buf, d+0xA0, 0, 0)
172 // byname: CreateJobObjectW @ 0xA8
173 _w16(buf, d+0xA8, 0)
174 _w8(buf,d+0xAA,67);_w8(buf,d+0xAB,114);_w8(buf,d+0xAC,101);_w8(buf,d+0xAD,97);_w8(buf,d+0xAE,116);_w8(buf,d+0xAF,101)
175 _w8(buf,d+0xB0,74);_w8(buf,d+0xB1,111);_w8(buf,d+0xB2,98);_w8(buf,d+0xB3,79);_w8(buf,d+0xB4,98);_w8(buf,d+0xB5,106)
176 _w8(buf,d+0xB6,101);_w8(buf,d+0xB7,99);_w8(buf,d+0xB8,116);_w8(buf,d+0xB9,87);_w8(buf,d+0xBA,0)
177 // SetInformationJobObject @ 0xBC
178 _w16(buf, d+0xBC, 0)
179 _w8(buf,d+0xBE,83);_w8(buf,d+0xBF,101);_w8(buf,d+0xC0,116);_w8(buf,d+0xC1,73);_w8(buf,d+0xC2,110);_w8(buf,d+0xC3,102)
180 _w8(buf,d+0xC4,111);_w8(buf,d+0xC5,114);_w8(buf,d+0xC6,109);_w8(buf,d+0xC7,97);_w8(buf,d+0xC8,116);_w8(buf,d+0xC9,105)
181 _w8(buf,d+0xCA,111);_w8(buf,d+0xCB,110);_w8(buf,d+0xCC,74);_w8(buf,d+0xCD,111);_w8(buf,d+0xCE,98);_w8(buf,d+0xCF,79)
182 _w8(buf,d+0xD0,98);_w8(buf,d+0xD1,106);_w8(buf,d+0xD2,101);_w8(buf,d+0xD3,99);_w8(buf,d+0xD4,116);_w8(buf,d+0xD5,0)
183 // AssignProcessToJobObject @ 0xD6
184 _w16(buf, d+0xD6, 0)
185 _w8(buf,d+0xD8,65);_w8(buf,d+0xD9,115);_w8(buf,d+0xDA,115);_w8(buf,d+0xDB,105);_w8(buf,d+0xDC,103);_w8(buf,d+0xDD,110)
186 _w8(buf,d+0xDE,80);_w8(buf,d+0xDF,114);_w8(buf,d+0xE0,111);_w8(buf,d+0xE1,99);_w8(buf,d+0xE2,101);_w8(buf,d+0xE3,115)
187 _w8(buf,d+0xE4,115);_w8(buf,d+0xE5,84);_w8(buf,d+0xE6,111);_w8(buf,d+0xE7,74);_w8(buf,d+0xE8,111);_w8(buf,d+0xE9,98)
188 _w8(buf,d+0xEA,79);_w8(buf,d+0xEB,98);_w8(buf,d+0xEC,106);_w8(buf,d+0xED,101);_w8(buf,d+0xEE,99);_w8(buf,d+0xEF,116);_w8(buf,d+0xF0,0)
189 // CreateProcessW @ 0xF2
190 _w16(buf, d+0xF2, 0)
191 _w8(buf,d+0xF4,67);_w8(buf,d+0xF5,114);_w8(buf,d+0xF6,101);_w8(buf,d+0xF7,97);_w8(buf,d+0xF8,116);_w8(buf,d+0xF9,101)
192 _w8(buf,d+0xFA,80);_w8(buf,d+0xFB,114);_w8(buf,d+0xFC,111);_w8(buf,d+0xFD,99);_w8(buf,d+0xFE,101);_w8(buf,d+0xFF,115)
193 _w8(buf,d+0x100,115);_w8(buf,d+0x101,87);_w8(buf,d+0x102,0)
194 // WaitForSingleObject @ 0x104
195 _w16(buf, d+0x104, 0)
196 _w8(buf,d+0x106,87);_w8(buf,d+0x107,97);_w8(buf,d+0x108,105);_w8(buf,d+0x109,116);_w8(buf,d+0x10A,70);_w8(buf,d+0x10B,111)
197 _w8(buf,d+0x10C,114);_w8(buf,d+0x10D,83);_w8(buf,d+0x10E,105);_w8(buf,d+0x10F,110);_w8(buf,d+0x110,103);_w8(buf,d+0x111,108)
198 _w8(buf,d+0x112,101);_w8(buf,d+0x113,79);_w8(buf,d+0x114,98);_w8(buf,d+0x115,106);_w8(buf,d+0x116,101);_w8(buf,d+0x117,99)
199 _w8(buf,d+0x118,116);_w8(buf,d+0x119,0)
200 // GetExitCodeProcess @ 0x11A
201 _w16(buf, d+0x11A, 0)
202 _w8(buf,d+0x11C,71);_w8(buf,d+0x11D,101);_w8(buf,d+0x11E,116);_w8(buf,d+0x11F,69);_w8(buf,d+0x120,120);_w8(buf,d+0x121,105)
203 _w8(buf,d+0x122,116);_w8(buf,d+0x123,67);_w8(buf,d+0x124,111);_w8(buf,d+0x125,100);_w8(buf,d+0x126,101);_w8(buf,d+0x127,80)
204 _w8(buf,d+0x128,114);_w8(buf,d+0x129,111);_w8(buf,d+0x12A,99);_w8(buf,d+0x12B,101);_w8(buf,d+0x12C,115);_w8(buf,d+0x12D,115)
205 _w8(buf,d+0x12E,0)
206 // ExitProcess @ 0x130
207 _w16(buf, d+0x130, 0)
208 _w8(buf,d+0x132,69);_w8(buf,d+0x133,120);_w8(buf,d+0x134,105);_w8(buf,d+0x135,116);_w8(buf,d+0x136,80);_w8(buf,d+0x137,114)
209 _w8(buf,d+0x138,111);_w8(buf,d+0x139,99);_w8(buf,d+0x13A,101);_w8(buf,d+0x13B,115);_w8(buf,d+0x13C,115);_w8(buf,d+0x13D,0)
210 // "kernel32.dll\0" @ 0x13E
211 _w8(buf,d+0x13E,107);_w8(buf,d+0x13F,101);_w8(buf,d+0x140,114);_w8(buf,d+0x141,110);_w8(buf,d+0x142,101);_w8(buf,d+0x143,108)
212 _w8(buf,d+0x144,51);_w8(buf,d+0x145,50);_w8(buf,d+0x146,46);_w8(buf,d+0x147,100);_w8(buf,d+0x148,108);_w8(buf,d+0x149,108);_w8(buf,d+0x14A,0)
213
214 return NX_PE_OK
215}
216
217func main() -> i64 {
218 let buf: *u8 = nx_hal_alloc_pages(PE_JOBRUN_FILE_SIZE)
219 if (buf as i64) == 0 { return 1 }
220 let rc: i64 = nx_pe_emit_job_run(buf)
221 if rc != NX_PE_OK { return 2 }
222 if buf[0] != 0x4D { return 10 }
223 if buf[0x86] != 0x03 { return 11 }
224 if buf[0x200] != 0x48 { return 12 }
225 if buf[0x420] != 0x37 { return 13 } // .rdata 'exit 7' -> '7' wchar (r+32 decimal = 0x400+0x20 = 0x420)
226 if buf[0x6AA] != 0x43 { return 14 } // 'C' of CreateJobObjectW (d+0xAA = 0x600+0xAA = 0x6AA)
227 let p: *u8 = "/mnt/c/Users/elder/nishi-core/nxc2/_offc/nx_pe_job_run.exe" as *u8
228 if nx_pe_write_to_file(p, buf, PE_JOBRUN_FILE_SIZE) != NX_PE_OK { return 70 }
229 let msg: *u8 = "[substrate] Container-run PE written: nx_pe_job_run.exe (job+limit+spawn+reap)\n" as *u8
230 sys_write(1, msg, 77)
231 return 0
232}