---
title: "Horse Museum: Taking Apart a Private Star Stable Launcher Distributed over Tor"
description: "An onion link, a setup.exe, and a signed cfg.hors profile looked like a stealer campaign. The files turn out to be a private Star Stable Online client: a launcher with bundled Tor, a winmm.dll proxy, an encrypted game package, and a game server address that is replaced at login."
date: 2026-09-28
category: "Offensive Security"
tags: ["malware", "star-stable", "windows", "tor", "nsis", "dll-hijack"]
---

This post analyzes an installer that was handed out as an invite to a private Star Stable Online server. The delivery, an onion link serving an executable and an opaque config file, has every trait of a stealer campaign. Taking apart the installer, the launcher, the signed profile, and the encrypted game package shows something else: a private-server client that talks to its account backend over Tor, proxies `winmm.dll` into the game, patches the client in memory, and only learns the real game server address after login.

## An Invite That Looks Like a Malware Drop

The invite consists of a code and short instructions: install Tor, open the link, sign in, download `setup.exe` and `cfg.hors`, run the installer, then import the config. The word "Link" is a hyperlink to `http://2slqbzmg7k56iwrmbxensn7nrjufubtdbhpanbix5gwusn53vpl454yd.onion/`.

An onion site that serves an installer together with a second, unexplained file is the typical shape of a malware drop. The working hypothesis for the analysis was therefore a credential stealer using a horse game as the lure.

The onion site itself is a minimal, well-structured HTML page. The Content-Security-Policy header restricts all resources to `'self'`, and the page links to a Discord server (`discord.gg/horseempire`), a download page, and a login page. Registration is invite-only: the `/api/registration/status` endpoint returns `{"mode": "invite"}`.

## Inside the NSIS Installer

`setup.exe` is a Nullsoft Scriptable Install System package [1] of 18,122,287 bytes.

SHA-256: `4e5129b0b02835ceb7de51ab572eb89e2c430fa3ff1ab105708f28987d6e99f0`

7-Zip reads the archive: solid Deflate compression, 33,704 bytes of headers, and a 109,568-byte stub. It contains the following members:

| Name | Size |
| --- | --- |
| `modern-wizard.bmp` | 26,494 |
| `$PLUGINSDIR/nsDialogs.dll` | 9,728 |
| `$PLUGINSDIR/System.dll` | 12,288 |
| `HorseLauncher.exe` | 1,904,640 |
| `tor.exe` | 10,222,592 |
| `$PLUGINSDIR/runtimes.exe` | 13,280,976 |

The wizard bitmap and the two plugin DLLs belong to the installer UI. The actual payload is `HorseLauncher.exe` and a complete copy of `tor.exe`. The sixth member, `runtimes.exe`, is a nested NSIS installer that bundles the game's runtime dependencies (see below).

Because the archive is a single solid block, 7-Zip attributes the entire compressed size to the first file, and no member can be extracted on its own. 7-Zip versions before 23.01 abort on this archive with `BadCmd=13` when trying to list it; later versions extract all six members. Cutting even two megabytes off the end makes the whole archive unreadable, so the installer cannot be trimmed below a 16 MB sandbox upload limit.

### runtimes.exe: a Second NSIS Installer Inside the First

`runtimes.exe` is itself a 13,280,976-byte NSIS installer. Its manifest requests `requireAdministrator` execution level. Extracting it reveals three files:

| Name | Size | Signed by | Countersign date |
| --- | --- | --- | --- |
| `vc14_x86.exe` | 6,941,536 | Microsoft Corporation | 2026-05-27 |
| `vc2013_x86.exe` | 6,510,136 | Microsoft Corporation (MOPR) | 2017-05-24 |
| `dxwebsetup.exe` | 295,320 | Microsoft Corporation | 2021-02-17 |

All three carry valid Authenticode signatures from Microsoft. The NSIS script disassembly confirms what they do: `vc14_x86.exe` is installed with `/install /quiet /norestart`, `vc2013_x86.exe` with the same flags, and `dxwebsetup.exe` with `/Q`. The script checks the exit code of each, showing `"Could not start Visual C++ runtime"` or `"DirectX web installer failed (code $0). Check your Internet connection and try setup again."` on failure.

