SlimePE-rev — Windows PE32+ x86_64 binary → ASM + C 逆変換
vendor-lost な Windows .exe から NASM と C のソースを bit-exact 復元。
Microsoft レガシー・産業制御・vendor-lost .exe / .dll の source-lost な Windows x86_64 PE32+ binary を、
NASM intel ソースと C ソースの両方に bit-exact 逆変換。NASM 出力は nasm -fwin64 + mingw-link で再アセンブル、
C 出力は x86_64-w64-mingw32-gcc -nostdlib -e _start でコンパイル—両方とも実 .exe を再生成し、
WSL2 binfmt 実行で原 binary と stdout が byte 完全一致。
- Phase A (SlimeELF-rev と共有): x86_64 命令 decoder (~37 opcode、整数 hot-loop subset) + NASM intel emitter + ストレートライン C emitter
- Phase B entry: CFG 復元 (Cooper-Harvey-Kennedy 反復 dominator + Aho/Sethi natural loop body) + 構造化 C (
do { ... } while (R[1] != 0);+if/elsediamond) - Phase B (b): 関数境界復元 (prologue/epilogue + call/ret + 自己再帰)
- Phase B (d): inter-procedural Slot IR — SlimeELF-rev と統一 schema、deterministic JSON round-trip
- Phase D — PE32+ 固有層: PECOFF v8.3 parser + DLL import 解決 (IAT slot → function 名) + Win64 ABI (rcx / rdx / r8 / r9 + shadow space
[rsp+0x20]) + WinAPI recipe table (kernel32 + msvcrt 9 関数) - S9 bench: PE 8 軸 168/168 + Phase E lift v2 21/21 = 189/189 PASS (2026-05-19)、21 sample (msvcrt + kernel32 統合、
printf/GetModuleHandleA/CreateFileA/CloseHandle含む) — libc-linked PE binary、MinGW local-thunk 経由 IAT call も ASM + C round-trip で green - Phase E lift v2: 関数ごとの stack-slot promotion + Win64 arg spill 復元 (
[rbp+0x10]、[rbp+0x18]) — 21/21 lift 後 C .exe が原 stdout と byte 完全一致
「vendor-lost な Windows .exe」を 決定論的変換 + 8 軸 round-trip 自動回帰 + 監査チェーンで逆変換する製品。
Linux ELF 側は姉妹製品 SlimeELF-rev (同じ Slot IR / 共有 decoder)、forward 側は SlimeASM (HLASM + MASM)。
Phase E v3 (loop / natural-cond 復元) と Phase F PSDP (auto OpenMP 並列化) は ELF 側で先行動作、PE32+ 拡張は roadmap。命令 decoder は共有のため、MinGW gcc -O0 の CFG pattern を吸収すれば移植可能。
主要計測値 (2026-05-19)
= msvcrt + kernel32 統合、printf / CreateFileA / lstrlenA 含む
= 真の local + Win64 arg spill 復元
WriteFile / GetStdHandle / ExitProcess / lstrlenA / GetModuleHandleA / CreateFileA / CloseHandle / puts / printf
SlimeELF-rev と共有、call graph が一級辺
SIB / movzx / movsx / cdqe / movsxd / shl-shr-sar / and-or — SlimeELF-rev と共有
kernel32 + msvcrt を 1 binary でインポート (sample 19 lstrlenA + puts)
市場文脈 — source-lost な Windows binary はどこに存在するか
| vendor-lost .exe | vendor 廃業や保守拒否で .exe / .dll のみ残る案件。SBOM、CVE パッチ、監査のために source 復元が要求される。 |
|---|---|
| Microsoft レガシー | 長期稼働 Windows ベース業務アプリで source repo 消失。C source 復元はクリーンルーム再実装の前提。 |
| 産業制御 / OT | 10-30 年 frozen な Windows ベース PLC HMI、計装 daemon、工場 MES アダプタ。 |
| コンプライアンス文書 | FDA / PMDA / IEC 62443 OT セキュリティが「完全なソフトウェア記述」を要求、binary しかない場合でも 監査再現可能な文書化として C 化必須。 |
| 競合関係 | Ghidra / IDA Pro / Hex-Rays / RetDec が既存。SlimePE-rev の差別化は 3 点: (1) 決定論 + 8 軸 round-trip 自動回帰で「lossless」を bench で証明、(2) SlimeELF-rev と統一された Slot IRで OS 横串監査 pipeline 可能、(3) 逆変換出力が mingw-gcc で実 .exe として build され、原 binary と stdout 一致—Hex-Rays は static C を出すが build + run round-trip 保証なし。 |
S9 bench — 全 8 軸: PE 168/168 PASS (bit レベル)
ELF と同じ 8 軸 S9 bench harness を PE32+ に適用し bit レベル検証。decoder (~37 opcode) と Slot IR schema は SlimeELF-rev と共有、container 層 (PE parser、IAT 解決、Win64 ABI) のみ PE 固有。
| 軸 1a PE dialect-detect | DOS magic “MZ” + PE 署名 “PE\0\0” + COFF Machine=0x8664 (IMAGE_FILE_MACHINE_AMD64) + Optional Header Magic=0x20B (PE32+) + Subsystem=3 (CONSOLE) を bit レベル検証。21/21 PASS。 |
|---|---|
| 軸 1b opcode-recover | .text (VirtualSize 制限内の live 領域) 内の全命令を decode — db 0xNN fallback 件数 = 0。21/21 PASS。 |
| 軸 2 mutation-detect | .text の 1-bit flip を 5 試行 × 21 sample = 105 試行、105/105 検出。 |
| 軸 3 determinism | 同じ .exe を 2 回 disasm → 21 sample 全 byte 一致。21/21 PASS。 |
| 軸 4 ASM round-trip | emit NASM → nasm -fwin64 → mingw-link (-lmsvcrt -lkernel32) → 実 .exe → WSL2 binfmt 実行 → 原 stdout 一致。IAT 参照は extern __imp_FUNC で出力するため linker が PE import table を再生成。21/21 PASS。 |
| 軸 5 C round-trip | emit C → mingw-gcc -nostdlib -e _start → 実 .exe → 原 stdout 一致。IAT-indirect call (mov reg, qword [rip+iat]; call reg) を peephole で call_iat <FUNC> に折り畳み、Win64 ABI 経由 (rcx/rdx/r8/r9 / [rsp+0x20]) で kernel32 / msvcrt 直接呼び出しに展開。21/21 PASS。 |
| 軸 6 structured-C round-trip | CFG 復元の構造化 C (do-while + if/else + 関数別) → mingw → run → 一致。21/21 PASS、recursive fact(4) = 24 含む。 |
| 軸 7 Slot IR round-trip | PE32+ binary を ELF と 同じ Slot IR schemaに lift。SlotFunction、call graph edge、構造同型すべて round-trip。21/21 PASS、06_call_simple (2fn/1call) / 07_two_funcs (3fn/2call) / 08_recursion (2fn/2call、self-loop) が Linux 版と同一の call graph を再構築。 |
PE32+ 固有層 (Phase D)
- PECOFF v8.3 parserDOS header (64B、magic MZ + e_lfanew @ 0x3C) → PE 署名 (4B) → COFF header (20B、Machine=0x8664 必須) → Optional Header (PE32+ 240B、Magic=0x20B 必須) → Section Table を bit レベル parse。VirtualSize で live .text 領域を bound。
- DLL import 解決DataDirectory[1] Import Table を walk:
_IMAGE_IMPORT_DESCRIPTOR配列 + INT/IAT thunk 配列 (8B、ordinal flag = bit 63) + Hint/Name table。各 IAT slot VA を DLL!function に解決。例:0x140003070 → msvcrt.dll!puts。 - IAT-call peepholegcc -O0 + MinGW が出力する 2 命令
mov reg, qword [rip+iat]; call regを 1 つの仮想call_iat <FUNC>に折り畳み、Win64 ABI 経由で直接 C call にする。 - Win64 ABI recipe引数 slot: rcx (R[1]) / rdx (R[2]) / r8 (R[8]) / r9 (R[9]) / 第 5 引数は
mem_r(R[4]+0x20, 8)。文字列引数 (LPCSTR) は VA を mem_r で 1 byte ずつ読んでローカル buffer に再構築してから kernel に渡す。 - WinAPI recipe table (拡張可能)現状 9 関数 — kernel32: GetStdHandle / WriteFile / ExitProcess / lstrlenA / GetModuleHandleA / CreateFileA / CloseHandle、msvcrt: puts / printf (1-arg form)。新 API 追加は 3 ステップ (PROLOGUE dllimport 宣言 + recipe table 追加 + link command
-l<dll>) のみ、decoder / CFG / Slot IR 層は不変。 - trailing thunk trim (call target 保持).text 末尾の
jmp qword [rip+iat]thunk テーブルは SlotImage から除外 (偽関数防止)。ただしcall rel32から到達可能な local thunk は保持 (MinGWprintf → puts最適化は local<puts>thunk 経由のため、削ると call target が undef になる)。
Phase E lift v2 — 真の local + Win64 arg spill 復元 (21/21)
Phase D の VM 形式 C 出力 (R[] + STACK[] + mem_r/mem_w dispatcher) を構造化 C emit に変換し、関数ごとに stack-slot promotion を適用 (rbp ± offset のメモリアクセスを名前付き C local に lift)。C のスコープ規則で関数間 frame collision は自動解消。Win64 ABI arg spill ([rbp+0x10]、[rbp+0x18] 等) も local として復元、natural C に一歩近づく。
21 PE sample 全てで lift 後 C 出力を mingw-gcc で再ビルド、WSL2 binfmt 実行で原 .exe と stdout が byte 完全一致を確認。
Sample inventory (21 PE32+ .exe binary)
Linux ELF 17 sample subset と機能等価な C source + libc-linked Windows 固有 4 (msvcrt!puts / lstrlenA + puts / multi-WinAPI / msvcrt!printf) を x86_64-w64-mingw32-gcc -nostdlib -e _start で PE32+ .exe にビルドし、同じ 8 軸 bench に通したもの。IAT-indirect call (mov rax, qword [rip+iat]; call rax) を peephole で call_iat <FUNC> に折り畳み、Win64 ABI 経由で kernel32 / msvcrt 直接呼び出しとして C 復元。stdout は WSL2 binfmt 直接実行で検証。
| 01 hello (PE) | GetStdHandle + WriteFile + ExitProcess → Hello, PE!。最小 IAT 3 関数の Win64 ABI 呼び出し。 |
|---|---|
| 02 arith (PE) | 17+25 = 42 を 2 桁 ASCII で WriteFile → SUM=42。cqo + idiv rsi で真の符号付き除算。 |
| 03 loop (PE) | For-loop で 1+2+3+4+5 = 15 → SUM=15。cmp DWORD PTR + jle + WriteFile。 |
| 04 branch (PE) | if (x >= 5)、x=7 → big。両 arm で別文字列を WriteFile、共通 join で合流。 |
| 05 compute (PE) | 6 × 7 = 42 → PROD=42。2-op imul rax, rbx + idiv。 |
| 06 call_simple (PE) | _start → do_print(handle)。Win64 rcx 引数渡し + nested WriteFile。 |
| 07 two_funcs (PE) | add_two() + print_dec(handle, val)。SlotImage に 3 関数ノード + 2 inter-procedural edge。 |
| 08 recursion (PE) | factorial(4) = 24 自己再帰 → FACT=24。call graph に fact → fact self-loop、callee-saved 状態を Win64 shadow space [rsp+0x20] 経由で保持。 |
| 09 array_sum (PE) | arr[5] = {3,5,7,9,11} 合計 → SUM=35。SIB byte mov rax, [rax*8+0x140002000]。 |
| 10 strlen (PE) | 手書き strlen で "Hello, World!\n" → LEN=14。movzx eax, BYTE PTR [rax] + test al, al。 |
| 11 signed_array (PE) | 符号付き char 配列 {-3, 5, -8, 12, 4} 合計 → SUM=10。movsx rax, al + 符号付き演算。 |
| 12 int_index (PE) | int i 配列ループ + 3 桁出力 → SUM=150。cdqe + 32-bit op variant + dual divmod (100 / 10)。 |
| 13 bitshift (PE) | 32 << 3 = 256、>> 1 = 128 → VAL=128。shl/sar r32。 |
| 14 bitmask (PE) | 0xFF12 & 0xFF = 18 → RES=18。and r/m64, imm8。 |
| 15 stride (PE) | ストライド arr[i*3] → STR=35。add + add 展開 + SIB ロード。 |
| 16 matrix (PE) | 3×4 行列ネスト合計 → MAT=78。2D 配列 + ネスト CFG、lea rdx, [rax*4+0x0] (SIB lea) で row offset。 |
| 17 struct (PE) | struct 配列 pts[3] = {{10,20},{30,40},{50,60}} field 合計 → PT=210。SIB + displacement。 |
| 18 msvcrt_puts (PE) | 最初の libc-linked sample。msvcrt!puts("Hello, msvcrt!") + kernel32!ExitProcess → Hello, msvcrt!。kernel32 以外の DLL import への拡張性実証。 |
| 19 lstrlenA (PE) | kernel32!lstrlenA("Hello, libc!") で長さ 12 を計測、msvcrt!puts で 2 桁出力 → LEN=12。1 binary から kernel32 + msvcrt 2 つの DLL を import。 |
| 20 winapi_multi (PE) | multi-WinAPI 統合 — GetModuleHandleA + CreateFileA + CloseHandle + WriteFile。WinAPI recipe table を本格的に使用、ファイルハンドルライフサイクルも逆変換で保持。 |
| 21 printf (PE) | msvcrt!printf("Hello, %s!\n", "printf") 1-arg form — local thunk 経由 call を検証 (MinGW は printf → puts を local thunk 経由で最適化、trim で target を保持する必要あり)。 |
関数 = Slot ノード、call graph を IR の一級辺 (Phase B (d))
各関数を SlotFunction ノードとして、call edge は IR の一級。自己再帰は self-loop edge。完全な SlotImage は deterministic JSON で encode/decode (軸 7 round-trip)、call graph と関数構造を 外部ツールチェーン (監査 DB、SBOM、静的解析) へ情報損失なく流せる。Slot IR schema は SlimeELF-rev と統一、OS 横串監査 pipeline 可能。
監査適合性 (金融・防衛・医療機器・OT)
- bit-exact同じ PE 入力 → 同じ sha256 NASM/C 出力。CFG / 関数境界 / 命令ストリーム全て完全決定論的。
- native .exe round-trip出力 NASM を nasm-fwin64 + mingw-link で再アセンブル、出力 C を mingw-gcc -nostdlib で compile、2 つの実 .exe を WSL2 binfmt 実行して原 binary と stdout 比較。シミュレーションでなく実機検証。
- 改ざん検出.text の 1-bit flip で必ず disasm が変わる。21 × 5 = 105/105 検出。
- determinism同じ .exe を 2 回 disasm + emit で byte 一致。並列・GPU 実行下でも安定。
- Slot IR 監査関数 = Slot ノード + call graph を deterministic JSON で永続化。SBOM / 監査 DB pipeline に構造化資産として接続。
- build-time LLMLLM は decoder rule 構築時のみ。runtime は決定論的 rule-based—金融・防衛・OT の監査要件と整合。
サポート命令 (SlimeELF-rev と共有、~37 pattern)
| データ移動 | mov reg/mem, imm/reg (B8+r / 89 /r / 8B /r / C7 /0 / 88 /r、64-bit + 32-bit) / movzx / movsx / lea r64, m (8D /r) / nop / leave |
|---|---|
| 算術 | add / sub r/m, r / imul r, r/m (REX.W 0F AF) / idiv r/m (F7 /7) / cqo / cdqe / cdq / movsxd r64, r/m32 |
| 論理 | and / or / xor r/m64, r64 / xor reg, reg idiom = zero-init |
| bit shift | shl / shr / sar r/m, imm8 (C1 /N) / shl / shr / sar r/m, 1 (D1 /N) |
| compare / test | cmp r/m64, r64 / cmp r/m, imm8 / test r/m, r |
| 分岐 | Jcc rel8/32 / jmp rel8/32 / loop rel8 (E2) |
| call / stack | call rel32 (E8) / ret (C3) / push/pop r64 / push imm |
| system (PE) | IAT-indirect call (mov rax, qword [rip+iat]; call rax) を peephole で call_iat <FUNC> に折り畳み、Win64 ABI 経由 (rcx / rdx / r8 / r9 + [rsp+0x20]) で直接 WinAPI call に展開。(ELF は syscall (0F 05) heuristic。SlimeELF-rev 参照。) |
| memory operand | [reg] / [reg+disp8/32] / [rip+disp32] / [base+index*scale+disp] (SIB byte、scale=1/2/4/8) — [rbp-disp] local 変数 + [rax*8+disp] 配列 indexing + Win64 shadow space [rsp+0x20] 完全カバー。 |
次フェーズ拡張: printf-N / malloc / SSE2 / SSE4 (XMM + 浮動小数点)、3-op imul r64, r/m64, imm32、movabs r64, imm64、加えて ELF 側で動作中の Phase E v3 (loop / natural-cond 復元) と Phase F PSDP (auto OpenMP) の PE 移植。
License モデル
| 課金対象 | WASM/WASI converter ツール (開発者側) |
|---|---|
| 非課金 | 生成された NASM / C source (顧客資産、永久デプロイ) |
| 方式 | Ed25519 144B 署名 license + 3-hop air-gap activation (金融・防衛・OT 監査対応) |
| 並列化 (PSDP) | 本製品に含まれない。SlimeNENC 内の独立 SKU PSDP 参照。 |
関連資料
- 姉妹 (Linux 逆変換)SlimeELF-rev — Linux ELF x86_64 逆変換、同じ Slot IR + 共有 decoder。
- 姉妹 (forward)SlimeASM — HLASM + Win x64 MASM forward 変換。
- 逆変換ファミリー overviewSlimeASM-rev landing — 逆変換ファミリー総合ページ。
- Slot IR 共有ファミリーSlimeCOBOL / SlimePL/I / SlimeRPG / SlimeMUMPS が同じ Slot IR を共有。
- 特許出願特願 2026-046620 v15b、請求項 11 / 14d。
