In this series of blogposts, I will be using a mobile game "Egg, Inc." as the target for
demonstration. It's a simple time killer app that got me through boring long waits when I was still at school.
-I won't bore you with details, but in essence it's an incremental game with partial online features
-such as cloud save, co-op contracts and server scheduled boosts.
+Egg, Inc. is a basic incremental "idler" game where your goal is to take over the world food supply with ever-increasing supply of eggs,
+if you have ever played Cookie Clicker, you know the premise of something like that. You have to unlock denser and denser eggs - the game
+is also designed around the fact that you can do certain online-tied activites such as Contracts to unlock more Soul Eggs (prestige boost) and
+"Eggs of Prophecy" which increase potency of your Soul Eggs.
+
+It's rather simple game with a very minimal API, making it perfect for learning. You may not like the game, but that's beside the point.
+The simplicity of our target matters here.
## The existing works
-In some cases, you will find previous works on the target you pick, in my case, some clever people have created
-[scripts to extract .proto file out of app.](https://github.com/DavidArthurCole/EggIncProtoExtractor)
+In some cases, you will find previous works on the target you pick. In my case, some clever people have created
+[scripts to extract .proto file out of the app.](https://github.com/DavidArthurCole/EggIncProtoExtractor)
I advise you to check it out if you wish to get a better understanding of how you would go about retrieving the
API spec .proto file for your target.
For this blog purposes, we will assume the game server is shut down (as in we cannot query from the live API) and our goal is
to make a semi-functional selfhosted gameserver for our own needs, assuming we are the only one on said server.
+## How to source builds of a game
+There are two methods of sourcing the apk file here - one method is if you already have the app installed, install something like ZArchiver
+and extract it from /data/app/ - identifying the app by its icon. From there you will find `base.apk` which is enough for most apps.
+
+Alternatively, if the app is still available on Google Play, you can use an app like Aurora Store to go to the store detail page, select
+"Manual Download" and enter a known Build ID.
+
## Getting Started
Thanks to the previously mentioned script, it's easy to get started - find the APK, extract protobuf spec file, convert it with
protoc and we're done there. One small problem - due to cheaters, latest version of the game includes "AuthenticatedMessage" structure,
more morally sound decision of picking a version prior to these integrity checks. We can crack that another day as all the needed information
is retained in the app itself.
-Going forward with this, we are targetting game version 1.12.13 (Build ID 111121).
+Going forward with this, we are targetting game version 1.12.13 (Build ID 111121 - use that in Aurora Store).
With all that out of the way, lets get into actual commands used here:
```
So we have a potential API endpoint, let's put it to the test. We're not going to recompile anything yet or do any byte-patching,
let's try a quick smoke-test. Ensure your phone is rooted and you have a variant of Xposed Framework installed (I used LSPosed).
-We will need to untrip the SSL pinning present in most apps, including this one, I used [io.github.tehcneko.sslunpinning](https://github.com/Xposed-Modules-Repo/io.github.tehcneko.sslunpinning) module.
+We will need to unarm the SSL pinning present in most apps, including this one, I used [io.github.tehcneko.sslunpinning](https://github.com/Xposed-Modules-Repo/io.github.tehcneko.sslunpinning) module.
(Note: I know it is possible to repackage the app to do SSL unpinning in most cases, but in many cases, you won't know if it's worth the effort yet)
Next, install [AdAway app from F-Droid](https://f-droid.org/packages/org.adaway/) so we can setup a redirection on any network we are on.
```
This looks promising, right off the bat, first strings I'd check here are `r_icon_drone_rewards`, `b_icon_drone_boost`, `drone-boost` and `GENEROUS DRONES`.
-I inspected all 4 of them, and when I got to final 2, I found the enum string translations used for event IDs - here they are extracted for game version 1.12.13
+I inspected all 4 of them, and when I got to the final 2, I found the enum string translations used for event IDs - here they are extracted for game version 1.12.13
```
piggy-boost (Rate piggy fills is increased.)
piggy-cap-boost (UNLIMITED PIGGY;Gains are retained when event ends.)
### Contracts
As we progress the game and start performing prestiges, we unlock a feature called "Contracts" - but disaster strikes as we don't have any contracts we could
-accept. So far we stil see our good friends `/ei/get_periodicals` and `/ei/save_backup` hammering the server at regular intervals.
+accept. So far we still see our good friends `/ei/get_periodicals` and `/ei/save_backup` hammering the server at regular intervals.
When we created the periodicals response payload, you might have noticed in the protobuf message an optional field called `ContractsResponse contracts`. Lets see
what this ContractsResponse message contains.
We can put two-and-two together here and infer that `repeated Goal goals` is the legacy contract system - where everyone was on equal footing
and `repeated GoalSet goal_sets` is the *new* goal system that is split into Standard and Elite.
-We also learn that in future game version, they completely reworked how contracts work *yet* again into a grading "bracket" system. Fortunately,
+We also learn that in future game versions, they completely reworked how contracts work *yet* again into a grading "bracket" system. Fortunately,
we do not have to worry about that in our current target revision.
Now to get the ball rolling, there is conveniently a starting point set ahead for us already. The developer of game intended to ease new players into
return base64.b64encode(CurrentPeriodicals.SerializeToString())
```
-Lets try that out in-game now - after waiting for a minute, we see our contract prop up, but I immediately noticed one thing amiss.
+Lets try that out in-game now - after waiting for a minute, we see our contract pop up, but I immediately noticed one thing amiss.
The contract goals are swapped! I am getting Elite contract rewards for a Standard contract.
This piece of information now tells us that the first entry in GoalSets refers to Elite rewards and the second entry in GoalSets to Standard rewards.
a contract database and try scheduling them like the game originally did - a "Leggacy" contract every Friday and regular contracts showing up every 1-2 weeks
for roughly 2 weeks.
+## Rootless SSL Unpinning + Endpoint URL patching
+Let's make the app not require a VPN or root privileges - let's make user CAs work and the endpoint URL something we control on the public net.
+Start off by pulling the following repository
+```
+git clone https://github.com/ilya-kozyr/android-ssl-pinning-bypass.git
+python3 -m venv .venv
+source .venv/bin/activate
+pip install -r requirements.txt
+cp /path/to/your/apk .
+python3 apk-rebuild.py egginc.apk --pause
+```
+
+Open a new terminal window, the script will wait for us to perform modifications, enter the created folder `egginc.apk-decompiled` and `lib`.
+
+We have two folders here now, `arm64-v8a` and `armeabi-v7a`, just as we saw when we pulled the .so file out of the apk earlier. Let's tackle
+the 64-bit build first.
+
+For arm64 build it was really simple to perform bytepatch on the said endpoint. We already know it's supposed to look as `G?www.auxbrain.com` - let's probe the .so library a bit.
+```
+$> hexdump -C libegginc.so | grep "ww.auxbrain.co" -A2 -B2
+00b02b40 cd cc 4c 3f 00 00 00 00 00 00 00 00 00 00 80 3f |..L?...........?|
+00b02b50 00 00 00 00 00 00 00 00 00 00 00 00 14 ae 47 3f |..............G?|
+00b02b60 77 77 77 2e 61 75 78 62 72 61 69 6e 2e 63 6f 6d |www.auxbrain.com|
+00b02b70 00 48 54 54 50 20 52 45 51 3a 20 25 64 00 64 61 |.HTTP REQ: %d.da|
+00b02b80 74 61 3d 00 65 69 2f 66 69 72 73 74 5f 63 6f 6e |ta=.ei/first_con
+```
+
+We seem to have nothing blocking our way, let's create hex representations of `G?www.auxbrain.com` and a target domain of equal length, for example `G?eggs.based.quest`.
+
+(Note: You can choose a shorter name as well, if you null-terminate the extra bytes as padding)
+```
+$> echo "G?www.auxbrain.com" | hexdump -ve '1/1 "%.2X"'
+473F7777772E617578627261696E2E636F6D0A
+$> echo "G?eggs.based.quest" | hexdump -ve '1/1 "%.2X"'
+473F656767732E62617365642E71756573740A
+```
+
+Remove the trailing `0A` from end of both hex strings and now proceed as follows:
+```
+# Place the source in first bracket of sed and the new URL at second bracket.
+hexdump -ve '1/1 "%.2X"' libegginc.so | sed "s/473F7777772E617578627261696E2E636F6D/473F656767732E62617365642E7175657374/g" | xxd -r -p > patched.so
+```
+
+Huzzah! We now have a patched linked-library for the arm64 build. Let's also patch the 32-bit version.
+```
+$> hexdump -C libegginc.so | grep "ww.auxbrain.co" -A2 -B2
+0087b770 69 67 68 5f 74 6f 6f 5f 6d 61 6e 79 5f 70 78 00 |igh_too_many_px.|
+0087b780 74 61 62 6c 65 74 5f 68 64 70 69 00 00 00 00 00 |tablet_hdpi.....|
+0087b790 77 77 77 2e 61 75 78 62 72 61 69 6e 2e 63 6f 6d |www.auxbrain.com|
+0087b7a0 00 00 00 00 00 00 00 00 65 69 2f 66 69 72 73 74 |........ei/first|
+0087b7b0 5f 63 6f 6e 74 61 63 74 00 00 00 00 00 00 00 00 |_contact........|
+```
+This one lacks the `G?` prefix on API endpoint, but we still have null terminators we can rely on. Let's replace the `473F` from our previous strings with `0000`.
+```
+# Place the source in first bracket of sed and the new URL at second bracket.
+hexdump -ve '1/1 "%.2X"' libegginc.so | sed "s/00007777772E617578627261696E2E636F6D/0000656767732E62617365642E7175657374/g" | xxd -r -p > patched.so
+```
+
+Replace both of the libegginc.so files with the patched.so files. Move back to main terminal window and press ENTER.
+
+We now have a patched and debug signed apk for the game that isn't SSL pinned and contains a custom API endpoint we control without a VPN.
+
## Conclusion so far
We have created a (rather ugly looking) server emulator for the game. It functions, but it needs a lot of work still before we can call it ready.
If you have followed this far, give yourself pat on the back - if you actually tried to run this code, give yourself an extra pat on the back.