The outer installer calls `runtimes.exe` via **ShellExecuteExW** with the `runas` verb, waits for it with **WaitForSingleObject**, and checks the exit code. If it fails, the user sees `"Game runtimes were not installed (code $0). Run setup again and approve the administrator prompt, or uncheck runtimes if they are already installed."` There is no code in `runtimes.exe` beyond the three Microsoft redistributable installers.

### The Outer Installer Script

Disassembly of the outer NSIS script shows what `setup.exe` does beyond extracting files:

1. Writes `HorseLauncher.exe` and `tor.exe` to `$INSTDIR`.
2. Calls `kernel32::CreateFileW` on `HorseLauncher.exe` to verify the install directory is writable. If not: `"Cannot update this file. Close the launcher and try again, or choose a writable folder."`
3. Runs `runtimes.exe` elevated (see above).
4. Creates a desktop shortcut (`Horse Launcher.lnk`) and a Start Menu entry under `Programs\HorseLauncher`.
5. Writes an uninstall entry under `HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall\HorseLauncher` with `DisplayName`, `UninstallString`, `DisplayIcon`, `EstimatedSize` (12099 KB), and `NoModify`/`NoRepair` set to 1.
6. Calls **IsWow64Process2** and **IsWow64Process** to detect the architecture.

The script does not touch any browser profile, run key, scheduled task, or service. It does not download anything at install time.

## The Launcher PE

`HorseLauncher.exe` is a 64-bit GUI PE [2] of 1,904,640 bytes. It is not packed; section entropy ranges from 4.68 (`.data`) to 6.95 (`.rsrc`), consistent with plain compiled code plus embedded resources. The seven sections are `.text`, `.rdata`, `.data`, `.pdata`, `.fptable`, `.rsrc`, and `.reloc`.

SHA-256: `93f83d0ead20be789340a3edd26557416c425e09fb2250077293a57ea58c1337`

The import table references 15 DLLs: KERNEL32 (160 functions), USER32 (50), WS2_32 (36), CRYPT32 (18), bcrypt (15), ADVAPI32 (12), WINHTTP (12), winsqlite3 (9), GDI32 (6), ole32 (4), Cabinet (3), COMCTL32 (3), IPHLPAPI (2), SHELL32 (2), and Secur32 (1). The launcher uses **cpp-httplib** as an embedded HTTP server (the demangled C++ symbols are visible in the binary) and **libcurl** for outbound requests, including aws-sigv4 support for the SwissTransfer download authentication.

The program goes by several names. Its version resources say Horse Launcher 2.0 and Star Riders Launcher 1.0. The invite video shows a Horse Museum logo on the Tor Browser start page, and Horse Museum is also the directory the launcher creates for Tor: `%LOCALAPPDATA%\HorseMuseum\Tor`, resolved through `FOLDERID_LocalAppData` [3]. The server profile calls itself Horse Empire. All of these names refer to the same program.

## No Stealer Code in the Launcher

The first pass targeted the usual stealer surface, and none of it is present. `HorseLauncher.exe` has no references to Chrome, Firefox, Discord, Telegram, or cryptocurrency wallets, no Run key persistence, and no **WriteProcessMemory** or **CreateRemoteThread**.

The few suspicious-looking pieces have mundane explanations. The only SQL-like string is `launcher_hash`, read from a JSON blob as part of a self-check. **CryptProtectData** is imported because the launcher can remember the account password, which it stores as `RememberedPassword` under `HKCU\Software\StarRiders\Launcher`. The launcher can write text to the clipboard, but it never reads from it.

The launcher also knows the SHA-256 hash of the bundled `tor.exe` (`60c45b01938c799862e511a9a5bab12f959a819c6264a24502edc342165f570c`, matching the actual file) and checks it before starting the Tor process.

**The launcher contains none of the stealer functionality the delivery suggested.** What it does contain is a client for a private game server.

## Three Pairs of Embedded DLLs

