Solving a Random Mobile CrackMe Challenge with Lobotomy - Part 0x1


Recently I was digging through my local HD and realized there was a folder full of past mobile CTF challenges, which I never solved. So I decided to throw one of these challenges into Lobotomy, to see how much it would help me come up with the solution.

Loading the APK

Lobotomy contains a macro module, which helps automatically load a target Android application into its console environment.

(lobotomy) macro

    --> [0] crackme.json
    --> [1] dolphin.json

    --> Select config : 0

[2017-01-02 18:21:57.409993] Loading : Crackme001.apk ...

    --> Loaded : Crackme001.apk (!)


First I want to see the components registered within the AndroidManifest.xml.

(lobotomy) components list
[2017-01-02 18:29:57.489603] Enumerating components ...

    --> activity : com.syclover.crackme001.MainActivity

The Crackme001.apk only has one registered component -> com.syclover.crackme001.MainActivity. We can observe the MainActivity class within the application's class tree.

    --> class : Lcom/syclover/crackme001/DES;
        --> field : ALGORITHM_DES
            --> method : <init>
            --> method : byte2HexString
            --> method : encode
            --> method : encode

    --> class : Lcom/syclover/crackme001/MainActivity;
        --> field : known_file
            --> method : <clinit>
            --> method : <init>
            --> method : hasQEmuFiles
            --> method : OnMySelfClick
            --> method : onCreate
            --> method : onCreateOptionsMenu


The com.syclover.crackme001.MainActivity has an interesting method called hasQEmuFiles. We can start analyzing this method within Lobotomy's interact module.

(lobotomy) interact

