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

3.8 KiB
Raw Permalink Blame History

Agent_Release_x64.exe — porting spec (Win32 → Linux)

This was kept from the removed Linux reconstruction src/agent_release/ (removed 2026-10-15). It records the facts about the original binary that a re-implementation needs, and the nearest Linux equivalent for each Win32 call.

Sources: evidence.md, cpp/AgentProcessControl.cpp. Names are inferred; the behaviour is static-analysis evidence only.

Behaviour to reproduce

The servers being managed are read from config (option\*.xml, mapping not yet recovered).

Item Behaviour
Single instance (0x1400100b0) Named mutex Global\RustyHearts_Agent
Launch (0x1400094e0) CreateProcessW(NULL, "<exe> <arg>", …, CREATE_NEW_CONSOLE, …, workDir), console 700×110, fill attribute 0x17. Command line is limited to 260 characters (MAX_PATH). Returns the pid, or 0 on failure (logs =======Server Fail=======)
After each launch attempt Sleep(1000)
Command line (0x140008ea0) <exe for server type> <code>. An alternative argument builder FUN_140027540 exists, but its output is unknown
Watcher (0x140008c90) Polls GetExitCodeProcess every 10 ms. On exit it sends two RMIs to AgentManager, then relaunches if the per-server revive flag (+0xd5) or the global flag (+0x171) is set
Kill (0x140009780) Toolhelp32 snapshot, case-insensitive image-name match, terminates the first match only (TerminateProcess(h,0))
Main loop (0x1400037a0) 100 ms tick, 1 s FPS counter. Optionally auto-starts servers after a configured delay (+0x178). At startup it kills a stale process of type 0x18 (identity unknown)

Win32 → Linux substitutions

Original (address) Win32 Linux replacement Behaviour difference
Single instance (0x1400100b0) CreateMutexW("Global\\RustyHearts_Agent") flock(LOCK_EX|LOCK_NB) on /tmp/RustyHearts_Agent.lock Lock file instead of a kernel object; still released on exit or crash
Launch (0x1400094e0) CreateProcessW(…, CREATE_NEW_CONSOLE, …, workDir) + STARTUPINFOW console geometry posix_spawnp + POSIX_SPAWN_SETSID + posix_spawn_file_actions_addchdir_np (glibc ≥ 2.29) No console window; the command line is split on whitespace (no Windows quoting rules)
Watcher (0x140008c90) GetExitCodeProcess polled every 10 ms blocking waitpid on a detached thread No polling. Reaping fixes the original handle leak; passing the pid by value fixes the original stack-pointer race
Kill (0x140009780) Toolhelp32 + OpenProcess + TerminateProcess(h,0) scan /proc/*/cmdline for the argv[0] basename with strcasecmp, then kill(SIGKILL) Same rule: first match only, case-insensitive
Main-loop timer QueryPerformanceCounter (TimeSourceHRWin32) std::chrono::steady_clock —
Thread start _beginthreadex std::thread —
Sleep Sleep std::this_thread::sleep_for —
Shutdown console menu (FUN_140001dc0, not analysed) SIGINT / SIGTERM Different trigger

Original bugs (O)

  • Process handle leak: the watcher never closes the process handle.
  • Stack-pointer race: LaunchProcess hands the watcher a pointer to a stack PROCESS_INFORMATION. It is only safe because of the Sleep(1000) that follows.
  • Snapshot handle leak: KillProcessByName leaks the snapshot handle on the success path.

Unknown / not recovered

  • ProudNet RMI to AgentManager and the game servers: proprietary transport. Wire-incompatible with ProudNet 1.8 (see the ProudNet section of third-party/README.md). The proxy vtable slots used are +0x40 and +0x50.
  • XML config field mapping for the Agent.
  • FUN_140027540 argument builder; the type == 1 exit follow-up (FUN_140009f20); the type 0x18 process killed at startup.
  • Locking around the server table: not visible in the analysed code.