I’ve always been fascinated by the inner workings of games and apps, and I’ve decompiled game clients before. This time, I am going to analyze the communication between a game client and server using a MITM (Man-in-the-Middle) attack.
Context and Motivation
Just like in my last blog post, playing mobile games can be a time-consuming and sometimes costly endeavor if you aim to excel. Back in college, I faced the dilemma of either investing countless hours or spending money to achieve items in the game. This motivated me to explore an alternative.
The game I’m using in this post is Card Crushers, a strategy card game. Click the link to learn more about it.
A bit about this game: It features two types of currencies, diamonds and gold. The primary objective for players is to collect cards through daily quests. These cards can then be used in Player vs. Player (PvP) battles or to conquer dungeons. Strengthening one’s character is achieved solely through collecting and upgrading these cards. Cards come in various rarities, including white, blue, purple, gold, and legendary.
The Idea
The idea is to use a tool to capture all requests and responses between the client and server. Then, I will document the system and analyze all communications to identify interesting parts. In the end, I created an automation Python script to help me get unlimited gold/diamonds and in-game items, which even helped me become a top player on the leaderboard.
Tools and Requirements
- mitmproxy: An open-source web debugging proxy server that allows you to inspect, manipulate, and replay HTTP(S) traffic between your computer and the internet.
Steps
Check out this video to learn how to install and enable a proxy on your Android/iPhone.
Overall steps:
- Install mitmproxy on your PC.
- On your phone, connect to and trust the proxy.
- Play the game as normal to let mitmproxy capture all network traffic.
- Document all requests and responses.
- Analyze the captured data to find the necessary/interesting APIs.
- Create an automation script using Python.
Findings
You can check out all the APIs that I have found here.
Some interesting points about this game:
- Most of the logic is executed through the API ExecuteCloudScript with a parameter of
FunctionName
and the required parameters for this function. - There is a public API for
addUserCurrency
, which basically means I can increase as much gold/diamonds as I want. - If I try to add over 100 diamonds through
addUserCurrency
, I get a message indicating that they know I am a cheater. - There is a public API for
AddChestToPlayer
, which means I can add whichever chest type I want. - All randomness from chests is set when the chest is created, not when you open it.
- My in-game profile was removed since the day I started cheating.
Examples and Code Snippets
Add Currency (Diamonds and Gold)
{
"FunctionName": "addUserCurrency",
"FunctionParameter": {
"currencyAmount": 13,
"currencyName": "CN" //GOLD="CN", DIAMOND="DM"
}
}
{
"code": 200,
"data": {
"Logs": [
{
"Level": "Info",
"Message": "CURRENCY ADD! Name: CN, amount:13"
}
],
},
"status": "OK"
}
From the requests and responses I documented here, I created an automation Python script like this:
def increaseCurrency(currencyName: Currency, goldCount: int):
json_data = {
'FunctionName': 'addUserCurrency',
'FunctionParameter': {
'currencyName': currencyName.value,
'currencyAmount': goldCount,
}
}
data = post_request(REQUEST_URL, HEADERS, json_data)
try:
balance = data["data"]["FunctionResult"]["Balance"]
balance_change = data["data"]["FunctionResult"]["BalanceChange"]
print(f"Balance: {balance}, Balance Change: {balance_change}")
except Exception as e:
print(e)
All similar automation functions are available on my Gitea site.
Add Chest to Player
{
"FunctionName": "AddChestToPlayer",
"FunctionParameter": {
"ChestSlot": 1,
"ChestType": "Wooden", //Wooden, Silver, Gold, Legendary
"Free": false,
"Shop": false,
"Skull": false,
"Time": -1
}
}
{
"code": 200,
"data": {
"Logs": [
{
"Data": {
"card amount": 3,
"chosen card": "Gremlin",
"index in array": -1
},
"Level": "Info",
"Message": "result"
}
]
},
"status": "OK"
}
Match Result
{
"FunctionName": "TmpMatchResult",
"FunctionParameter": {
"PlayerName": "Guest5135043",
"PlayfabId": "928893CFC0C62052",
"bTournament": false,
"bTute": false,
"bWon": true, //can cheat right here to make the result to always be true
"opponentName": "Rampage",
"scoreLeft": 1,
"scoreRight": 0
},
"GeneratePlayStreamEvent": true,
"RevisionSelection": "Live",
"SpecificRevision": null
}
{
"code": 200,
"data": {
"FunctionResult": {
"LoserP": -2,
"WinnerP": 10,
"Won": true,
"opponentName": "Rampage"
}
},
"status": "OK"
}
Some screenshots and GIFs.
Buff of gold |
Buff of diamond |
Increase leaderboad score |
Then get to the top 1 ranking |
Buff of LENGENDARY Chest & skip waiting time |
Collected all Hero Cards |
Some Notes About This Game
- Most logic can be injected via MITM, including
addUserCurrency
,AddChestToPlayer
, andTmpMatchResult
. This is dangerous because anyone could inject scripts to manipulate in-game items and status. - It seems like they are aware of potential cheaters but ignore it, as they respond with an error message when I tried to increase too many diamonds in a single request.
- If I were the developer, I would definitely move all important logic to the backend side and not expose any logic to the API. Also, I would implement obfuscated JSON requests if possible.