`HorseLauncher.exe` embeds three pairs of 32-bit DLLs as RT_RCDATA resources, one pair for each supported client year. The resource IDs follow a consistent scheme: `2014` + `1` or `2` for 2014, `2018` + `1` or `2` for 2018, `2020` + `1` or `2` for 2020. The first resource in each pair is `winmm.dll`, the second is `patch.dll`.

| Resource ID | Role | Size | Exports | Key imports |
| --- | --- | --- | --- | --- |
| 20141 | 2014 winmm.dll | 50,688 | 194 WinMM functions | KERNEL32, USER32, bcrypt, WININET |
| 20142 | 2014 patch.dll | 22,016 | 4 ordinal exports | KERNEL32 only |
| 20181 | 2018 winmm.dll | 51,200 | 195 WinMM functions | KERNEL32, bcrypt, WININET |
| 20182 | 2018 patch.dll | 20,480 | 4 ordinal exports | KERNEL32 only |
| 20201 | 2020 winmm.dll | 90,624 | 194 WinMM functions | KERNEL32, bcrypt, WININET |
| 20202 | 2020 patch.dll | 16,896 | 4 ordinal exports | KERNEL32 only |

All six are x86 PE32 DLLs. All three `winmm.dll` variants contain the strings `HORSE_HTTP_PORT`, `127.0.0.1`, and `http://`. The 2018 and 2020 variants additionally contain `whatsnew.starstable.com` and `whatsnew.stage.starstable.com`; the 2014 variant does not. The 2014 variant uniquely imports `USER32.dll` for **GetAsyncKeyState**; the 2018 and 2020 variants do not import USER32 at all for the proxy itself (the launcher imports it separately).

All three `patch.dll` variants export four functions by ordinal only, import only KERNEL32 and the VC++ runtime, and contain the strings `VirtualProtect`, `VirtualAlloc`, and `FlushInstructionCache`. None of the six DLLs has any network imports beyond WININET in the `winmm.dll` variants.

The `winmm.dll` proxies export the real WinMM functions and forward them to the system copy, which they load from System32. Windows searches the application directory before System32 [4], so `SSOClient.exe` loads the proxy instead of the real library. This is the standard [DLL proxy](/blog/dll-proxy-framework) technique: the host keeps working, and the proxy's code runs first.

The proxy uses **WININET** to talk to `127.0.0.1` on the port named by `HORSE_HTTP_PORT`. Where present, the `whatsnew` hostnames cause the patched client to redirect its news requests away from the official Star Stable servers.

`patch.dll` is the memory-patching component. Its three notable KERNEL32 imports, **VirtualProtect**, **VirtualAlloc**, and **FlushInstructionCache**, are the standard combination for rewriting executable code in a running process. The DLL expects to run inside `SSOClient.exe` or `PXStudioRuntimeMMO.exe`.

## Tor for Accounts, Direct TCP for the Game

The profile parser accepts only a v3 onion address as the account host: 56 base32 characters followed by `.onion` [5]. A regular domain is rejected. The launcher then starts the bundled `tor.exe` with `--DataDirectory`, `--SocksPort`, `--ControlPort`, `--CookieAuthentication`, `--CookieAuthFile`, `--ClientOnly`, and `--SafeSocks`, and routes login, downloads, and code redemption through it. The launcher monitors the Tor control port for `Bootstrapped 100%` and `status/circuit-established=1` before enabling login.

The game connection does not use Tor. For each client year the profile holds a host and a port, which the launcher hands to the game as **NetworkServer** and **NetworkServerPort**. **NetworkLauncherServer** is set to `127.0.0.1` and **UseNewServerConnect** to 1, so the game talks to the launcher on localhost. The launcher talks to the real server and passes a **NetworkTicket** once it has one.

These connections are **SOCK_STREAM** / **IPPROTO_TCP** sockets. The only other socket is an IPv6 **SOCK_DGRAM** socket that is created and immediately closed, which is a UDP availability check rather than part of the game protocol. The embedded DLLs import neither **sendto** nor **recvfrom**.

## The Signed and Encrypted cfg.hors Profile

`cfg.hors` is 4,205,180 bytes. The file layout is:

