7.9 KiB
Workflow — reverse-engineering a server binary
The repeatable process used for Agent_Release_x64.exe. Follow it for each
binary in decomp/INVENTORY.md. Ground rules, layout and tooling are in
AGENTS.md. What has been learned so far is in docs/ARCHITECTURE.md.
Every finding is recorded as observed, inference or unknown, with an Evidence ID or decomp file reference. Static analysis never establishes runtime behaviour.
0. Prerequisites (once per machine)
- Ghidra is at
/opt/ghidra, and Java 21 and Node come frommise.toml.- REA MCP is registered project-locally in
opencode.json. - Don't reinstall any of these; repair the configuration instead.
- REA MCP is registered project-locally in
- Ghidra headless fails with
UnsupportedClassVersionError(class file 71.0): its compiled-script cache was built by a newer JDK.- Move
~/.config/ghidra/ghidra_<ver>_DEV/osgi/compiled-bundlesaside. - Ghidra rebuilds the cache on the next run.
- Move
1. Identify the binary (cheap, before any decompilation)
sha256sum binaries/<X>.exe
objdump -p binaries/<X>.exe | grep -E 'DLL Name|Subsystem|MajorLinker|pdb'
In REA:
- Run
open_binarywith the original path underbinaries/. - Run
binary_overview. - Run
search_strings(regex) for config paths, log text, protocol names and class names.
Record the SHA-256, linker version, PDB path, imports and procedure count in
decomp/<X>/evidence.md.
2. Determine language and toolchain
Check for these C++ markers:
- RTTI descriptors (
strings -a | grep '^\.?AV') __CxxFrameHandler3- an
MSVCP*.dllimport
All 16 servers are MSVC 2010 C++ (see docs/ARCHITECTURE.md). Re-check each
new binary and record the result.
3. Discover third-party libraries and acquire their sources (mandatory)
Any library you can match against upstream source saves you from decompiling it, and gives names, struct layouts and intent that the binary lacks. Every time a library is found, search for its source online before continuing.
3a. Find candidates
| Signal | How |
|---|---|
| Imported DLLs | objdump -p DLL names, cross-checked against binaries/*.dll |
| DLL version resources | strings -el <dll> | grep -A1 -E '^(FileVersion|ProductName|CompanyName)$' |
| Embedded version text | strings -a | grep -iE '<lib>/[0-9]|part of OpenSSL|libcurl/' |
| Statically linked C++ libraries | RTTI class prefixes (TiXml*, Proud::*, ATL::*) |
| Library-specific strings | assert/error messages (Proud::CNetServerImpl::…, TinyXML error table) |
| PDB paths | e.g. …\OpenLibrary\openssl-1.0.0d\…, which names the exact version |
3b. Pin the version
Use, in this order of preference:
- An explicit version string (VERSIONINFO,
"OpenSSL 1.0.0d 8 Feb 2011"). - A version in the PDB path.
- Differential evidence: download several candidate releases and compare a
distinctive artefact with the binary. Examples are error-string tables,
exported symbol lists and header constants.
- Example: the TinyXML error table contains
Memory allocation failed.. That entry exists in 2.5.x and was removed in 2.6, which gives 2.5.1–2.5.3.
- Example: the TinyXML error table contains
- If none of these work, choose the closest era-compatible release and mark it closest, not exact.
3c. Search for the source
Search in this order:
- Official release archives (project site or SourceForge; check
old/andarcheology/paths). - Official git tags (
git ls-remote --tags <repo>), or a GitHub tag archive if the release tarball no longer exists. - Vendor package repositories. These often contain headers only.
- Clean-room equivalents (for example mingw-w64 headers for Microsoft APIs) or open reimplementations (for example Wine).
gh search repos <lib>for forks or mirrors. Treat these as unverified and label them that way.
Verify each download against an official checksum where one exists. Always record its SHA-256.
3d. Check the licence and place the files
| Licence | Location | Committed? |
|---|---|---|
| Open source (OpenSSL, curl/MIT, zlib, BSD, public domain) | third-party/<lib>-<version>/ |
Yes |
| Proprietary or EULA-restricted (e.g. ProudNet) | third-party/_local/<lib>-<version>/ |
No. It is gitignored. Ask the user before fetching. |
| No public source (XTrap, Apex, HASP runtime) | nothing downloaded | Record acquisition routes only |
Other rules:
- Keep upstream files unmodified. If you must change one (for example curl's version stamp), record exactly what changed.
- Read the licence before committing anything. If redistribution is
prohibited, it goes in
_local/or isn't fetched.
3e. Document it
Add a row or section to third-party/README.md with:
- the observed identity in the binary, with where it was seen;
- the version and how it was pinned;
- the source URL, tag or commit, and SHA-256;
- match quality (exact / closest / headers only);
- the licence;
- any local edits.
Also:
- Mention the library in
docs/ARCHITECTURE.mdif it is statically linked. - Add it to the list in
AGENTS.md. - If no source exists, add it to the "No public source" and "Acquisition routes" tables instead.
3f. Use it
- Label library functions in the decompiled output from known exports, class method tables, error strings and the matched headers.
- Exclude labelled library code from application analysis.
4. Decompile
In REA, choose seeds from distinctive strings or imports and use
trace_feature to find the functions that reference them. Then run
procedure_pseudo_code / batch_decompile on those functions.
For full coverage, run Ghidra headless with the repo scripts:
analyzeHeadless /tmp/opencode/ghproj X -import binaries/<X>.exe -overwrite \
-scriptPath decomp/scripts -postScript ExportDecomp.java "$PWD/decomp/<X>/full" -deleteProject
analyzeHeadless /tmp/opencode/ghproj X -import binaries/<X>.exe -overwrite \
-scriptPath decomp/scripts -postScript VTableMap.java "$PWD/decomp/<X>/full/vtables.tsv" -deleteProject
This produces:
full/functions/<addr>.candfull/index.tsvfromExportDecomp.java;full/vtables.tsvfromVTableMap.java, which maps class vftable slots to functions.
Generate cpp/classes.hpp from vtables.tsv, then check that it compiles:
g++ -fsyntax-only.
5. Record evidence
In decomp/<X>/:
- Run
export_evidence_bundleto writeevidence-bundle.json. - Extract the REA pseudocode to
<addr>.pseudo.cand the overview tobinary_overview.json. - Write
evidence.md: the findings table (conclusion → type → evidence), the proposed function names, and the open questions. - Update the status and role of the binary in
decomp/INVENTORY.md.
6. Lift to readable C++
decomp/<X>/cpp/*.cpp holds hand-lifted code for the analysed functions:
- Cite the original address on each function.
- Mark every name as inferred.
- Record the original bugs you notice (leaks, races).
7. Porting spec
There is no source tree in this repo. The earlier src/ reconstructions were
removed on 2026-10-15. Instead, record the facts a re-implementation needs in
decomp/<X>/porting.md:
- the behaviour to reproduce, with decomp addresses;
- a substitution table: original address → Win32 API → nearest Linux equivalent → behaviour difference;
- the original bugs, and what is unknown or unrecovered.
8. Close
Run close_binary when the investigation is complete. Before closing, export
the Evidence bundle, because session annotations are temporary.
Checklist per binary
- SHA-256, imports, linker and PDB recorded
- Language confirmed
- Every third-party library identified, its version pinned, its source
searched for and downloaded where licensing allows, and it documented in
third-party/README.md - Key functions decompiled;
full/export done evidence.md,INVENTORY.md,docs/ARCHITECTURE.mdupdatedcpp/lift andporting.mdupdated, if in scope- Evidence bundle exported; REA session closed