3.8 KiB
3.8 KiB
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:
LaunchProcesshands the watcher a pointer to a stackPROCESS_INFORMATION. It is only safe because of theSleep(1000)that follows. - Snapshot handle leak:
KillProcessByNameleaks 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+0x40and+0x50. - XML config field mapping for the Agent.
FUN_140027540argument builder; thetype == 1exit follow-up (FUN_140009f20); the type 0x18 process killed at startup.- Locking around the server table: not visible in the analysed code.