Ghidra Re
OrbitCurve/firmware-reverse-engineering
Expert-level Ghidra reverse engineering for firmware binaries with emphasis on stripped binary analysis, automated function discovery, cryptographic routine identification, authentication logic…
Builds TH08 functions with the repository's VC7 toolchain and compares each against the hash-attested 1.00d binary to tune code generation and verify exact matches.
$ npx skills add N0zoM1z0/th08 --skill th08-matching -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install N0zoM1z0/th08 th08-matching --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/N0zoM1z0/th08.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/th08-matching .claude/skills/th08-matching && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "th08-matching" agent skill from https://github.com/N0zoM1z0/th08/tree/main/.agents/skills/th08-matching into .claude/skills/th08-matching/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "th08-matching", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/N0zoM1z0/th08/tree/main/.agents/skills/th08-matchingType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add N0zoM1z0/th08 --skill th08-matching -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install N0zoM1z0/th08 th08-matching --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/N0zoM1z0/th08.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.agents/skills/th08-matching .agents/skills/th08-matching && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "th08-matching" agent skill from https://github.com/N0zoM1z0/th08/tree/main/.agents/skills/th08-matching into .agents/skills/th08-matching/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "th08-matching", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add N0zoM1z0/th08 --skill th08-matching -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install N0zoM1z0/th08 th08-matching --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/N0zoM1z0/th08.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.agents/skills/th08-matching .cursor/skills/th08-matching && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "th08-matching" agent skill from https://github.com/N0zoM1z0/th08/tree/main/.agents/skills/th08-matching into .cursor/skills/th08-matching/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "th08-matching", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/N0zoM1z0/th08.git --path .agents/skills/th08-matching--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add N0zoM1z0/th08 --skill th08-matching -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install N0zoM1z0/th08 th08-matching --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/N0zoM1z0/th08.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.agents/skills/th08-matching .gemini/skills/th08-matching && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "th08-matching" agent skill from https://github.com/N0zoM1z0/th08/tree/main/.agents/skills/th08-matching into .gemini/skills/th08-matching/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "th08-matching", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install N0zoM1z0/th08 th08-matchingInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add N0zoM1z0/th08 --skill th08-matching -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/N0zoM1z0/th08.git skills-src && mkdir -p .github/skills && cp -r skills-src/.agents/skills/th08-matching .github/skills/th08-matching && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "th08-matching" agent skill from https://github.com/N0zoM1z0/th08/tree/main/.agents/skills/th08-matching into .github/skills/th08-matching/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "th08-matching", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add N0zoM1z0/th08 --skill th08-matching -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install N0zoM1z0/th08 th08-matching --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/N0zoM1z0/th08.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.agents/skills/th08-matching .opencode/skills/th08-matching && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "th08-matching" agent skill from https://github.com/N0zoM1z0/th08/tree/main/.agents/skills/th08-matching into .opencode/skills/th08-matching/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "th08-matching", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
th08-matchingBuilds TH08 functions with the repository's VC7 toolchain and compares each against the hash-attested 1.00d binary to tune code generation and verify exact matches.
Part of a source reconstruction of Touhou 8, Imperishable Night 1.00d, used alongside the th08-re skill once the target boundary, semantics and ABI are backed by evidence. The loop verifies the target and lists configured units, reads the unit's entry in config/match-units.toml, builds the object with scripts/build.py, and compares with scripts/compare-function.py, which reports exact, mismatch or error.
A mismatch is classified before any edit: function boundary, ABI or layout, compiler flags, decorated symbol, REL32 or DIR32 mapping, control-flow order or translation-unit effect. Then one source-plausible change is made, the same object is rebuilt and the same unit is rerun. A read-only discovery script, propose-exact-units.py, offers review-only candidates, and a Ghidra export script can produce original delinked objects when Ghidra is configured.
6 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit 67e7e49. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
python3gitcursorFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use git, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
TH08 Exact Function Matching loads about 8.7k tokens when it runs. Until then it costs about 74 tokens; SKILL.md has 4,699 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from N0zoM1z0/th08 at commit 67e7e49, republished under its MIT licence (© N0zoM1z0). 4,699 words, ~8,726 tokens.
.claude/skills/th08-matching/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.Use with $th08-re only after the target boundary, semantics, and ABI are
supported by evidence.
Verify the target and list configured units:
python3 scripts/verify-target.py
python3 scripts/compare-function.py --listRead the selected [[units]] entry in config/match-units.toml. Confirm
its target address/size, decorated COFF symbol, object path, and every
relocation against config/mapping.csv, config/reccmp-*.csv, and target
disassembly.
If an original delinked object is required and Ghidra is configured,
generate it through the verified disposable import:
python3 scripts/export_ghidra_objs.py --import-csvBuild the entry's object basename. For an object recorded as
build/ScreenEffect.obj, run:
python3 scripts/build.py --build-type=objdiffbuild --object-name ScreenEffect.objIf VC7 emits the right implicit constructor bytes but omits a COFF function- definition auxiliary record, declare and define the natural empty constructor explicitly, rebuild, and compare again. Do not synthesize behavior; member construction must remain compiler-driven.
Compare the configured function:
python3 scripts/compare-function.py UNIT --jsonTreat exact, mismatch, and error literally. Classify a mismatch before
editing source: function boundary, ABI/layout, compiler flags, decorated
symbol, REL32/DIR32 mapping, source/control-flow order, or translation-unit
effect.
Change one source-plausible expression or declaration, rebuild the same object, and rerun the same unit.
For a read-only search of already built, source-emitted leaves, use the conservative discovery gate:
nice -n 15 python3 scripts/analysis/propose-exact-units.py --min-size 0x80It emits review-only candidates only when the target-pinned isolated extent,
full relocation replay, direct-call multiset, named destination ledgers, and
unique object ownership agree. --allow-unimplemented may reveal source-built
functions missing from implemented.csv; it never updates tracking. Inspect
the source and target, add a reviewed unit, and obtain this skill's canonical
compare-function.py exact result before entering either ledger.
For a candidate-proposal batch, reject same-address multi-owner candidates and run the
canonical comparator once for every remaining unit before adding any
implemented.csv or matches.csv rows. Aggregate replay evidence is a
prioritization result, not a substitute for per-unit acceptance.
The proposer includes ordinary and build/probes/ VC7 objects. Use
--allow-unnamed-mapping only to diagnose a one-to-one exact body whose
target row is still FUN_*; it is review-only. Reconcile the target name and
ABI in the ledgers before creating a canonical unit, rather than accepting a
placeholder name.
If a fully replayable leaf is blocked only by a source-emitted static global, decode that relocation against the target and add its canonical global-ledger row before acceptance. Then add the function mapping/implementation record, write the unit's explicit relocation, and require the canonical comparator to replay it. Do not treat a private static name or a successful object build as an implicit target address.
For a VC7 switch function whose COFF auxiliary total_size includes compiler-
owned jump tables, keep size as the authored code coverage and set
compare_size to the complete code-plus-table extent. Prove the extent ends at
a target boundary, list every code and table relocation, and require the whole
compare_size range to match. Never count table bytes as authored progress.
For a nonmatching dispatcher, diagnose size movement with a read-only span
crosswalk: map each target table slot and each COFF table DIR32 relocation to
handler starts, sort unique physical starts, and compare neighboring spans.
This exposes the responsible handler even when slots share a default body. It
is only a source-shaping aid; it does not establish a boundary or an exact
match without the canonical full-range comparison.
EclManager::RunEcl is already exact. Only when reproducing its historical
evidence or verifying a dependency change, use:
python3 scripts/analysis/historical/runecl-crosswalk-dispatch.py --object build/probes/EclRun.obj --top 20
python3 scripts/analysis/historical/runecl-score-shape.py --top 20runecl-crosswalk-dispatch.py reports physical handler movement. The score
helper is a non-mutating historical guardrail: it checks the required
code-plus-table extent, sums all positive/absolute handler deltas, and lists
focus opcodes. Neither script selects current work or replaces the accepted
canonical comparison.
When a dispatcher target loads ECX from an absolute address immediately before
a __thiscall, resolve that address through config/reccmp-globals.csv before
choosing the C++ owner. Bind the observed global and an evidence-backed member
declaration rather than retaining a dispatcher facade or guessing an adjacent
subobject with pointer arithmetic. Record a new global/function ledger name
only after target disassembly establishes its address, access width, and ABI;
cross-version class ownership is not sufficient evidence.
Add a [[units.relocations]] entry only after proving its byte offset, COFF
symbol, relocation kind, exact TH08 target address, and addend behavior. The
comparator rejects actual relocations that differ from the manifest and rejects
the wrong target hash. Never wildcard an unresolved relocation or borrow a
TH06/TH07 address. A zero-addend DIR32 function pointer accepts only the
canonical TH08 function start supported by the ledgers; an address merely
falling inside the image is not evidence for a function, global, or literal.
Compiler-local $L numeric suffixes may change when an earlier function in the
same translation unit changes; their offset, kind, addend, and resolved target
must still agree exactly.
Treat a missing header-inline COMDAT as a caller/TU question, not a license to
manufacture a symbol. Prefer restoring a target-proven caller so the configured
object emits the function naturally. Do not add dummy callers, artificial
function-pointer references, or noinline solely to satisfy the comparator;
defer exact acceptance when natural emission is not yet available.
Conversely, when a mapped member has an observed target body and target callers
use a direct REL32 call to it, do not leave its recovered definition header-
inline. Move the same natural, side-effect-free body to the target-proven
translation unit, rebuild its owner object, and compare the independent body
before recording it. This restores real object partitioning and can unblock
caller relocations; it is not permission to add noinline, dummy references,
or a forwarding shim. The four Supervisor option predicates at
0x00438A29..0x00438A71 are the corpus example.
After converting a header-inline or declaration-only member into a target-
observed direct callee, run the serial normal link in addition to the focused
object comparisons. A focused caller object can exactly replay an unresolved
REL32, while the normal link correctly exposes a declaration with no emitted
definition. Recover and compare the real target body instead of adding a dummy
definition or a linker-only workaround. Player::DeletedCallback and
AsciiManager::FUN_00422bb0 are the TH08 corpus example.
A mapping and an external target body establish ABI, not the original
translation-unit profile. Before moving an inline member, compare the natural
candidate object(s): a target-only mov esp, ebp; pop ebp epilogue or a
different receiver/member access sequence is evidence that the assumed TU or
layout is wrong. Stop short of a match and retain the existing source form
until that difference is explained; do not tune it with inert locals, padding,
or assembly. The GameManager query cluster at 0x00406C70..0x00406DE3 is
the corpus counterexample.
This VC7 build can retain obsolete inline definitions in build/th_pch.pch
after a header-only member becomes out-of-line. If a full link reports a
duplicate that is absent from the edited header, rebuild the generated PCH and
then the normal target before diagnosing source ownership. Keep this recovery
limited to reproducible build/ artifacts; never delete source, target, or
analysis data to clear a compiler cache.
An exact JSON result proves only the configured function bytes after the
declared relocations. It does not prove the original object partition, adjacent
functions, data layout, or a repository-wide percentage. Update shared
mapping/match manifests and published status only in a reviewed coherent commit.
Before publishing repository-wide authored totals, cold-build and replay the entire accepted ledger:
python3 scripts/analysis/verify-exact-units.py --all --json \
> build/accepted-unit-replay.json--all is cold by default: it regenerates the objdiff graph, cleans only
generated Ninja outputs plus the explicitly known VC7/linker side outputs,
builds every configured comparison object with one job, and then replays
accepted units. --reuse-build is a non-attesting diagnostic shortcut. A
focused exact result may update its own row only after the affected object's
accepted units pass; aggregate progress requires the cold full replay.
Do not force bytes with naked assembly, copied code arrays, arbitrary padding, fake types, ABI lies, empty behavior, or target patches. Run only one Wine/VC7 build at a time. End with:
python3 scripts/validate-tracking.py --require-target
python3 scripts/analysis/verify-exact-units.py --all
python3 scripts/ci.py
python3 scripts/progress.py --check
git diff --checkWhen matching /Od replay callbacks, check three high-value shapes before introducing extra locals. First, let new T remain directly inside registry/allocation calls when the target frame already contains VC7's hidden new-expression temporary; a redundant named allocation local can add exactly six bytes. Second, retain apparently pointless legacy division/modulo stores when target code contains their signed arithmetic lowering; adjacent Touhou sources may explain why the statements existed, but validate the exact constants against TH08. Third, infer narrow-looking setter ABIs from exact callers as well as the callee: a byte store in the callee does not prove a byte parameter if callers consistently sign-extend to int.
For a final one-byte register-owner mismatch, do not use padding, inline assembly, or fake types. Build a bounded expression/type matrix under the repository VC7 flags and compare instruction templates against already-exact corpus functions. Record eliminated hypotheses in .analysis/ or docs/BUILD_MATCHING.md, keep the semantic implementation, and defer the match until natural C/C++ emission reaches zero differences.
For /Os GUI setup code, keep target-observed table dimensionality and branch-local validation. A real paths[stage][shot] table can be codegen-visible (imul stage,row_size plus scaled column), so do not flatten it merely because the address is equivalent. Likewise, two mutually exclusive resource-load arms may intentionally carry duplicate NULL checks; hoisting a shared check can shorten the function materially. When a helper or ANM member is addressable through both a receiver home and a canonical global/member owner, preserve the owner shown by target ECX/member offsets. Use already-exact accessors to name overlapping AnmVm fields before tuning a large grid loop.
For VC7 /Od interpreters and spawn paths, separate non-trivial object lifetime from scalar-local placement. Keep branch-only vectors or timer-like objects in the lexical branch so their constructors are not hoisted. If adjacent scalar locals remain swapped despite declaration and #pragma var_order experiments, test whether the source shape is one meaningful POD work aggregate rather than adding padding or no-ops. Preserve explicit continue/fallthrough structure when it controls short-branch extents or the start of an associated jump table.
For large copies, compare target rep movsd register ownership before using memcpy. Typed aggregate assignment can intentionally schedule source/destination/count in a different order and is often the real source shape. Likewise, use semantic one-bit fields for target bitfield RMW sequences rather than algebraically equivalent mask/or expressions.
When an exact dependency still belongs to the reconstruction probe graph, keep one canonical probe implementation and leave normal production link inputs clean. Do not create duplicate forwarding shims merely to satisfy the linker; promote the owner and its dependency closure together.
Treat authored exact identity as target address + extent, not logical name. Overloaded members may share the same reccmp/mapping name; tooling that collapses authored rows by name will mis-account exact evidence. For tiny __fastcall bridge functions, do not cache ECX/EDX arguments unless target stack accesses prove a third local. Repeated casts/field accesses from the original parameter can be the exact /Od source shape.
For VC7 /Od ECL helpers, two class-local constructor calls can require sequential non-overlapping source scopes. If the target constructs a dead legacy Float3 in one slot and then a used Float3 in another, test { Float3 legacy; } Float3 used; before changing declarations globally. A synthetic aggregate is not equivalent if it creates a compiler-generated constructor.
Keep conditional expressions wide until a target-proven narrow store. If a ResolveInt/raw-value ternary feeds an i16 descriptor member, casting both arms early can create word stack temporaries; leaving the ternary as i32 lets VC7 materialize a dword conditional temp and then copy its low word, matching the TH08 shot descriptor path.
Guard-return grouping is code-generation-visible. A single if (rejectA || rejectB) return; can make VC7 share one nearby reject trampoline, while two separate returns duplicate long epilogue jumps and the inverse positive if can produce long conditional branches. For x87 range gates, preserve positive strict predicates such as radius > 0.0f && distanceSquared < radius when the target status masks prove them; algebraically equivalent <=/>= rewrites can change unordered behavior and branch opcodes.
When a TH08 /Od float comparison contains Float3::operator float*() or another call, keep the exact source-side operand ownership observed in the target. positionMember < bound can avoid an x87 spill that appears for the algebraically equivalent bound > positionMember. Also prefer a proven typed member over a raw offset when the target's fld/fcomp ownership depends on the member AST.
Do not normalize direct/wrapped distance tests: the target can require directDistance < wrappedDistance specifically. Reversing it to an equivalent wrapped <= direct changes fcompp ownership and status masks. Likewise preserve branch polarity when one side is much larger; if (duration <= 0) immediate else timed can be required solely to keep the large block as fallthrough and the short call at the tail.
For slot-scanning installers, try rejection-first control flow before adding temporaries: if (reject) continue; install; break; lets VC7 place the continue edge at the loop increment and the successful install fall through to the common epilogue. A positive accept block with return may add both a long continue branch and an epilogue jump. TH08 interpolation callbacks use a separate eight-pointer table at 0x004C6C90; do not alias it to g_EclExInsn @ 0x004C6CB0.
Switch bounds checks can depend on where shared labels are written in source. If VC7 inserts a tiny default trampoline, try placing the shared success code inside the switch after the final case and place default: on the shared failure label immediately afterward. In TH08 CompareOperands, that lexical layout removed the only two-byte residual while preserving the 12-entry associated jump table.
For repeated operand dispatch, do not cache resolved values unless target call counts prove a source local. Independent ?: reads can be responsible for large compiler-owned temp frames. ApplyInterpolationOperation also proves that an operand may intentionally be resolved twice.
When a rep movsd copy uses a target-resident static source, test a real extern aggregate owner instead of an absolute pointer cast. The g_EclCallParameters @ 0x004ECE20 aggregate changes VC7 evaluation from source-first to the target destination/count/source order. Use typed EnemyEclContext aggregate assignment for 0x228 stack frames rather than memcpy/asm.
For raw VM-offset cleanup, verify the exact narrow field. enemy + index*0x2A4 + 0x4CA is AnmVm::scriptIndex relative to VM base +0x2B0; activeSpriteIndex would be six bytes earlier and is not equivalent.
When a target address falls inside a known large global object, express it through the owning typed object and index/field before creating a new global. 0x00F4F8F0 is &g_EnemyManager.enemies[480]; VC7 emits the target base relocation plus a large addend automatically. This is preferable to an absolute pointer or synthetic sentinel symbol.
For one-bit setters, a real bitfield assignment can be required for register ownership even when a manual mask/OR expression is mathematically identical. If target code computes the incoming bit first and then uses the old flag word as the OR destination, test a bitfield member store.
If MSVC reports that a newly declared member does not exist, check whether that class definition is already cached in the PCH through an indirect include. Force-rebuild th_pch.pch before treating the diagnostic as a declaration/source mismatch.
Before tuning a small residual, verify that the function is compiled in the target-proven translation unit and optimization profile. Gui::StartStageBackgroundSequence @ 0x00439007 is 77 bytes in the /Od ECL dependency probe but exactly 73 bytes in the real /Os Gui TU with the same behavior; the profile change naturally restores EAX reuse and the leave epilogue.
For a loop that owns a non-trivial vector local, constructor timing and physical stack order are independent. BulletManager::RemoveBulletsInRadius materializes the bullet-pool pointer before constructing Float3 delta, while #pragma var_order(delta, i, bullet, this) controls the final slots. A rejection-first if (distanceSquared > radiusSquared) continue; is also target-visible and should be preferred over an algebraically equivalent positive-body form when the target has a short conditional plus explicit loop jump.
When a cross-version aggregate lines up field-for-field with target accesses, promote the target-proven fields instead of maintaining raw offsets. TH08 SpawnLaserPattern confirms the laser runtime tail and the descriptor's nine +0x1D0..+0x1F0 laser parameters; the resulting typed TH06-style source matches all 639 target bytes directly. Re-audit large-dispatcher direct-call closures after such promotions and separate remaining runtime/library wrappers from authored functions.
If a target function is an SDK/header-inline helper, keep the toolchain header as the source of truth and let a real reconstruction caller TU emit its COMDAT naturally. Do not paste the SDK body into src/ or add a dummy caller. Pick a stable TU that already calls the helper, require a normal build plus strict per-function relocation replay, and only then record exactness. The TH08 D3DXVec3Length/Dot/Cross bodies are emitted naturally by Background.obj this way.
Do not treat T members[N] as codegen-equivalent to T member0; ...; T memberN; when T has a target-visible constructor. VC7 can lower the array through ??_H but emit one direct constructor call per individual member. If target construction shows repeated direct calls and all consumers use constant indices, split the fields, keep the same offsets, and rerun every accepted unit that touches the aggregate before promoting the layout.
For VC7 effect callbacks, place a non-trivial work vector at the lexical point where the target first calls its constructor; do not hoist it just because C++ permits it. Use direct owner expressions when the target repeatedly reloads an enemy/global/member instead of caching references. Hidden Float3 return temporaries then tend to fall into the target slots naturally. Treat x87 comparison polarity and return-block order as source evidence: equivalent negation can keep semantics but swap je/jne and fail strict matching.
For dense /Os switches, verify the enum's actual numeric base before changing source shape. A compiler normalization such as dec can mean the first logical enum value belongs to the default path. If the target bounds check is short and the table starts immediately after the authored body, test lexical default placement as well as case order; Gui::FUN_00438046 requires default first and uses size=body, compare_size=body+table.
For VC7 /Od branch-local class temporaries, the target may consume the constructor's returned this directly. When a target shows ctor; push eax with no later address materialization, an old-MSVC address-of temporary expression such as &Float3(...) can be the real source shape. A named local followed by &local adds an observable lea. Accept the temporary form only under strict canonical replay and never replace it with asm or byte padding.
Treat early-return topology as codegen evidence. if (value == 0) return; body; and if (value != 0) { body; } are semantically equivalent at function end but can differ by a short conditional plus an explicit epilogue jump. Preserve the form demonstrated by the target.
When VC7 /Od target code stores an x87 result with fst [local] and immediately continues arithmetic without reloading it, test a real named source local rather than an artificial spill. A named value assigned from the call and used in the following expression can make VC7 preserve both the stack home and the live ST0 value; use #pragma var_order only to place proven locals.
Canonical evidence should come from a COFF function symbol with a reproducible extent. If an already-exact TU-local static helper has only a raw/internal aux record, prefer promoting that existing implementation to its real namespace-level owner when ownership is semantically valid and codegen remains exact. Do not add comparator exceptions merely to accept an unbounded symbol.
Some VC7 compiler-generated functions have no function-definition aux record even though their .text COMDAT is an isolated, reproducible body. Do not hand-write a fake deleting destructor merely to manufacture an aux record. Use allow_auxless_comdat = true only when the manifest intentionally owns that compiler body and the comparator can prove all of: code+COMDAT section, function symbol at offset zero, exactly one section-defined external function owner, and section length equal to the full comparison extent. Relocations must still match and replay normally. Undefined symbols of the same decorated name are references, not competing body owners.
For a real implicit class constructor, prefer a real explicit empty constructor if that preserves bytes: AnmVmBase::AnmVmBase demonstrates that member initialization remains compiler-generated while the explicit owner gains a standard COFF function extent. Keep ordinary functions off the auxless path whenever a natural source-level owner can provide normal canonical evidence.
When a target writes a scalar/pointer local before constructing a later non-trivial local, test a declaration initializer rather than a body assignment. VC7 may hoist non-trivial local construction ahead of ordinary body statements, while C++ declaration initialization preserves declaration sequencing. ECL EX FUN_00423A60 requires the bullet cursor to be initialized at declaration before a later Float3 constructor.
Short-circuit operand order is machine-code evidence even when the boolean expression is commutative. For a == 0 || b == 0, VC7 emits the first stack-home compare exactly in lexical order; do not reorder equivalent operands after the target has established their sequence.
When several absolute globals differ by an already-attested type size, test an aggregate owner before introducing independent globals. The TH08 ECL EX barrier block has AnmVm instances at 0x004E4B68 and 0x004E4E0C, exactly 0x2A4 bytes apart. Modeling g_EclExBarrierRenderState @ 0x004E4B60 with vm0 @ +8 and vm1 @ +0x2AC naturally reproduces all target member-address relocations.
Do not infer callback parameters from the functions that publish the callback address. A stored function pointer may target a no-argument callback even when the publisher itself is fastcall with ECX/EDX inputs. Use the callback target prologue and actual indirect call sites; update relocation symbol spelling when ABI recovery changes the source declaration.
Non-trivial local arrays are compiler structure, not just storage. If the target calls VC7's vector-constructor iterator with element size/count/ctor, express the local as a real C++ array of that element type and keep its lexical lifetime. Also retain target-proven dead locals instead of deleting them as cleanup.
#pragma var_order controls VC7 local stack homes but does not require declarations to follow that same order. For multiple non-trivial locals, recover two separate facts from the target: physical slots and constructor call chronology. The ECL EX laser-hitbox trio constructs origin -> outer -> inner -> position while the stack is ordered nearest-EBP as position, outer, inner, origin; a matching pragma plus semantic declaration order reproduces both facts.
When a target tests a mask and then skips a large region with one forward branch, write the source as a positive wrapper around the body. Replacing it with an equivalent reject-and-continue commonly generates jcc + short jmp and changes the function by two bytes. This pattern repeats in ECL EX bullet callbacks at 0x00424A20, 0x00424C40, and 0x00424E50.
For floating ternaries, recover comparison polarity and expression operand ownership separately. constant + local can be required even when local +/- constant is algebraically identical, because VC7 chooses different x87 load/add/sub forms.
An empty user-written constructor can still compile to a large target when its class contains non-trivial members and arrays. If the target consists of the expected member ctor calls/vector-constructor iterators, prefer fixing the class layout and leaving the body empty. AsciiManager::AsciiManager @ 0x00402000 becomes 0x128-byte exact solely from correct member ownership/order.
A VC7 function symbol's aux total_size may include an immediately associated jump table. If the target's authored mapping stops at the first table byte, keep authored progress at that body size and set the canonical unit's compare_size through the table. AsciiManager::OnDrawLowPrioImpl is 0x6A2 authored bytes but 0x6B6 compared bytes because of its five-entry table.
Do not force standard math/runtime helpers into authored reconstruction merely because an imported seed marked them function. If the target name/provenance and body establish a CRT/x87 helper (_sinf, _cosf, _sqrtf, fabs, fmodf, fsincos in TH08), change the inventory type to library. This is a classification correction, not an exact-match claim; revisit the helper only after authored work is complete. Never use inline asm just to mimic such runtime stubs.
VC7 MATH.H itself is valid provenance for float wrapper classification: inline acosf/atanf/tanf lower to tiny x87 wrappers around CRT double cores. If the target has that wrapper shape and the core is runtime-owned, classify the wrapper as library; do not write inline asm or count it as authored reconstruction.
In large VC7 /Os setup functions, local-vs-global receiver ownership, helper placement, table dimensionality, and loop spelling are first-class exact-match evidence. Equivalent refactors can move dozens of bytes.
If POD new/delete/free traffic has stable stack work homes, model the real allocation/deallocation pointers as semantic locals and control them with #pragma var_order; do not use padding or asm. Direct operator new/delete is acceptable only when the owner has no constructor/destructor work and full canonical replay proves equivalence.
A target dword work value later consumed as one byte should not automatically be declared u8. Preserve the dword source shape when target stores prove it.
When a same-type totals record physically follows an array, a narrow overlay for the contiguous table can recover the original constant-index AST without undefined one-past-array access.
If an exact function lives only in a dirty production TU, make the evidence reproducible before promotion: copy the minimal function plus required file-scope data/helpers into a probe compiled with the same rule, verify strict zero-diff there, and ledger the probe object. Never make canonical progress depend on an uncommitted experimental object.
Keep stable exact probes narrow. Remove neighboring near-match experiments before committing an exact anchor so future agents can reproduce the accepted unit without inheriting unrelated unfinished code.
When an /Os function appears longer by exactly N*4, inspect the target boundary before changing source: VC7 may have appended an associated switch table to the COFF aux extent. Use authored size plus a larger compare_size and replay the table relocations.
Do not replace direct flag/bitfield reads with convenience getters in exact work; even a one-instruction getter call can change branch reach and body size. Let target relocation/disassembly decide the lexical owner.
If a large function has equal instruction count/extent and only stack-displacement residuals, map source locals from first use and solve with #pragma var_order before touching control flow.
Repeated cmp operand-owner residuals can signal a real semantic inversion. If target behavior is cursor >= count but source says count >= cursor, correct the relation itself; do not treat the mismatch as a register-allocation problem merely because the function extent is already exact.
Continue to include associated switch tables in compare_size while counting only the authored body in progress. OnUpdateSpellCardSelect is a 0xFCF body plus a 0x2C table.
If target and object have the same instruction count but the object is much shorter, compare frame sizes before searching for missing logic. TitleScreen::OnUpdateSpellStageSelect was 589 instructions on both sides; three omitted per-inline inlineSlot homes reduced the frame by 12 bytes and allowed hundreds of deep-stack accesses to use disp8 instead of disp32. Reusing the target-shaped inline helper restored the frame and exact bytes without padding.
For small fastcall helpers, a zero-extent-gap residual concentrated entirely in ebp-4/ebp-8 displacements is often only #pragma var_order. ConvertToFullWidthDigits @ 0x0046D763 became exact by changing the order to (i, multiplier); keep the arithmetic untouched when the target/object instruction forms already agree.
/Os /Ob1, use a target-proven (... ? TRUE : FALSE) when the binary materializes a BOOL work slot before testing it; a direct logical OR usually skips that slot.push imm / pop / shl), an inline member helper can preserve the parameterized index without the extra pointer temp introduced by a free helper.i32 helper parameters can also be source evidence for target variable-index stores that would otherwise constant-fold. Avoid do/while(0) macros when the target has no zero-loop tail./Os /Ob1 vararg formatters, avoid caching aggregate/table references unless the target has a source home for them. Four innocent C++ references enlarged FormatSpellCardInfo's frame by exactly 0x10.zero ? fallback : real and nonzero ? real : fallback are semantically equivalent but select opposite physical arms.character*stride; add aggregate_subobject_base), a free helper over the final field value may over-fold the address. Model the helper as a narrow member view so VC7 retains the implicit this parameter and original aggregate ownership./Os, field & ZUN_BIT(stage) can evaluate the mask first, while ZUN_BIT(stage) & field evaluates the field first.© N0zoM1z0, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 1 other file in .agents/skills/th08-matching of N0zoM1z0/th08.
Open the folder on GitHubat commit 67e7e49
TH08 Exact Function Matching next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| TH08 Exact Function Matching this skillN0zoM1z0/th08 | 105 | — | ~8.7k | Automated safety check: Pass | MIT | |
| Ghidra ReOrbitCurve/firmware-reverse-engineering | 216 | — | ~4.2k | Automated safety check: Pass | Apache-2.0 | |
| Binary Reaiskillstore/marketplace | 433 | 1 repos | ~2.6k | Automated safety check: Pass | None | |
| Rea Changelog Updatemorluto/rea | 80k | — | ~1.9k | Automated safety check: Pass | MIT | |
| Bench ExperimentDavidClawson/OpenScope-2C53T | 116 | — | ~1k | Automated safety check: Pass | GPL-3.0 | |
| Rea Tool Designmorluto/rea | 80k | — | ~239 | Automated safety check: Pass | MIT |
OrbitCurve/firmware-reverse-engineering
Expert-level Ghidra reverse engineering for firmware binaries with emphasis on stripped binary analysis, automated function discovery, cryptographic routine identification, authentication logic…
aiskillstore/marketplace
This skill should be used when analyzing binaries, executables, or bytecode to understand what they do or how they work.
morluto/rea
Prepare or rewrite REA release changelogs and GitHub release notes from pinned Git history, with verified contributor thanks and Release Please synchronization.
DavidClawson/OpenScope-2C53T
Run and record a hardware experiment on the 2C53T bench using a controlled five-step cycle.
morluto/rea
Design or change REA investigation tools, CLI/MCP contracts, provider capabilities, and Evidence semantics.
rudybear/renderdoc-skill
GPU frame debugging with RenderDoc via rdc-cli. An agent skill from rudybear/renderdoc-skill.
N0zoM1z0/th08
Covers recovering and verifying the VC7 C runtime, compiler-runtime and D3DX library functions in the TH08 decompilation, with hash-pinned evidence.
N0zoM1z0/th08
Reconstructs bounded functions from the original Japanese TH08 1.00d executable for a source decompilation project, using hash-verified target evidence and labeled corroboration.
N0zoM1z0/th08
Generates and reads target-pinned instruction and ABI fact packets for a source reconstruction project, to guide source shaping before a strict comparison.
N0zoM1z0/th08
Replaces raw offsets and anonymous fields in a TH08 C++ source reconstruction with evidence-backed names and types, without changing accepted bytes or playable behavior.
Categories
Builds TH08 functions with the repository's VC7 toolchain and compares each against the hash-attested 1.00d binary to tune code generation and verify exact matches. 00d, used alongside the th08-re skill once the target boundary, semantics and ABI are backed by evidence.py, which reports exact, mismatch or error.
TH08 Exact Function Matching fits situations like: tuning source until a function compiles to the target bytes; diagnosing relocation mismatches in a decompiled unit; verifying an exact match before recording it; searching built leaf functions for candidate exact units.
Run `npx skills add N0zoM1z0/th08 --skill th08-matching -a claude-code`. Or copy the skill folder (.agents/skills/th08-matching in N0zoM1z0/th08) into .claude/skills/th08-matching in your project. Claude Code loads it when a task matches its description.
Run `npx skills add N0zoM1z0/th08 --skill th08-matching -a codex`. Or copy the skill folder (.agents/skills/th08-matching in N0zoM1z0/th08) into .agents/skills/th08-matching in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add N0zoM1z0/th08 --skill th08-matching -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/th08-matching, .gemini/skills/th08-matching, .github/skills/th08-matching and .opencode/skills/th08-matching in your project.
Going by SKILL.md and its folder, TH08 Exact Function Matching needs the command-line tools its instructions call (python3, git and cursor). Our summary lists: Python 3; The repository's VC7 build setup; Ghidra, only for exporting original objects.
SKILL.md contains no URLs. Its commands use git, which can reach the network depending on how they are called. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
TH08 Exact Function Matching is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 8.7k tokens (SKILL.md is roughly 35k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with TH08 Exact Function Matching: Ghidra Re (OrbitCurve/firmware-reverse-engineering, 216 stars), Binary Re (aiskillstore/marketplace, 433 stars), Rea Changelog Update (morluto/rea, 80k stars) and Bench Experiment (DavidClawson/OpenScope-2C53T, 116 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
N0zoM1z0 (a GitHub user) maintains it in N0zoM1z0/th08, which has 105 GitHub stars. The repository holds 5 skills in this directory. The repository was last updated on October 11, 2026.
Source: N0zoM1z0/th08 on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.