In [1]: clz = self.find_class("MainActivity")  
In [2]: self.print_methods(clz)  
[email protected] :  
    0  (00000000) const/4              v2, 0
    1  (00000002) sget-object          v4, Lcom/syclover/crackme001/MainActivity;->known_file [Ljava/lang/String;
    2  (00000006) array-length         v5, v4
    3  (00000008) move                 v3, v2 [ [email protected] ]

[email protected] :  
    4  (0000000a) if-lt                v3, v5, +3 [ [email protected] [email protected] ]

[email protected] :  
    5  (0000000e) return               v2

[email protected] :  
    6  (00000010) aget-object          v0, v4, v3
    7  (00000014) new-instance         v1, Ljava/io/File;
    8  (00000018) invoke-direct        v1, v0, Ljava/io/File;-><init>(Ljava/lang/String;)V
    9  (0000001e) invoke-virtual       v1, Ljava/io/File;->exists()Z
    10 (00000024) move-result          v6
    11 (00000026) if-eqz               v6, +4 [ [email protected] [email protected] ]

[email protected] :  
    12 (0000002a) const/4              v2, 1
    13 (0000002c) goto                 -f [ [email protected] ]

[email protected] :  
    14 (0000002e) add-int/lit8         v3, v3, 1
    15 (00000032) goto                 -14 [ [email protected] ]

The hasQEmuFiles method first accesses the known_file variable, which is an array of strings. It loops through this array and creates a new File instance from each item, and checks whether or not that file exists. If we checkout the MainActivity 's static initialization constructor, we can observe the creation of the known_file instance.

    0  (00000000) const/4              v0, 3
    1  (00000002) new-array            v0, v0, [Ljava/lang/String;
    2  (00000006) const/4              v1, 0
    3  (00000008) const-string         v2, '/system/lib/'
    4  (0000000c) aput-object          v2, v0, v1
    5  (00000010) const/4              v1, 1
    6  (00000012) const-string         v2, '/sys/qemu_trace'
    7  (00000016) aput-object          v2, v0, v1
    8  (0000001a) const/4              v1, 2
    9  (0000001c) const-string         v2, '/system/bin/qemu-props'
    10 (00000020) aput-object          v2, v0, v1
    11 (00000024) sput-object          v0, Lcom/syclover/crackme001/MainActivity;->known_file [Ljava/lang/String;
    12 (00000028) return-void

So it appears the Crackme001.apk is just performing basic emulator detection by checking if certain qemu binaries exist on disk. This is a pretty standard technique, but not very effective. The hasQEmuFiles method's XREF(s) indicate that it is only called by MainActivity 's onCreate method.

########## XREF
F: Lcom/syclover/crackme001/MainActivity; onCreate (Landroid/os/Bundle;)V 10  
[email protected] :  
    0  (00000000) invoke-super         v1, v2, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V
    1  (00000006) const/high16         v0, 32515 # [1.7412886744782398e+38]
    2  (0000000a) invoke-virtual       v1, v0, Lcom/syclover/crackme001/MainActivity;->setContentView(I)V
    3  (00000010) invoke-static        Lcom/syclover/crackme001/MainActivity;->hasQEmuFiles()Z
    4  (00000016) move-result          v0
    5  (00000018) if-eqz               v0, +9 [ [email protected] [email protected] ]

[email protected] :  
    6  (0000001c) invoke-static        Landroid/os/Process;->myPid()I
    7  (00000022) move-result          v0
    8  (00000024) invoke-static        v0, Landroid/os/Process;->killProcess(I)V [ [email protected] ]

If the hasQEmuFiles method returns True, the application finds its own process identifier and terminates itself.

Bypass Emulator Detection

If we want to install the Crackme001.apk into an emulator, we will need to bypass the emulator detection implemented within the application. Normally in this situation I would dynamically instrument the hasQEmuFiles method to always return false. However, I opted for some basic smali patching instead.

First let's decompile the Crackme001.apk with apktool.

apktool d Crackme001.apk  
I: Using Apktool 2.0.0-RC4 on Crackme001.apk  
I: Loading resource table...  
I: Decoding AndroidManifest.xml with resources...  
I: Loading resource table from file: /Users/rotlogix/Library/apktool/framework/1.apk  
I: Regular manifest package...  
I: Decoding file-resources...  
I: Decoding values */* XMLs...  
I: Baksmaling classes.dex...  
I: Copying assets and libs...  
I: Copying unknown files...  
I: Copying original files...  

I'm only going to patch one statement in MainActivity.smali 's onCreate method.

.line 24
invoke-static {}, Lcom/syclover/crackme001/MainActivity;->hasQEmuFiles()Z

move-result v0

if-eqz v0, :cond_0

.line 25
invoke-static {}, Landroid/os/Process;->myPid()I  

If we change if-eqz to if-nez, the onCreate method will never continue to .line 25, thus never terminating itself.

After we make that change, let's use apktool again to [re] build the Crackme001.apk.

apktool b Crackme001  
I: Using Apktool 2.0.0-RC4  
I: Checking whether sources has changed...  
I: Smaling smali folder into classes.dex...  
W: Unknown file type, ignoring: Crackme001/smali/com/syclover/.DS_Store  
I: Checking whether resources has changed...  
I: Copying raw resources...  
I: Building apk file...  

Finally we need to sign the [re] built application, and install it in our emulator.

jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore /Users/rotlogix/MobileCTF/Crackme001/dist/Crackme001.apk alias_name  
Enter Passphrase for keystore:  
  signing: AndroidManifest.xml
  signing: classes.dex
  signing: res/drawable-hdpi/ic_launcher.png
  signing: res/drawable-hdpi/xxoo.png
  signing: res/drawable-mdpi/ic_launcher.png
  signing: res/drawable-xhdpi/ic_launcher.png
  signing: res/drawable-xxhdpi/ic_launcher.png
  signing: res/layout/activity_main.xml
  signing: res/menu/main.xml
  signing: resources.arsc
jar signed.  
adb install Crackme001.apk  
690 KB/s (269827 bytes in 0.381s)  
    pkg: /data/local/tmp/Crackme001.apk


Now that we have bypassed the Crackme001.apk 's emulator detection, we can now start reverse engineering out the logic needed to craft our solution.

Lobotomy Takeaways

The ability to edit the application's smali within Lobotomy, rather than relying on apktool, would be pretty solid.