Romero's Aftermath API

I've recently started playing Romero's Aftermath, the "spiritual successor" to Infestation: Survivor Stories. I decided to see how the game handles logging in, converting GC to $ and quickly ended up documenting most of the API calls I have seen. Any improvement is appreciated, decoding the binary data returned by certain endpoints, or finding new ones.

Tools required

  • Proxifier (since Aftermath doesn't respect the System Proxy rule)
  • Charles Proxy
  • Wireshark
  • PHP
  • Curl

User login

  • URL: https://api.willyousurvive.com/aftermath/api/api_Login.aspx
  • Request type: POST

Request parameters

  • username: User's email address
  • password: User's password

Response

Success

Example: WO_01100631 590520624 100

  • 1100631 is later referred to as s_id and remains the same between two sessions
  • 590520624 is later referred to as s_key and changes on each new session
  • 100 means that the account is OK, 200 means that the account is banned

Error

The following value is returned when the username and / or password is incorrect: WO_00 0 0

PHP Example

<?php

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, "https://api.willyousurvive.com/aftermath/api/api_Login.aspx");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POSTFIELDS, array("username" => "example@domain.tld", "password" => "examplePassword"));

$data = curl_exec($ch);

var_dump($data);

?>

Session polling

The session is kept active server-side by polling the server every minute

  • URL: https://api.willyousurvive.com/Aftermath/api/api_LoginSessionPoller.aspx
  • Request type: POST

Request parameters

  • s_id: Permanent session ID
  • s_key: Temporary session key

Response

Success

WO_0

Error

WO_1

PHP Example

<?php

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, "https://api.willyousurvive.com/Aftermath/api/api_LoginSessionPoller.aspx");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POSTFIELDS, array("s_id" => 1100631, "s_key" => 590520624));

$data = curl_exec($ch);

var_dump($data);

?>

Fetching shop data

  • URL: https://api.willyousurvive.com/Aftermath/api/api_GetShop2.aspx
  • Request type: POST

Request parameters

  • s_id: Permanent session ID
  • s_key: Temporary session key

Response

This endpoint returns a heap of binary data, maybe some kind of object serialization ?

Note

You can send totally incorrect s_id and s_key values to the API endpoint, or even perform at GET request without the parameters and the same data is returned.

Fetching Game Rewards

  • URL: https://api.willyousurvive.com/Aftermath/api/api_GetDataGameRewards.aspx
  • Request type: POST

