server-re/docs/WORKFLOW.md
2026-10-15 09:08:48 -04:00

7.9 KiB
Raw Blame History

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 from mise.toml.
    • REA MCP is registered project-locally in opencode.json.
    • Don't reinstall any of these; repair the configuration instead.
  • 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-bundles aside.
    • Ghidra rebuilds the cache on the next run.

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:

  1. Run open_binary with the original path under binaries/.
  2. Run binary_overview.
  3. 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*.dll import

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:

  1. An explicit version string (VERSIONINFO, "OpenSSL 1.0.0d 8 Feb 2011").
  2. A version in the PDB path.
  3. 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.
  4. 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:

  1. Official release archives (project site or SourceForge; check old/ and archeology/ paths).
  2. Official git tags (git ls-remote --tags <repo>), or a GitHub tag archive if the release tarball no longer exists.
  3. Vendor package repositories. These often contain headers only.
  4. Clean-room equivalents (for example mingw-w64 headers for Microsoft APIs) or open reimplementations (for example Wine).
  5. 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.md if 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>.c and full/index.tsv from ExportDecomp.java;
  • full/vtables.tsv from VTableMap.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>/:

  1. Run export_evidence_bundle to write evidence-bundle.json.
  2. Extract the REA pseudocode to <addr>.pseudo.c and the overview to binary_overview.json.
  3. Write evidence.md: the findings table (conclusion → type → evidence), the proposed function names, and the open questions.
  4. 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.md updated
  • cpp/ lift and porting.md updated, if in scope
  • Evidence bundle exported; REA session closed