server-re/decomp/Agent_Release_x64/evidence.md
2026-10-15 09:08:48 -04:00

6.2 KiB
Raw Blame History

Agent_Release_x64.exe — finding ledger

  • SHA-256 6f2be7b8eb799ba51b359d1958720aac5baee5b7ee0d1d36684d3cbd72d45469, 1,472,512 bytes
  • PE32+ x86-64, Windows CUI, MSVC linker 10 (VS2010; imports MSVCR100/MSVCP100)
  • PDB reference F:\work\Project\Output\Exe\Agent_Release_x64.pdb (GUID a4775113-…, age 1) — PDB not available; names are Ghidra defaults
  • Ghidra 12.1.2 via REA 6.5.0; 6,814 procedures, 1,472 defined strings (ev_9fe3a449…)
  • Full Evidence: evidence-bundle.json (14 records). Static analysis only — nothing here was observed at runtime.

Status: first pass (process-control core). Network protocol and config parsing not yet decompiled.

Language: C++ (high confidence)

Observed C++-only artifacts: MSVC RTTI type descriptors (.?AV…@@, incl. templates MBISingleton<T>, CFastArray<T>), vftables, __CxxFrameHandler3 (C++ EH), operator new/delete, MSVCP100 std::basic_ostream imports. Multiple vftables per class (e.g. MagiAgentNetwork, MGAgentManagerSession) indicate multiple inheritance. Statically linked C++ libraries: ProudNet, TinyXML (TiXml*), ATL.

Outputs

Porting spec (Win32 → Linux substitutions, behaviour to reproduce, original bugs): porting.md.

Path Content
full/functions/<addr>.c Ghidra pseudocode for all 6,423 non-thunk functions (1 failed; see full/index.tsv). Includes library code (ProudNet, TinyXML, CRT) — not yet labelled.
full/index.tsv address, namespace, name, body size, decompile status
full/vtables.tsv 1,430 RTTI vftable slots → function addresses (all classes)
cpp/classes.hpp Generated C++ skeleton of 43 application classes from vftables (compiles); method names unknown
cpp/AgentProcessControl.cpp Hand-lifted C++ for the 6 analyzed functions; names inferred, not compilable as-is
../scripts/ExportDecomp.java, VTableMap.java Ghidra headless scripts that produced full/

Additional observations from the lift (cpp/AgentProcessControl.cpp):

  • Watcher thread never closes the process handle → handle leak per launch (observed).
  • LaunchProcess passes a stack PROCESS_INFORMATION* to the watcher thread; safe only because of the following Sleep(1000) (observed race, inference about intent).
  • KillProcessByName leaks the snapshot handle on the success path (observed).
  • RMI stubs give the protocol handler tables: AgentManager2AgentStub 24 slots, Server2AgentStub 19 (full/vtables.tsv).

Findings

# Conclusion Type Evidence
1 Process supervisor for the other server EXEs: launches them, kills them, watches for exit Observed (code) 0x1400094e0, 0x140009780, 0x140008c90 below
2 Launch: CreateProcessW(NULL, cmdline, …, CREATE_NEW_CONSOLE (0x10), …, workdir, …); console 700×110, fill attr 0x17; closes thread handle, spawns watcher thread, returns PID (0 on failure, logs =======Server Fail======= + Path/File) Observed 0x1400094e0.pseudo.c, ev_e77cf4b5…
3 Command line = <exe path for server type> + " " + <args>; args either generated (FUN_140027540, a global option flag at +0x152 selects) or the numeric server code. Logs FAILED SERVER ON %s on failure; reports result to AgentManager via RMI proxy Observed (flow); arg contents unknown 0x140008ea0.pseudo.c, ev_e87b1dd7…
4 Kill: Toolhelp32 snapshot, case-insensitive exe-name match, OpenProcess(PROCESS_TERMINATE), TerminateProcess(h,0) — kills the first match only, by image name (not PID) Observed 0x140009780.pseudo.c, ev_ad71d730…
5 Watcher: polls GetExitCodeProcess every 10 ms while STILL_ACTIVE (0x103); on exit sends two RMI notifications to AgentManager; relaunches if per-server revive flag (+0xd5) or global flag (+0x171) is set Observed; mapping of flags to revive_server XML key is inference 0x140008c90.pseudo.c
6 Single instance via named mutex Global\RustyHearts_Agent; on ERROR_ALREADY_EXISTS / ACCESS_DENIED prints, sleeps 3 s, returns failure Observed 0x1400100b0.pseudo.c
7 Main run loop: startup banner, CoInitialize, builds network objects, kills a stale process by name before running (type 0x18 entry), loop Sleep(1) with 100 ms tick and 1 s FPS counter; optional delayed "Start Auto Server On" after a configured time (+0x178) Observed 0x1400037a0.pseudo.c, ev_2fa3bc1d…
8 Networking is ProudNet (Nettention) — Proud::CNetServerImpl, CNetClientImpl, RMI stubs/proxies Observed (RTTI + assert strings) strings ev_47e1666c…
9 Agent is both a ProudNet server (for game servers: SERVER2AGENT_PROTOCOL stub, AGENT2SERVER proxy) and client of AgentManager (AGENTMANAGER2AGENT stub, AGENT2AGENTMANAGER proxy) Inference from RTTI class names strings ev_47e1666c…
10 Config files: option\agent_serveroption.xml, option\agent.xml, option\total_option.xml, option\patch.xml, option\customoption.xml, option\service_control.xml Observed (strings); which are actually read is unknown — option\agent.xml referenced from FUN_1400f06e0 only ev_d4bb4bc3…
11 Crash reporting via BugTrap (BT_* imports) + MiniDumpWriteDump; also an HTTP error reporter GET /PNReport/ReportError.php?Text= (ProudNet's) Observed (imports/strings); host contacted unknown imports

Named-function map (proposed, not applied in a persistent DB)

Address Proposed name
0x1400037a0 MagiAgentServer::Run (main loop)
0x140008ea0 ServerOn(serverEntry)
0x1400094e0 LaunchProcess(cmdline) -> pid
0x140008c90 ProcessWatchThread
0x140009780 KillProcessByName(name)
0x1400100b0 AcquireSingleInstanceMutex

Open questions / next steps

  • Decode RMI message IDs/handlers for AGENTMANAGER2AGENT and SERVER2AGENT (commands: All Server On/Down/Kill, Server Info, Shutdown, Notice, Patch).
  • Parse config XML loaders (FUN_1400f06e0, MagiServerOption), and map fields (start_server, revive_server, ports) to struct offsets.
  • Identify FUN_140027540 (argument builder) to learn exact launch arguments.
  • Locate ProudNet SDK version to label library code (likely large share of the 6,814 functions).