Skip to content

Sato-Isolated/ReforgeIL

Repository files navigation

ReforgeIL

ReforgeIL is a IL2CPP analysis and managed-code recovery pipeline. It does not translate x64 instructions mechanically into CIL. It first reconstructs stable managed identities and signatures from IL2CPP metadata and runtime registrations, then treats native code as evidence of the effects that were executed.

0.16.0 status

The latest exhaustive Core.dll scan contains 16,221 fully proven CIL bodies and 90,810 unresolved concrete methods. Version 0.16.0 adds evidence-gated conditional moves, general bit-mask tests, register bit tests, scalar SSE arithmetic, exact float-to-double widening, unsigned shifts, and explicit loads for integer memory arithmetic. General recovery proves 2,566 CFG/SSA/Managed IR bodies across 123,643 bounded attempts in 80.31 seconds. The larger native tiers still add no complete bodies, so semantic coverage remains the measured bottleneck. Complete assembly emission remains blocked until the unresolved count reaches zero.

GameAssembly.dll                 global-metadata.dat
        |                                  |
        v                                  v
   PE32+ AMD64                  versioned metadata frontend
        |                                  |
        +---------------+------------------+
                        v
              immutable IL2CPP model
                        v
        CodeGenModules + MetadataRegistration
                        v
 image + RID + token + metadata index -> native address
                        v
 bounded native body + structured x64 disassembly
                        v
        CFG -> micro-IR -> ABI-typed SSA
                        v
 calls / fields / locals / conditions -> Managed IR
                        v
          strict CIL + symbolic token relocations

Names are labels, never identities. Unsupported instructions remain NativeOpaque. A shared native address is never assigned arbitrarily to one managed method.

Current implementation status

Implemented:

  • PE32+ AMD64 parsing, exports, sections, relocations, and .pdata runtime-function bounds;
  • Unity 6000.3 compact metadata v39 profile;
  • immutable images, types, fields, methods, parameters, properties, events, and generic metadata;
  • discovery of Il2CppCodeGenModule and Il2CppMetadataRegistration;
  • exact image + MethodDef RID -> native method pointer mapping;
  • reverse native-address index that preserves all shared-body aliases;
  • bounded native reading with policy caps, known-entry boundaries, and truncation diagnostics;
  • structured x64 operands and conservative micro-IR;
  • SSA for registers, flags, stack slots, constants, copies, and phi nodes;
  • conditional-edge splitting for phi assignments during CIL stackification;
  • Windows x64 ABI modeling for this, normal arguments, hidden RuntimeMethod*, hidden value-type return buffers, integer returns, floating-point returns, and generic-instance value/reference classification;
  • direct managed-call resolution, runtime export classification, and small thunk following;
  • register-indirect and static-slot-indirect call preservation;
  • automatic virtual-dispatch layout calibration without hard-coded Unity offsets;
  • compact-v39 vtable decoding and exact MethodDef dispatch resolution;
  • bounded parsing of Il2CppMethodSpec, Il2CppGenericInst, and generic method function definitions;
  • exact MethodRef linking to definitions and generic arguments;
  • ABI specialization when VAR or MVAR resolves to a concrete type;
  • metadata interface dispatch through interface_offsets, including MethodSpec slots;
  • global metadata usage decoding for TypeInfo, Il2CppType, MethodDef, FieldInfo, StringLiteral, and MethodRef;
  • native recognition of merged interface fast paths and slow paths;
  • exact recovery of receiver, declaring interface, declared slot, and VirtualInvokeData { methodPtr, method };
  • dispatch-helper absorption and one managed callvirt operation per proven interface call;
  • explicit MethodSpec CIL relocations, separate from MethodDef and MemberRef;
  • terminal external jumps represented as native tail calls;
  • inherited field-offset recovery from runtime registrations, including unboxed value-type offset normalization;
  • Managed IR for arguments, locals, fields, arithmetic, comparisons, calls, branches, and returns;
  • strict CIL emission for proven operations;
  • batch Managed IR recovery with one shared workspace and an optional in-process LLVM C disassembler;
  • graduated native proof tiers that defer only exact size-policy failures and never reanalyze definitive semantic failures;
  • fail-fast opaque-native rejection and a deterministic SSA work budget for exhaustive scans;
  • exact pruning of unreachable trap/padding blocks that have no predecessors;
  • explicit memory loads for comparisons and narrow integer conversions, preserving value-versus-address semantics;
  • evidence-gated setcc, integer comparisons, signed 32-to-64 conversion, and typed 8/16-to-32 extension;
  • exact register cmovcc lowering through CFG diamonds and SSA phi nodes;
  • general test masks, register bt, logical shr.un, and exact unary integer operations;
  • scalar SSE arithmetic, ordered floating comparisons, and exact float32 to float64 widening;
  • explicit SSA loads for integer memory arithmetic and exact three-operand imul;
  • 32-bit affine lea lowering and exact instance-field recovery through typed managed arguments;
  • opaque-native mnemonic telemetry with method counts and representative evidence;
  • compiler-frame spill/restore filtering for proven Windows x64 nonvolatile-register artifacts;
  • structural narrowing of identical-code-folded base-constructor calls through exact metadata inheritance;
  • linked ECMA-335 StandAloneSig local-variable signatures and fat bodies with initlocals;
  • symbolic cross-assembly field and method MemberRef relocations plus local/external type-token relocation;
  • versioned JSON method reports;
  • an IL-only PE/CLI reference-assembly writer;
  • fast native pattern recovery for empty methods, wrapper chains, direct field accessors, metadata-initialized field chains, null-guarded field comparisons, compound field initializers, blittable value-type block copies, type-token returns, constant returns, and direct argument returns;
  • direct injection of proven tiny and fat CIL bodies into generated assemblies;
  • conservative generic-instance field-layout synthesis and exact external-field MemberRef linking;
  • strict complete-export refusal when any concrete body remains unresolved;
  • per-method JSONL body manifests with separate source and output identities;
  • independent manifest-to-assembly verification for every emitted MethodDef;
  • reconstruction of types, inheritance, interfaces, fields, methods, parameters, properties, events, generic parameters, and generic constraints;
  • structural verification of PE/CLI headers, streams, tables, heaps, row lists, signatures, and method bodies;
  • fast method search by image and name.

