Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions ARCHITECTURE.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,20 @@
"summary": "validated_manjeom=0(실 게임 프레임 미증명) 돌파의 명명된 사다리 = native E-series (docs/CLOSURE_ROADMAP.md §5). fleet-lab F-NSWINDOW-E5 가 추적. 각 라운드 measured 결과를 여기 update-in-place 박제 → 차세션 research 가 흩어진 메모리 대신 한 곳에서 표적을 본다.",
"children": [
{
"name": "F-NSWINDOW-E5 — E1✅ E2✅ E3✅ E4🛠️ E5⬜",
"summary": "validated_manjeom=0 돌파 사다리(docs/CLOSURE_ROADMAP.md §5). E1 PE-parse+mmap done(CM-25a)·E2 i386 디코더 done(CM-25b)·**E3 i386 인터프리터 done**(실 game-binary EXECUTE)·**E4 kernel32 IAT 바인딩 IN-PROGRESS**·E5 first NSWindow=validated_manjeom>0 첫후보(CM-25e). 실행은 native/ C(i386_cpu.c)·lib/loader/pe_i386_*.hexa는 TSV 트래커(실행 아님)."
"name": "F-NSWINDOW-E5 — E1✅ E2✅ E3✅ E4🛠️(user-entry 도달) E5⬜",
"summary": "validated_manjeom=0 돌파 사다리(docs/CLOSURE_ROADMAP.md §5). E1 PE-parse+mmap done(CM-25a)·E2 i386 디코더 done(CM-25b)·E3 i386 인터프리터 done(실 game-binary EXECUTE)·E4 kernel32 IAT 바인딩 IN-PROGRESS(합성 prologue가 CRT→user-entry handoff 도달, insns=60, K32 import 13)·E5 first NSWindow=validated_manjeom>0 첫후보(CM-25e). 실행은 native/ C(i386_cpu.c)·lib/loader/pe_i386_*.hexa는 TSV 트래커(실행 아님)."
},
{
"name": "E4 r5 measured (2026-06-26·#19/24/25/26 merged) — 18명령 실행·kernel32 5 import 바인딩",
"summary": "native/i386_cpu.c 인터프리터가 실 __scrt_common_main prologue 18명령 실행. import-stub registry 5엔트리 바인딩(GetCurrentThreadId·GetCurrentProcessId·GetTickCount·GetSystemTimeAsFileTime[버퍼쓰기 shim·LPFILETIME 8B mem_write]·QueryPerformanceCounter). 디코더 group-1 imm+mov r/m imm+byte mov+test+and/or+shift(0xC1/D1/D3) 커버(C+i386_decode.hexa RUNEQ byte-eq). EFLAGS(CF/PF/AF/ZF/SF/OF) 모델. 측정: insns 1(r2)→9(r3)→11(r4)→**18(r5)**·halt@0x5392A1=unbound IAT slot 0x538014. Blacksmith CI 게이트(i386_cpu_test PASS·ci.yml native step). own1: 로더가 자기 import 바인딩=로딩(바이패스 아님)·kernel32=OS API·Wine/QEMU 0. ⚠️ validated_manjeom 여전히 0(로더 진척·프레임 아님·E5가 프레임)."
},
{
"name": "r6 다음 표적 (measured wall, reopenable)",
"summary": "벽=unbound IAT slot 0x538014@0x5392A1. r6: 다음 kernel32 import 바인딩 + 0F B6 MOVZX 디코더 gap + __security_init_cookie(첫 TEB/PEB 접근·bounded-synthetic). prologue 아직 main/WinMain 미도달 — CRT-init import set 소진→mainCRTStartup→main→user32 CreateWindowEx(E5 경로). 매 rung CI 검증·머지·단일직렬(swap 19.7G 피크 경험)."
"name": "E4 r7/r8 measured (2026-06-26·#29 + r8 PR) — 60명령·CRT→user-entry handoff(합성)·K32 import 13",
"summary": "native/i386_cpu.c 인터프리터가 합성 __scrt_common_main prologue를 r7 벽(unbound IAT @0x539318, insns=44) 너머로 전진 → CRT init 종점 = `call WinMain` CRT→user-entry handoff(i386_cpu_t.user_entry_va + I386_HALT_USER_ENTRY). 디코더: 0F AF/69/6B IMUL·0F A2 CPUID·0F 31 RDTSC(r7) + 0F A3/AB/B3/BB BT/BTS/BTR/BTC(r8) — C + i386_decode.hexa RUNEQ byte-eq. import: security cookie 산술 + GetCommandLineW(0x53802C)·SetUnhandledExceptionFilter(0x538030) 바인딩, K32 11→13. 측정: insns 44(r7)→60(r8)·halt@0x539400=user_entry(call). Blacksmith CI 게이트(i386_cpu_test 25 checks PASS·-Wall -Wextra -std=c11). real-PE 경로 i386_cpu_load_pe 구조적 배선완료(섹션+entry 매핑)·미실행(IAT name-autobind 필요). ⚠️ validated_manjeom 여전히 0(합성 user-entry·실 프레임 아님)."
},
{
"name": "🧱 E5 게이트 재분류 (2026-06-26 실측 — substrate/투자 프론티어, 하드웨어 천장 아님)",
"summary": "validated_manjeom>0(실 프레임) 도달 게이트를 실측 재분류. ① real i386 D3D PE — 최소 D3D11 테스트 PE 자작 가능(own1 클린·꼭 AAA 아님). ② D3DMetal SDK — 'CI 부재'는 틀림: `brew tap apple/apple && brew install game-porting-toolkit`로 Blacksmith macos-15(실 Apple Silicon·GPU)에 설치 가능(하드웨어 무관). ③ 디스플레이 — 불필요: Metal 헤드리스(오프스크린 MTLTexture→getBytes readback)로 화면 없이 프레임 증명 가능(M4/Metal4 확인). → 진짜 벽 = 엔지니어링 거리: lib/loader/dx_d3d11.hexa 등 DX→Metal 브리지는 MTL* 이름만 적힌 텔레메트리 스캐폴드(실 Metal 호출 0건). 다음 사다리(자율가능, 실-GPU CI 위): real-PE IAT name-autobind → 넓은 opcode 커버 → D3D11 device shim → 실 MTLDevice/오프스크린 → readback. 이전 'real-asset user-only 게이트' 분류는 verify 안 한 게으른 벽(break-walls 위반)이라 정정."
}
]
},
Expand Down Expand Up @@ -495,4 +499,4 @@
]
}
]
}
}
42 changes: 42 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,48 @@ All notable changes to `gamebox` are documented in this file.