```text
0x00  magic "HORS" (0x53524F48)
0x04  version (u32, must be 1)
0x08  ciphertext length (u32, file size minus 104)
0x0C  12-byte AES-GCM nonce
0x18  ciphertext
      16-byte AES-GCM tag
      64-byte ECDSA P-256 signature
```

The last 64 bytes are an ECDSA P-256 signature over everything before them. The public key ships inside the launcher as a BCrypt blob with magic `0x31534345` (`ECS1`, `BCRYPT_ECDSA_PUBLIC_P256_MAGIC`), key size `0x20`, and the X and Y coordinates. The algorithm string is `ECDSA_P256`.

The payload is encrypted with AES-256-GCM via **BCryptDecrypt** using the `ChainingModeGCM` chaining mode [6]. The first 24 bytes of the file (magic, version, and ciphertext length) serve as additional authenticated data, so the header is covered by the tag. The 32-byte key is a constant at file offset `0x1438F0` in `HorseLauncher.exe`:

```text
378aa25e7de20d75caa84957b795858fefd395c4274868ff6f137ba2ec67699a
```

Decryption yields 4,205,076 bytes of plaintext. A truncated file fails the tag check. The plaintext structure is:

```text
u32    version (1)
lp     profile id         "horse-empire"
lp     profile name       "Horse Empire"
lp     account host       "2slqbzmg7k56iwrmbxensn7nrjufubtdbhpanbix5gwusn53vpl454yd.onion"
u16    account port        80
lp     api path           "/api/launcher/"
lp     note               "Thanks for the Discord art Bunni!"
u8     game count          3
       for each game:
         u16   year number
         lp    year string
         lp    game host
         u16   game port
u8     image count          4
       for each image:
         u8    type index (0, 1, 2, 3)
         u32   size
         raw   image data
```

Where `lp` is a uint16 length prefix followed by that many bytes. The three game entries:

| Year | Host | Port |
| --- | --- | --- |
| 2020 | `77.233.223.176` | 20000 |
| 2018 | `77.233.223.176` | 18000 |
| 2014 | `77.233.223.176` | 13000 |

The four images are two JPEGs (237 KB and 3,528 KB) and two PNGs (54 KB and 288 KB). There is no executable code in the profile. The plaintext ends exactly after the last image, with zero bytes remaining.

`77.233.223.176` belongs to Timeweb in Russia (`77.233.223.0/24`, AS9123, netname `TW-Cloud`). TCP connections to ports 80, 443, 13000, 18000, and 20000 were refused, and UDP probes to the three game ports received no response.

## The Launcher API

The launcher's strings reveal a comprehensive REST API routed through Tor to the onion backend. The endpoints fall into four categories:

**Launcher endpoints** (all under `/api/launcher/`):

| Endpoint | Method | Purpose |
| --- | --- | --- |
| `login` | POST | Returns `token`, `uid`, `username`, `star_coins`, `subscription_level`, `game_master`, `moderator`, and all game server addresses |
| `session` | POST | Validates an existing token, returns the same fields as login |
| `logout` | POST | Invalidates the session token |
| `download` | POST | Returns a SwissTransfer URL, file size, SHA-256 hash, and 32-byte AES key for the requested game year |
| `integrity` | POST | Accepts `game` and `build_id`, returns whether the client files are up to date |
| `launch-ticket` | POST | Returns a `ticket` string after integrity verification passes |
| `code-history` | POST | Returns the user's redeemed code history |
| `invites` | POST | Returns the user's invitation list |
| `redeem` | POST | Redeems a gift code |
| `change-password` | POST | Changes the account password |

**Web endpoints** (under `/api/web/`): `login`, `session`, `logout`, `download`, `payments/create`, `payments/history`, `reseller/create`, `reseller/history`. These are CORS-restricted; requests from outside the site return `"Origin is not allowed."`.

**Game relay endpoints** (under `/launcher/`): `game_data/`, `update_ticket/`, `reserve_character_name/`, `create_character_horse/`. These appear to be proxied to the game server.