Validated Core.dll snapshot:

  • 107,031 concrete methods analyzed;
  • 16,221 proven CIL bodies injected in explicit reference mode;
  • 2,566 bodies recovered by the general CFG/SSA/Managed IR path;
  • 90,810 unresolved concrete methods represented by visible throwing stubs only in reference mode;
  • 3,684 abstract methods emitted without bodies;
  • 15.16% proven-body coverage among concrete methods;
  • 123,643 bounded general-recovery attempts, including 30,267 size-tier retries;
  • 217 distinct linked local-variable signatures;
  • complete mode refuses to emit a DLL because coverage is not yet 100%;
  • zero signature fallbacks and zero silent body substitutions.

Project organization in this release:

  • domain-owned public APIs under include/reforge/<module> and matching implementations under src/<module>;
  • a five-line executable entry point backed by Application, CommandArguments, command groups, formatters, and output services;
  • recovery-only native matchers isolated in src/recovery/detail;
  • test suites split by metadata, native, managed, recovery, and assembly domains;
  • documented dependency boundaries in docs/PROJECT_STRUCTURE.md.

Still in progress:

  • assigning shared native bodies to exact generic instantiations;
  • generic virtual and generic interface invokers;
  • RGCTX helper recovery;
  • general memory SSA and alias analysis;
  • structured exception regions and exception-object recovery;
  • MethodSpec and TypeSpec table emission for generic general calls;
  • broader multiblock memory semantics, exception regions, and elimination of every unresolved concrete body before 1.0.

Build

Requirements: CMake 3.20+, a C++20 compiler, and either a loadable LLVM shared library or llvm-objdump for native method analysis. On Windows, the in-process decoder searches the explicit --llvm-library path first, then C:\Program Files\LLVM\bin\LLVM-C.dll, and finally LLVM-C.dll through the system loader.

Linux or macOS:

cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build
ctest --test-dir build --output-on-failure

Windows with Visual Studio:

cmake -S . -B build -G "Visual Studio 17 2022" -A x64
cmake --build build --config Release
ctest --test-dir build -C Release --output-on-failure

Fast iteration preset (no tests, compiler cache auto-detected):

cmake --preset dev
cmake --build --preset dev --target reforge --config Release

On Windows (Visual Studio):

cmake --preset windows-dev
cmake --build --preset windows-dev --target reforge --config Release

Release and Debug presets are provided in CMakePresets.json.

Commands

Inspect the complete input pair:

reforge inspect --assembly GameAssembly.dll --metadata global-metadata.dat --out report.json

Fast recovery pass for large Dlls (iterative profile):

reforge emit-assembly --assembly C:\Users\...\GameAssembly.dll --metadata C:\Users\...\global-metadata.dat --image Core.dll --output Core.dll --body-mode reference --general-recovery-limit all --general-recovery-profile fast --general-byte-tiers 256,512,1024,4096 --general-parallel-methods 8 --timing-summary true --report fast-profile.json