### Added

- feat(F-NSWINDOW-E5 r8): **CRT→user-entry 핸드오프 도달(합성) + BT/BTS/BTR/BTC
디코더 갭 종결 + 마지막 CRT import 2개 바인딩** — r7 의 벽(미등록 IAT 호출
@`0x539318`, insns=44)을 **넘어 CRT-init 의 종착점인 `call WinMain`(CRT→user-entry
핸드오프)에 도달**한다. **이것이 정직한 E4 완료 마일스톤이며, 렌더된 게임 프레임이
아니다 — `validated_manjeom` 은 여전히 0.** (1) `native/i386_cpu.{c,h}` 에 두 개의
kernel32 셰임: `GetCommandLineW()`(0-arg → **합성 in-image 커맨드라인 포인터**
`base+0x1FE0`), `SetUnhandledExceptionFilter(fn)`(1-arg → 이전 필터 NULL=0). IAT
슬롯 `0x53802C`/`0x538030` 으로 13개(count=13). (2) `native/i386_decode.{c,h}` +
byte-equal `.hexa` 미러: enum `I386_OP_BT_RM_R`(61,`0F A3`)/`_BTS`(62,`0F AB`)/
`_BTR`(63,`0F B3`)/`_BTC`(64,`0F BB`) + 0F 2-byte 분기(0F AF IMUL 과 동일한 `/r`
ModR/M 형태), op_name="bt"/"bts"/"btr"/"btc". 인터프리터: **CF ← bit(r/m, idx)**
(idx=ModR/M.reg 레지스터, mod 32 마스크), BTS/BTR/BTC 는 수정 비트 write-back —
평범한 Intel SDM Vol.2 비트 연산(보호 아님). RUNEQ corpus D(0F 전수)에서 C↔hexa
byte-equal 확인. (3) **핸드오프 검출**: `i386_cpu_t.user_entry_va` 필드 + halt
`I386_HALT_USER_ENTRY` 추가 — `call` 의 타깃이 `user_entry_va` 와 같으면 리턴 주소를
push 한 뒤(faithful "about to enter") 정직하게 정지. (4) hermetic 테스트
(`native/i386_cpu_test.c`) Run A 는 r7 체인(44) 뒤에 `GetCommandLineW` → `pop ecx`
(잔여 push 정리) → `mov esi,eax` → `SetUnhandledExceptionFilter` → `bt/bts/btr/btc`
→ WinMain 4-인자 stdcall 프레임 push(nShowCmd/lpCmdLine/hPrevInstance/hInstance) →
`call 0x539400`(합성 user entry) 를 이어 붙여 **insns 44→60, halt
`0x539318`(unbound)→`0x539400`(user_entry)**, bound=13, last=SetUnhandledException-
Filter 를 증명. WinMain 인자 프레임([esp+4..0x10] = hInstance·hPrevInstance·
lpCmdLine·nShowCmd) + entry CALL 리턴주소(`0x5388AB`) 무결성까지 검증. B/D
sentinel 은 `0F A3`(이제 BT 로 실행됨) → `0F B1`(CMPXCHG, 다음 진짜 갭, r9) 으로
교체. 측정 라인 `__SHIM__ PARTIAL phase=e4_reached_user_entry insns=60 bound=13
last=SetUnhandledExceptionFilter entry_va=0x539400 halt=user_entry halt_op=call
(SYNTHETIC user-entry; validated_manjeom=0)` + `__SHIM__ INFO
real_pe_path=structurally_ready needs:real_i386_PE+IAT_autobind_by_import_name+
wider_opcode_coverage+D3DMetal+display`, `__SHIM_TEST__ PASS`(CI 게이트, Run A 25
checks 전부 green, 로컬 clang `-Wall -Wextra -Wpedantic -std=c11` 청정). (5)
**실-바이너리 경로 점검**: `i386_cpu_load_pe`(argv[1]) 는 섹션 매핑·엔트리 설정·
이미지 할당까지 **구조적으로 배선됨** — 실제 i386 PE 를 주면 엔트리부터 실행 가능.
단, K32_IAT 슬롯이 hermetic 합성 VA(`0x538000..`)라 실제 PE 의 IAT 와는 매칭되지
않으므로, 실행이 첫 import 를 넘으려면 **PE import 테이블 파싱 + 이름 기반 자동
바인딩**이 필요(real-PE emit 가 INFO 로 명시). **own1**: 자기 import 를 네이티브
구현에 묶는 **로딩**(우회 아님), 합성 TEB/커맨드라인/user-entry VA 는 **정직 라벨**,
BT 류는 평범한 CPU 비트 연산 — Wine/보호 없음. **`validated_manjeom>0`(실 프레임)
까지 남은 거리**: real-asset 게이트(실 i386 게임 PE + D3DMetal SDK + 실 디스플레이 —
사용자만 제공 가능) + 남은 엔지니어링(실 PE IAT 이름 자동바인딩, 더 넓은 opcode
커버리지, 게임 자신의 WinMain → 메시지 루프 → CreateWindowEx → D3D→Metal 프레임).
현재는 **합성 user-entry 도달**까지이며 실 프레임은 자율 범위 밖(real-asset-gated).

- feat(F-NSWINDOW-E5 r7): **CRT import 3개 추가 바인딩 + IMUL/CPUID/RDTSC 디코더
갭 종결 + `__security_init_cookie` 산술 실행** — r6 의 벽(미등록 IAT 호출
@`0x5392D4`, insns=30)을 **넘는다**. (1) `native/i386_cpu.{c,h}` 에 세 개의
Expand Down
65 changes: 64 additions & 1 deletion native/i386_cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const char *i386_halt_name(i386_halt_t h) {
case I386_HALT_TRUNC: return "trunc";
case I386_HALT_GUARD: return "guard";
case I386_HALT_UNBOUND_IMPORT: return "unbound_import";
case I386_HALT_USER_ENTRY: return "user_entry";
default: return "?";
}
}
Expand Down Expand Up @@ -148,6 +149,26 @@ uint32_t i386_shim_GetProcAddress(i386_cpu_t *cpu, const struct i386_image *img)
return img->base + 0x1FF0u; // synthetic in-image stub addr
}

// GetCommandLineW() — WINAPI/stdcall, 0 args, returns LPWSTR. own1: the LOADER
// hands the program a SYNTHETIC in-image command-line pointer (its own argv
// blob). The CRT-startup `__scrt_common_main_seh` reads this before invoking the
// user entry. Not a bypass — this is the program's OWN command line.
uint32_t i386_shim_GetCommandLineW(i386_cpu_t *cpu, const struct i386_image *img) {
(void)cpu;
return img->base + 0x1FE0u; // synthetic in-image cmdline ptr
}

// SetUnhandledExceptionFilter(LPTOP_LEVEL_EXCEPTION_FILTER) — WINAPI/stdcall, 1
// ptr arg, returns the PREVIOUS top-level filter. own1: a CRT-startup OS-API
// call installing the program's OWN handler; we return NULL (none installed).
// An exception-handler registration, NOT a protection / anti-debug mechanism.
uint32_t i386_shim_SetUnhandledExceptionFilter(i386_cpu_t *cpu, const struct i386_image *img) {
uint32_t pfn = 0;
i386_mem_read32(img, cpu->gpr[I386_REG_ESP], &pfn); // arg (unused — no real SEH)
(void)pfn;
return 0; // previous filter == NULL
}

const i386_import_t *i386_iat_lookup(const i386_iat_t *iat, uint32_t slot_va) {
if (!iat || !iat->imports) return NULL;
for (uint32_t i = 0; i < iat->count; i++) {
Expand Down Expand Up @@ -427,8 +448,18 @@ void i386_cpu_run(i386_cpu_t *cpu, const i386_image_t *img,
if (!i386_mem_write32(img, cpu->gpr[I386_REG_ESP], ret)) {
res->halt = I386_HALT_OOB; res->halt_va = eip; return;
}
cpu->eip = eip + insn.len + (uint32_t)insn.imm; // same formula as the disassembler
uint32_t target = eip + insn.len + (uint32_t)insn.imm; // disassembler formula
cpu->eip = target;
res->insns++;
// CRT→user-entry handoff: this CALL transfers control to the
// user entry (main/WinMain). Faithful "about to enter" — the
// return address is already pushed. Honest E4 milestone, NOT a
// rendered frame. own1: we stop AT the handoff; we do not run
// the (absent) real game body.
if (cpu->user_entry_va && target == cpu->user_entry_va) {
res->halt = I386_HALT_USER_ENTRY; res->halt_va = target;
res->halt_op = insn.op; return;
}
continue; // branch — skip linear advance
}
case I386_OP_JMP_REL: { // E9 cd / EB cb
Expand Down Expand Up @@ -713,6 +744,38 @@ void i386_cpu_run(i386_cpu_t *cpu, const i386_image_t *img,
cpu->gpr[I386_REG_EDX] = (uint32_t)(cpu->tsc >> 32);
break;
}
// ── BT / BTS / BTR / BTC r/m32, r32 (E5 r8) — 0F A3/AB/B3/BB ─────
// CF ← bit(r/m, index). The bit index is the ModR/M.reg register.
// For a register-direct operand the index is masked mod 32 (SDM);
// for a memory operand we model the same 32-bit-window form (bounded
// — bit base + index/8 byte-stride not modeled). BTS/BTR/BTC write
// the modified value back. own1: plain Intel SDM Vol.2 bit ops.
case I386_OP_BT_RM_R: case I386_OP_BTS_RM_R:
case I386_OP_BTR_RM_R: case I386_OP_BTC_RM_R: {
i386_halt_t why = I386_HALT_UNSUPPORTED;
uint32_t v;
if (!rm_get32(cpu, img, &insn, &v, &why)) {
res->halt = why; res->halt_va = eip; res->halt_op = insn.op; return;
}
uint32_t idx = cpu->gpr[(insn.modrm >> 3) & 7] & 0x1Fu; // bit index
uint32_t bit = (v >> idx) & 1u;
set_flag(&cpu->eflags, EFL_CF, (int)bit);
uint32_t out_v = v;
int wb = 1;
switch (insn.op) {
case I386_OP_BT_RM_R: wb = 0; break;
case I386_OP_BTS_RM_R: out_v = v | (1u << idx); break;
case I386_OP_BTR_RM_R: out_v = v & ~(1u << idx); break;
case I386_OP_BTC_RM_R: out_v = v ^ (1u << idx); break;
default: break;
}
if (wb) {
if (!rm_set32(cpu, img, &insn, out_v, &why)) {
res->halt = why; res->halt_va = eip; res->halt_op = insn.op; return;
}
}
break;
}
// ── E4 kernel32 boundary — indirect IAT call / jump ───────────
case I386_OP_CALL_RM: // FF /2 [..]
case I386_OP_JMP_RM: { // FF /4 [..]
Expand Down
19 changes: 19 additions & 0 deletions native/i386_cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ struct i386_cpu {
// I386_TSC_STEP and writes the running 64-bit count to edx:eax. own1: a
// plausible synthetic counter, NOT the host TSC and NOT a protection clock.
uint64_t tsc;
// ── E5 r8 CRT→user-entry handoff marker (NOT a frame) ────────────────────
// When non-zero, a CALL (rel32 or bound import-thunk) whose computed target
// equals user_entry_va is the CRT→user-entry handoff: the interpreter pushes
// the return address (faithful "about to enter"), records it, and halts
// I386_HALT_USER_ENTRY. This is the honest E4 milestone (CRT init reached the
// `call main`/`call WinMain` site) — it is NOT a rendered game frame.
uint32_t user_entry_va;
};

// Per-rdtsc increment for the synthetic timestamp counter (plausible, fixed).
Expand Down Expand Up @@ -163,6 +170,15 @@ uint32_t i386_shim_GetStartupInfoW(i386_cpu_t *cpu, const struct i386_image *img
uint32_t i386_shim_GetSystemInfo(i386_cpu_t *cpu, const struct i386_image *img);
uint32_t i386_shim_GetProcAddress(i386_cpu_t *cpu, const struct i386_image *img);

// CRT→user-entry kernel32 shims (E5 r8). own1: native re-impls of the OS API
// surface bound to the program's OWN imports — loading, not a bypass.
// GetCommandLineW() — 0 args, returns a synthetic in-image
// command-line pointer (the loader hands the program its own argv blob).
// SetUnhandledExceptionFilter(fn) — 1 ptr arg, returns the previous filter
// (NULL — none installed). A CRT-startup OS-API call, not a protection.
uint32_t i386_shim_GetCommandLineW(i386_cpu_t *cpu, const struct i386_image *img);
uint32_t i386_shim_SetUnhandledExceptionFilter(i386_cpu_t *cpu, const struct i386_image *img);

// Look up an IAT slot VA in the registry. Returns NULL if `iat` is NULL or
// the slot is not bound (→ the caller halts UNBOUND_IMPORT honestly).
const i386_import_t *i386_iat_lookup(const i386_iat_t *iat, uint32_t slot_va);
Expand Down Expand Up @@ -191,6 +207,9 @@ typedef enum {
I386_HALT_UNBOUND_IMPORT, // indirect IAT call FF 15 [slot] to an UNregistered
// kernel32 import — the next import to bind (own1:
// honest stop, we never invent the function)
I386_HALT_USER_ENTRY, // CRT init reached the `call main`/`call WinMain` site
// (cpu->user_entry_va). The honest E4 milestone — the
// CRT→user-entry handoff. NOT a rendered game frame.
} i386_halt_t;

typedef struct {
Expand Down
Loading
Loading