**Other endpoints**: `/api/register`, `/api/profile` (returns character creation defaults including `first_name`, `horse1`, `face_style`, etc.), `/api/registration/status`, `/api/security/manifest`, `/api/archive-options`, `/api/n/` (news).

## Login Replaces the Game Server Address

The login endpoint takes form-encoded `username` and `password` at `/api/launcher/login`. The response includes the session token, account metadata, and all game server addresses:

```json
{
  "ok": true,
  "token": "286db87c...25d7b",
  "uid": 2007,
  "username": "victorspet",
  "star_coins": 0,
  "subscription_level": 70,
  "game_master": false,
  "moderator": false,
  "server_host": "162.35.243.182",
  "server_port": "20000",
  "game_2020_host": "162.35.243.182",
  "game_2020_port": "20000",
  "game_2014_host": "162.35.243.182",
  "game_2014_port": "13000",
  "game_2018_host": "162.35.243.182",
  "game_2018_port": "18000"
}
```

**The game server address in the signed profile (`77.233.223.176`, Timeweb, Russia) is stale. The login response overwrites it with `162.35.243.182`.** The ports remain the same. A session token validation via `/api/launcher/session` returns identical fields, confirming the token is stateless with respect to address resolution.

`162.35.243.182` falls in the range `162.35.242.0` to `162.35.243.255`, netname `moula-world-llc`, country `US`, origin AS212477 [7]. TCP connections to the three game ports timed out after 8 seconds. Unlike the Timeweb host, which refused connections immediately, this host silently dropped them.

## The Integrity and Ticket Flow

The launch sequence requires three steps after login:

1. **Download**: POST to `/api/launcher/download` with `token` and `game` (e.g., `2020`). The response includes a SwissTransfer URL, the package size, its SHA-256 hash, and a 32-byte AES-256-GCM key.

2. **Integrity check**: POST to `/api/launcher/integrity` with `token`, `game`, and `build_id`. The server compares the build ID against its current manifest. If the client is outdated, it returns `{"ok": false, "update_available": true}`. If current, it returns `{"ok": true}`.

3. **Launch ticket**: POST to `/api/launcher/launch-ticket` with `token` and `game`. This only succeeds after a passing integrity check. The response is a single-use ticket: `{"ok": true, "ticket": "363a4b7c...6264"}`.

The server publishes integrity manifests at `/api/security/manifest?game=<name>`. These list every file the client directory must contain, one SHA-256 hash per line. The launcher manifest covers only `HorseLauncher.exe` itself. The game manifests list 7 to 9 files each:

**2020 manifest** (9 files): `Data.ccx`, `fmod.dll`, `fmod_distance_filter.dll`, `fmod_gain.dll`, `fmodstudio.dll`, `libxl.dll`, `patch.dll`, `SSOClient.exe`, `winmm.dll`.

**2018 manifest** (7 files): `Data.ccx`, `fmod.dll`, `fmodstudio.dll`, `InputActions.ini`, `patch.dll`, `PXStudioRuntimeMMO.exe`, `winmm.dll`.

**2014 manifest** (8 files): `Data.cch`, `fmod.dll`, `fmodstudio.dll`, `patch.dll`, `PluginData.cch`, `PXStudioRuntimeBrowserDLL.pxzi`, `PXStudioRuntimeMMO.exe`, `winmm.dll`.

Every hash in the 2020 and 2014 manifests matches the corresponding hash from the HORSBOX1 packages and the embedded DLL resources. The server verifies that the launcher and the game files are exactly the versions it distributed.

## The HORSBOX1 Game Package

`setup.exe` does not contain the game itself. The `/api/launcher/download` endpoint returns a SwissTransfer link, a file size, a SHA-256 hash, and a 32-byte key. The download response for each game year on September 29, 2026:

| Game | Size | Variant | SHA-256 (first 16) |
| --- | --- | --- | --- |
| 2020 | 3,398,253,064 | C | `810a3a8dc3a9...` |
| 2018 | 3,025,198,333 | D | `9f1c1b445164...` |
| 2014 | 955,644,456 | A | `402495fe7af4...` |

The 2020 package was downloaded and analyzed for the original article. Its SHA-256 was `b0173cdd55c0ed3bc5ad0f5a473044589a524d7e25ef5058c27bfa15836a9f8f` with variant `A`; the current download returns a different hash with variant `C`, confirming the server rotates packages.

The file starts with the magic `HORSBOX1`. It is neither a ZIP archive nor a client executable with a prepended header, but a custom encrypted container. The launcher reads a 0x48-byte header:

| Offset | Field |
| --- | --- |
| 0x00 | `HORSBOX1` |
| 0x08 | version, u32, value 1 |
| 0x0C | header size, u32, value 0x48 |
| 0x10 | index offset, u64 |
| 0x18 | index length, u64 |
| 0x20 | the same length again |
| 0x28 | 12-byte nonce |
| 0x34 | 16-byte GCM tag |

The index occupies the last bytes of the file. The launcher seeks to the index offset and decrypts it with AES-256-GCM, using the `key` from the download JSON and the first 0x28 header bytes as additional authenticated data. With these inputs the tag verifies.

The decrypted index starts with a u32 version (`1`) and a u32 chunk size (`0x400000`, 4 MiB). Three u32 length-prefixed strings follow: the client year, the release hash (an MD5 hex string), and a single-letter variant. Then a u64 total plaintext size and a u32 file count. Unlike `cfg.hors`, which uses u16 length prefixes, the HORSBOX1 index uses u32 prefixes throughout.

Each file record holds a u32-prefixed name, the plaintext size as u64, a 32-byte SHA-256 hash, and a u32 chunk count followed by that many chunk entries. Each chunk entry is 49 bytes: a u64 file offset, three u32 sizes (ciphertext, plaintext, stored), a u8 flag, a 12-byte nonce, and a 16-byte GCM tag. Every flag in the analyzed packages is 0, indicating no compression. The additional authenticated data for a chunk is `HORSBOX1` followed by five little-endian u32 values (version, file index, chunk index, plaintext size, ciphertext size) and the flag byte.

Every decrypted file matched the SHA-256 hash stored in the index. These hashes are what the "File verification required" error refers to: the launcher hashes the files it has just written and only then requests a ticket.

## Contents of the 2014 Package (Independently Verified)

The 2014 package was downloaded (955,644,456 bytes, SHA-256 `eba302005515fb333490a63080d702b13186711a3df597ed88e8868f3faeca0d`), its index decrypted, all 18,762 files catalogued, and a random sample of 100 files across all file types decrypted and verified against their index hashes. All 100 passed. The eight executables and DLLs were fully extracted and analyzed:

| File | Size | Authenticode | Notes |
| --- | --- | --- | --- |
| `PXStudioRuntimeMMO.exe` | 3,181,616 | No | 32-bit, imports D3D9, DirectSound, FMOD, WinSock, WinINet |
| `winmm.dll` | 50,688 | No | 194 exports, imports bcrypt + WININET; **byte-identical to resource 20141** |
| `patch.dll` | 22,016 | No | 4 ordinal exports, KERNEL32 only; **byte-identical to resource 20142** |
| `fmod.dll` | 1,518,080 | No | FMOD 1.x, 1088 C++ exports |
| `fmodstudio.dll` | 1,075,200 | No | FMOD Studio, 341 exports, links fmod.dll |
| `Plugins/fmod_distance_filter.dll` | 9,216 | No | FMOD plugin, single export |
| `Plugins/fmod_gain.dll` | 8,704 | No | FMOD plugin, single export |
| `PXStudioRuntimeBrowserDLL.pxzi` | 2,510,839 | n/a | Not a PE; compressed game data |

Two additional `.dylib` files (macOS builds of the FMOD plugins, 42 KB and 41 KB) are included but unused on Windows.

`PXStudioRuntimeMMO.exe` imports 12 DLLs: KERNEL32, USER32, GDI32, SHELL32, WININET, WINMM, WS2_32, DSOUND, d3d9, d3dx9_36, COMDLG32, and ole32. The SHELL32 import is **ShellExecuteA** (for opening URLs, standard in game clients). The WININET imports are **InternetOpenA**, **InternetConnectA**, **HttpOpenRequestA**, **HttpSendRequestA**, **InternetReadFile**, and **InternetCloseHandle**, covering HTTP asset requests. There are no imports for **CreateRemoteThread**, **WriteProcessMemory**, or any injection API. Section entropy ranges from 4.11 (`.data`) to 6.58 (`.rsrc`), consistent with unpacked native code.