Recovery-only (no rebuilt PE output, same binary), useful for iterative timing runs:

reforge emit-assembly --assembly C:\Users\...\GameAssembly.dll --metadata C:\Users\...\global-metadata.dat --image Core.dll --output Core.dll --body-mode reference --general-recovery-limit all --general-recovery-profile fast --general-byte-tiers 256,512,1024,4096 --general-parallel-methods 8 --timing-summary true --general-recovery-only true --report fast-profile.json

List metadata images:

reforge images --metadata global-metadata.dat --out images.json

Search methods without disassembling them:

reforge methods --metadata global-metadata.dat --image Core.dll --name MoveNext --limit 50 --out methods.json

Calibrate native dispatch layout:

reforge layout --assembly GameAssembly.dll --metadata global-metadata.dat --out layout.json

Inspect a generic instantiation and specialized ABI:

reforge method-spec --assembly GameAssembly.dll --metadata global-metadata.dat --index 8395 --out method-spec.json

Collect generic-linking statistics:

reforge generic-stats --assembly GameAssembly.dll --metadata global-metadata.dat --out generic-stats.json

Resolve a virtual or interface metadata slot:

reforge dispatch-slot \
  --assembly GameAssembly.dll \
  --metadata global-metadata.dat \
  --type-index 10 \
  --interface-type-index 90177 \
  --slot 0 \
  --out dispatch.json

Audit all interface offset tables:

reforge interface-stats --assembly GameAssembly.dll --metadata global-metadata.dat --out interface-stats.json

Decode a global metadata slot referenced by native code:

reforge metadata-usage \
  --assembly GameAssembly.dll \
  --metadata global-metadata.dat \
  --slot-va 0x18626c750 \
  --out metadata-usage.json

Enable interface-pattern trace output without changing JSON reports:

REFORGE_TRACE_INTERFACE=1 reforge method ...

Analyze one method:

reforge method \
  --assembly GameAssembly.dll \
  --metadata global-metadata.dat \
  --image mscorlib.dll \
  --rid 2199 \
  --max-native-bytes 262144 \
  --fallback-bytes 256 \
  --out method.json

--max-native-bytes prevents corrupt or unusual bounds from triggering huge disassemblies. Reports set truncated_by_policy=true when the policy cap is applied.

Generate a managed reference assembly:

reforge emit-assembly \
  --assembly GameAssembly.dll \
  --metadata global-metadata.dat \
  --image Core.dll \
  --output Core.dll \
  --body-mode reference \
  --general-recovery-limit all \
  --general-recovery-profile fast \
  --general-byte-tiers 256,512,1024 \
  --general-max-ssa-work 5000000 \
  --body-manifest Core.bodies.jsonl \
  --report Core.report.json

Verify its PE/CLI and ECMA-335 structure:

reforge verify-assembly --assembly Core.dll --out Core.verification.json

Body modes are explicit:

  • complete is the default and refuses to leave an output DLL while any concrete method remains unresolved;
  • reference injects proven bodies and uses explicit ldnull; throw stubs for unresolved concrete methods;
  • stubs emits a metadata-focused reference assembly without attempting body recovery.

General recovery is opt-in with --general-recovery-limit N|all. By default it uses the graduated native byte tiers 256,512,1024,4096 and an early/final SSA budget that is capped at 5,000,000 units except for the explicit --general-recovery-profile complete.

For faster recovery, add --general-recovery-profile fast, which defaults to 256,512,1024, caps SSA work at 2,500,000, and keeps explicit tuning options like --general-max-ssa-work when provided.

A DLL produced in reference mode is a partial reference assembly, not a complete behavioral reconstruction. The body manifest identifies every output MethodDef as recovered, stub, or abstract, records the native evidence for recovered methods, and attaches a stable terminal failure category and diagnostic to every attempted method that remains unresolved.

Recovery status rules

For managed_cil reports:

  • status=recovered means every traversed Managed IR operation has CIL emission;
  • requires_linking=true means one or more symbolic tokens must be materialized in output metadata;
  • verifiable=true requires complete linking, known local types, and convergent stack analysis;
  • partial or unsupported is preferred over a plausible but fabricated body.

Safety and scope

The frontend rejects unknown metadata profiles. GameAssembly.dll is never loaded or executed; it is treated only as bytes. Native disassembly always uses bounded address ranges.

Use ReforgeIL only on binaries you are authorized to analyze.

Documentation

About

ReforgeIL is a IL2CPP analysis and managed-code recovery pipeline

Topics

Resources

License

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors