6.2 KiB
6.2 KiB
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).
LaunchProcesspasses a stackPROCESS_INFORMATION*to the watcher thread; safe only because of the followingSleep(1000)(observed race, inference about intent).KillProcessByNameleaks the snapshot handle on the success path (observed).- RMI stubs give the protocol handler tables:
AgentManager2AgentStub24 slots,Server2AgentStub19 (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
AGENTMANAGER2AGENTandSERVER2AGENT(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).