The remaining 18,752 files are game data: `.pte` textures (6,799 files), `.pmt` material definitions (6,485), `.pme` meshes (2,440), `.pan` animations (1,163), `.pso` shaders (997), `.pxo` objects (341), `.pco` collision (202), `.psm` scene maps (185), `.wav` audio (101), and smaller counts of `.pxw`, `.pfo`, `.ed`, `.tei`, `.ter`, and `.cch` files. No file with a game-data extension contained a PE header or suspicious strings (cmd.exe, powershell, mimikatz, keylog, stealer) in its first 4 KB.

**The 2014 package is the Star Stable 2014 client (PXStudio engine build) plus the same winmm.dll proxy and patch.dll that the launcher carries as embedded resources. No additional executables, no stealer code, no unexpected binaries.**

## Contents of the 2020 Package

The relevant files in the package:

| File | Size | SHA-256 |
| --- | --- | --- |
| `SSOClient.exe` | 7,677,576 | `ac54e8321c8bb20c6323dae7c84436b80c77a39b249b2940e967666b376e56bd` |
| `winmm.dll` | 90,624 | `12170bd9431163d765f1a8c4f828ddaaa1abe4cde761e3f08d2d3a651ffd8cc8` |
| `patch.dll` | 16,896 | `f21da892593900e13a57148fdf567100554c9d961533b20b19789ba8be643ab7` |
| `fmod.dll` | 1,518,080 | `7665458ae8805ac7b6ca719c973235c518aad189d56579537413e11159f97da5` |
| `libxl.dll` | 6,517,760 | `1919cee80b6e63bcf61071e7e441a68873a27ac1cf856d3cee90baca23928bb6` |
| `crashpad_handler.exe` | 741,888 | `9c1bf63828413cdb5f176072e4453d7c0516bce34f7883b687e7ba7352fc85fb` |
| `Data.ccx` | 4,578,029 | `cc87b021a2bd02a071b8fe5f973b99b9b6e89eea0a47a37c2c8ab1ce281be98d` |

`SSOClient.exe` is a PE32 binary with CompanyName Star Stable Entertainment AB, ProductName Star Stable Online, FileVersion 51.95333, and ProductVersion PXEngine 51. It contains the build path `PXRelease2020_51\Projects\StarStableOnline`, imports `WINMM.dll`, FMOD, OpenGL32, and WinHTTP, and has a `CPADinfo` section. Its OpenGL 3.0 warning string matches the text of the official client.

`winmm.dll` and `patch.dll` are byte-identical to the 2020 pair embedded in `HorseLauncher.exe` as resources 20201 and 20202, with the same `HORSE_HTTP_PORT`, `http://127.0.0.1:`, and news host strings and the same three memory imports in `patch.dll`.

The remaining program files are the usual dependencies of the 2020 client. `fmod.dll` is FMOD 1.10.1 from Firelight Technologies, accompanied by `fmodstudio.dll` and two small FMOD plugins; Mach-O `.dylib` builds of those plugins sit in the same tree. `crashpad_handler.exe` is Chromium's crash reporter. `libxl.dll` is LibXL 3.8.1.0 from XLware, and `SSOClient.exe` already references it by name.

The bulk of the package is game data: `Data.ccx` and `PackFiles/p_00000000.csa` through `p_00000012.csa`, each with a matching `.csaheader`. The headers list scenes, animations, and models (`.scene`, `.pan`, `.pme`, `.pte`) and do not reference any additional executable.

**The package is the 2020 Star Stable client plus the same two DLLs the launcher already carries.** None of the 40 files contains a separate stealer.

## Host Artifacts

For identifying an installation on a machine:

| Artifact | Location |
| --- | --- |
| Saved password | `RememberedPassword` under `HKCU\Software\StarRiders\Launcher` |
| Server profiles | `HKCU\Software\HorseLauncher\Profiles\` |
| Mutex | `Local\HorseLauncher.Profile.` |
| Profile import filter | `*.hors` |
| Tor data directory | `%LOCALAPPDATA%\HorseMuseum\Tor` |
| Uninstall entry | `HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall\HorseLauncher` |
| Desktop shortcut | `Horse Launcher.lnk` |
| Start menu entry | `Programs\HorseLauncher` |

The game process receives `NetworkServer`, `NetworkServerPort`, `NetworkTicket`, `NetworkUserName`, `NetworkUserId`, `NetworkLauncherServer`, `NetworkLauncherHash`, and `UseNewServerConnect` from the launcher.

## Assessment

Despite its delivery, the installer is a private Star Stable Online client and not a stealer. Every component has been accounted for:

- The outer NSIS installer writes the launcher, Tor, and runtime redistributables. Its script creates shortcuts and an uninstall entry; it touches no browser profiles, run keys, or services.
- The nested `runtimes.exe` contains only three Microsoft-signed redistributables (VC++ 2013, VC++ 2015-2022, DirectX).
- The launcher binary imports 15 DLLs, none of which are associated with credential theft. Its embedded DLLs are `winmm.dll` proxies and in-memory patchers, not injectors.
- The `cfg.hors` profile contains server metadata and four images, no code.
- The HORSBOX1 2014 game package was independently downloaded (955 MB), its custom encrypted container format reverse-engineered, and all 18,762 files catalogued. A random sample of 100 files was decrypted and verified against the index hashes with zero failures. All eight executables and DLLs were extracted and analyzed: the game engine (`PXStudioRuntimeMMO.exe`), FMOD audio libraries, and the same `winmm.dll` proxy and `patch.dll` that the launcher embeds. No hidden executables, no stealer code, no suspicious strings in any sampled file.
- The 2020 package contains the same pattern: the Star Stable 2020 client (`SSOClient.exe` with PXEngine 51) plus the same proxy DLLs.
- The `winmm.dll` and `patch.dll` files from the 2014 package are byte-identical (SHA-256 match) to the ones embedded in the launcher as resources 20141 and 20142. The same holds for the 2020 pair.
- The API surface covers account management, game downloads, integrity checking, and launch tickets. There are no endpoints for exfiltrating browser data, Discord tokens, or wallet files.
- The server enforces file integrity through SHA-256 manifests and issues launch tickets only after verification passes.

The game server address in the signed profile is a placeholder. The onion backend replaces it at login, and it has already moved once (from Timeweb in Russia to Moula World LLC in the US). The download packages rotate variants. The backend can change the game server address, the download links, or the package contents at any time without touching `setup.exe` or `cfg.hors`.

Both game server addresses were unreachable during testing (the Timeweb host refused connections, the US host silently dropped them), so the game protocol itself was not observed.

*Drafted with LLM assistance from `setup.exe`, `HorseLauncher.exe`, `cfg.hors`, the onion backend API (login, session, download, integrity, ticket, manifest, profile, and registration endpoints queried on September 29, 2026), the 2014 `HORSBOX1` package (955 MB, independently downloaded, decrypted, and verified: all 18,762 files catalogued, 100-file random sample passed SHA-256 verification, all executables extracted and analyzed), and the 2020 `HORSBOX1` package. Reviewed against the samples.*

## References

[1] Nullsoft Scriptable Install System, https://nsis.sourceforge.io/Main_Page

[2] Microsoft, PE Format, https://learn.microsoft.com/en-us/windows/win32/debug/pe-format

[3] Microsoft, KNOWNFOLDERID, `FOLDERID_LocalAppData`, https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid

[4] Microsoft, Dynamic-Link Library Search Order, https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order

[5] Tor Project, Onion services, https://community.torproject.org/onion-services/overview/

[6] Microsoft, BCryptDecrypt, https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptdecrypt

[7] RIPE Database query for 162.35.243.182, https://apps.db.ripe.net/db-web-ui/query?searchtext=162.35.243.182