Request parameters

  • 411: 1 (Don't ask me why)

Response

<?xml version="1.0"?>
<rewards><rwd ID="1"
Name="Zombie Kill"
BP_SOFT="0"
XP_SOFT="5"
BP_HARD="0"
XP_HARD="5"
/>
<rwd ID="2"
Name="Super Zombie Kill"
BP_SOFT="0"
XP_SOFT="250"
BP_HARD="0"
XP_HARD="250"
/>
</rewards>

Note

No authentication required for this endpoint, works as a GET request with no parameters or as a POST request with random parameters, the response is always the same.

Fetching item info

  • URL: https://api.willyousurvive.com/Aftermath/api/api_GetItemsInfo.aspx
  • Request type: POST

Request parameters

  • s_id: Permanent session ID
  • s_key: Temporary session key

Response

This endpoint returns a heap of binary data, maybe some kind of object serialization ?

Note

You can send totally incorrect s_id and s_key values to the API endpoint, or even perform at GET request without the parameters and the same data is returned.

Fetching GS conversion rates

  • URL: https://api.willyousurvive.com/Aftermath/api/api_GSConvert.aspx
  • Request type: POST

Request parameters

  • s_id: Permanent session ID
  • s_key: Temporary session key
  • func: rates

Response

Success

<?xml version="1.0"?>
<rates>
<r GS="700"
Rate="80"
/><r GS="9999999"
Rate="90"
/></rates>
<gs>
<r r0="1000"
r1="140"
r2="3000"
r3="150"
r4="999999"
r5="160"
/></gs>

Error

  • WO_1 is returned when the session id / key is invalid / incorrect / expired
  • WO_5no parameter func is returned when the func parameter is missing
  • WO_5bad func is returned when the func parameter is incorrect

Notes

This endpoint actually verifies the parameters passed in, it's a first, congratulations.

Fetching profile data

  • URL: https://api.willyousurvive.com/Aftermath/api/api_GetProfile.aspx
  • Request type: POST

Request parameters

  • s_id: Permanent session ID
  • s_key: Temporary session key

Response

Success

This endpoint returns a heap of binary data, maybe some kind of object serialization ?

Error

  • WO_1 is returned when the session id / key is invalid / incorrect / expired

Authenticating with the Account Frontend

  • URL: http://account1.willyousurvive.com/FrontendAccount/inner_buygc.php
  • Request type: GET

Request parameters

  • AfAuth: This one is interesting, it's a base64 encrypted string (Example: d3d2dnB1d3xzf3ZzdHZwdHI=). Explained in detail in notes below

Response

Success

The account frontend page is returned with a new cookie: PHPSESSID which is a randomly generated PHP session. From here you can view transaction history, buy GC, and redeem a code whenever that function becomes available.

Error

  • no auth if AfAuth is not provided / is invalid
  • login failed if AfAuth is an incorrect set of keys

Notes

This endpoint uses some home-made encoding techniques: when decoding d3d2dnB1d3xzf3ZzdHZwdHI= from base64 we find the following string:
wwvvpuw|svstvptr, looks cryptic right ? Wrong.
Taking a look at the s_id: 1100631 and s_key: 590520624 used in the previous requests we can quickly notice that the "cryptic" string is just the two session parameters converted to letters separated with a |. Here's the correspondance chart:

- => k
0 => v
1 => w
2 => t
3 => u
4 => r
5 => s
6 => p
7 => q
8 => ~
9 =>  (DEL character)

PHP Example

<?php

function digitToChar($digit) {
    switch($digit)
    {
        case '-':
            return "k";
        case 0:
            return "v";
        case 1:
            return "w";
        case 2:
            return "t";
        case 3:
            return "u";
        case 4:
            return "r";
        case 5:
            return "s";
        case 6:
            return "p";
        case 7:
            return "q";
        case 8:
            return "~";
        case 9:
            return chr(127);
    }
}

function getAfAuth($s_id, $s_key) {
    $afauth = "";

    foreach(str_split($s_id) as $i) {
        $afauth .= digitToChar($i);
    }

    $afauth .= chr(124);

    foreach(str_split($s_key) as $i) {
        $afauth .= digitToChar($i);
    }

    return base64_encode($afauth);
}

echo(getAfAuth(1100631, 590520624));

?>

Fetching user balance

  • URL: https://api.willyousurvive.com/Aftermath/api/api_GSConvert.aspx
  • Request type: POST

Request parameters

  • s_id: Permanent session ID
  • s_key: Temporary session key
  • func: balance

Response

Success

WO_0250 where 250 is the user's GC balance

Error

  • WO_1 is returned when the session id / key is invalid / incorrect / expired
  • WO_5no parameter func is returned when the func parameter is missing
  • WO_5bad func is returned when the func parameter is incorrect

Converting GC to $

  • URL: https://api.willyousurvive.com/Aftermath/api/api_GSConvert.aspx
  • Request type: POST

Request parameters

  • s_id: Permanent session ID
  • s_key: Temporary session key
  • func: convert
  • GS: Number of GC to convert (Example: 10)

Response

Success

WO_01224 where 1224 is the new $ balance

Error

  • WO_6 not enough GS when you try and convert more GC than you actually have
  • WO_6 sneaky bastard... when you try to convert negative values (made me laugh)

Notes

Posting with GS = 0 allows you to get your $ balance, but adds an entry to the transaction log "Converted to 0 Battle Points"

Changing skin / hair / body / legs / feet

  • URL: https://api.willyousurvive.com/Aftermath/api/api_CharSlots.aspx
  • Request type: POST

Request parameters

  • s_id: Permanent session ID
  • s_key: Temporary session key
  • func: skin
  • CharID: The ID of the user's character (Example: 230901)
  • HairIdx: The ID of the hair model (Example: 2)
  • HeadIdx: The ID of the head model (Example: 0)
  • BodyIdx: The ID of the body model (Example: 14)
  • LegsIdx: The ID of the leg model (Example: 5)
  • FeetIdx: The ID of the feet model (Example: 0)
  • HairColor: The color code for the user's hair (Example: -1)
  • SkinColor: The color code for the user's skin (Example: -8252)

Response

Success

WO_0

Error

  • WO_5bad func when the func is incorrect
  • WO_6 bad charid when the CharID is incorrect

Notes

Only s_id, s_key, func and CharID are verified server-side, the other parameters can take invalid values and the success code is still returned. Warning: Invalid values can cause game crashes.

Sending crash reports

  • URL: http://api.willyousurvive.com/aftermath/api/php/api_CrashReport.php
  • Request type: POST

Request parameters

  • appname: Aftermath
  • appversion: 1.0
  • crashguid: 162deeba-9925-4d5d-82a0-53c0b28d39d6
  • crashrptver: 1301
  • description
  • emailfrom
  • emailsubject: Aftermath 1.0 Error Report
  • exceptionaddress: 10254279
  • exceptionmodule: C:\Users\Cuonic\Documents\Aftermath\Aftermath.exe
  • exceptionmodulebase: 4194304
  • exceptionmoduleversion: 0.0.0.0
  • md5: No idea where it comes from
  • crashrpt : ZIP file (Example file: http://www.cuonic.com/files/162deeba-9925-4d5d-82a0-53c0b28d39d6.zip)

Response

200 Success.

Verifying serial keys

  • URL: https://api.willyousurvive.com/aftermath/api/api_AccCheckKey.aspx
  • Request type: POST

Request parameters

  • serial: FREE-AFTERMATH-ALPHA

Response

Success

WO_00 0 :Serial Key is Valid

Error

WO_01 -1 :Serial Key is not valid

User registration

  • URL: https://api.willyousurvive.com/aftermath/api/api_AccRegister.aspx
  • Request type: POST

Request parameters

  • serial: FREE-AFTERMATH-ALPHA
  • username: User's email address
  • password: User's password

Response

Success

WO_01218207 where 1218207 is the new user's s_id

Error

  • WO_3 Serial not valid
  • WO_2 Email already in use

Note

The password parameter can be empty, a new account is still created and login works smoothly...

Leaderboard

  • URL: http://api.willyousurvive.com/aftermath/api/api_LeaderboardGet.aspx
  • Request type: POST

Request parameters

  • s_id: Permanent session ID
  • s_key: Temporary session key
  • CharId: Character ID
  • t (int): Unknown
  • pos (int): Position ?

Response

<?xml version="1.0"?>
<leaderboard pos="0" size="0"></leaderboard>

Note

I have tried multiple values for t and pos but the leaderboard remains empty, maybe it isn't implemented yet.
By inputting incorrect parameter types SQL errors are displayed giving an insight to what's going on behind

Moving items from backpack to Global Storage

  • URL: https://api.willyousurvive.com/aftermath/api/api_CharBackpack.aspx
  • Request type: POST

Request parameters

  • s_id: Permanent session ID
  • s_key: Temporary session key
  • CharId: Character ID
  • op: Operation type (10 to move item from backpack to global storage, 11 to move item from global storage to backpack, 12 to switch backpacks)
  • v1: Item ID
  • v2: Backpack slot
  • v3: Quantity
  • v4: Unknown

Response

Error

WO_6 not in safezone when trying to move to / from global storage when player is outside of the safezone.

Buying items

  • URL: https://api.willyousurvive.com/aftermath/api/api_BuyItem3.aspx
  • Request type: POST

Request parameters

  • s_id: Permanent session ID
  • s_key: Temporary session key
  • ItemID: Item ID
  • BuyIdx: Quantity ?

Response

Error

  • WO_5bad BuyIdx when BuyIdx is invalid (0 or negative numbers)
  • WO_6 bad GetPrice maybe because the item doesn't exist

Other endpoints

  • http://api.willyousurvive.com/aftermath/api/php/api_GetItemsDB.php Always returns oops (GET, POST, HTTP, HTTPS)
  • http://api.willyousurvive.com/aftermath/api/php/api_GetLootBoxConfig.php Always returns oops (GET, POST, HTTP, HTTPS)
  • http://api.willyousurvive.com/aftermath/api/php/dbinfo.inc.php The database configuration file
  • http://api.willyousurvive.com/aftermath/api/api_SrvUploadLogFile.aspx All server related API calls require skey1, a server API key
  • http://api.willyousurvive.com/aftermath/api/api_SrvCharUpdate.aspx
  • http://api.willyousurvive.com/aftermath/api/api_SrvAddWeaponStats.aspx
  • http://api.willyousurvive.com/aftermath/api/api_SrvAddLogInfo.aspx
  • http://api.willyousurvive.com/aftermath/api/api_SrvAddCheatAttempts.aspx
  • http://api.willyousurvive.com/aftermath/api/api_Friends.aspx Apparently broken
  • http://api.willyousurvive.com/aftermath/api/api_ClanMgr.aspx
  • http://api.willyousurvive.com/aftermath/api/api_ClanInvites.aspx
  • http://api.willyousurvive.com/aftermath/api/api_ClanGetStatus.aspx
  • http://api.willyousurvive.com/aftermath/api/api_ClanGetInfo.aspx
  • http://api.willyousurvive.com/aftermath/api/api_ClanCreate.aspx
  • http://api.willyousurvive.com/aftermath/api/api_ClanApply.aspx
  • http://api.willyousurvive.com/aftermath/api/api_AccApplyKey.aspx
  • http://api.willyousurvive.com/aftermath/api/api_GetShop1.aspx Returns binary data

Other findings

  • In-game homescreen: http://account1.willyousurvive.com/homescreen/
  • Launcher version verification URL: https://api.willyousurvive.com/aftermath/launcher/amupd.xml
  • Launcher homescreen with patch information: http://api.willyousurvive.com/aftermath/status/api_getserverinfo.xml
  • Game data version verification URL: https://api.willyousurvive.com/aftermath/launcher/am.xml
  • Server list: game.willyousurvive.com (209.151.247.186)
    Communication is established via UDP on ports 34001 and 34003, unknown protocol
  • EU PVP Novice Server 09 (31.204.158.69)
    Communication is established via UDP on port 34014, unknown protocol