// HackTricks · Mobile

Google CTF 2018 - Shall We Play a Game?

Google CTF 2018 - Shall We Play a Game?

The original Google CTF repository preserves the 2018 challenges, and community write-ups preserve this APK and its reversing workflow.[1][2]

Upload the APK to an isolated emulator service such as Appetize.io, or install it on a disposable local emulator/device, to observe its behavior:[3]

Appetize.io emulator running the Shall We Play a Game APK

The game requires 1,000,000 wins to reveal the flag.

Following the Android pentesting workflow, decode the APK with Apktool and inspect decompiled Java with JADX.[2]

Reading the java code:

Google CTF 2018 - Shall We Play a Game?: Reading the java code

The function that prints the flag is m().

Smali changes

Call m() the first time

To make the application call m() when this.o != 1000000, invert the branch condition:

 if-ne v0, v9, :cond_2

to:

 if-eq v0, v9, :cond_2

Before

After

Follow the Android pentesting workflow to rebuild and sign the APK, then run it again:

Appetize.io emulator showing the modified APK after changing the Smali condition

The displayed flag is not fully decrypted because the per-win transformation must run 1,000,000 times; merely bypassing the comparison does not reproduce those iterations.[2]

Another approach is to leave the branch instruction intact and change its operands:

Alternative Smali change: keep the branch instruction and change the compared registers

Another way is instead of comparing with 1000000, set the value to 1 so this.o is compared with 1:

Smali changes - Call m() the first time: Another way is instead of comparing with 1000000, set the value to 1 so this.o is compared with 1

A fourth approach is to move the value of v9 (1,000,000) into v0 (this.o):

Smali changes - Call m() the first time: A forth way is to add an instruction to move to value of v9(1000000) to v0 (this.o)

Smali changes - Call m() the first time: A forth way is to add an instruction to move to value of v9(1000000) to v0 (this.o)

Solution

Make the application run the win/decryption loop 1,000,000 times after the first win. Create the :goto_6 loop and jump back while this.o has not reached 1,000,000:[2]

Call m() the first time - Solution: Make the application run the loop 100000 times when you win the first time. To do so, you only need to create the :goto 6 loop and make the...

The original experiment succeeded on a physical device but not its emulator. That is an observation about that setup rather than an inherent requirement; emulator CPU speed, watchdogs, or service limits can make the million-iteration loop appear to hang.

References

☧ CPTS · ordered posts

Locked

Unlock with your Main_Key through local GPG.

Start the local GPG helper

In your cheatsheet checkout, run:

npm run unlock:helper

Open the link it prints, or paste its pairing code below. Keep the terminal open until GPG finishes.

Import an index you already decrypted

Download the encrypted index, then decrypt with GPG:

gpg --output report-index.json --decrypt report-index.gpg

Your private key stays in GPG. The decrypted index is saved in this browser so you can reopen it after navigation, refreshes and restarts. Lock this browser or clear this site's browser data to remove the saved index. Nothing is uploaded.