Slide 1

Slide 1 text

How to reverse engineer Android applications Finding Vulnerabilities through Reverse Engineering Hasso Plattner Institute, Potsdam Hubert Hesse, Lukas Pirl, Christoph Matthies, Conrad Calmez using a popular word game as an example ? ? Images: “Freepik” on flaticon.com (CC BY 3.0), Google (CC BY 3.0)

Slide 2

Slide 2 text

1 Get the .apk 2 3 4 Extract the .apk 5 Decompilation to Smali Debugging 6 Putting it together 7 8 Automation Proxy Decompilation to Java

Slide 3

Slide 3 text

Our Example—a word game ● Top 10 word game in 145 countries (as of July 2014) ● More than 10.000.000 installs ● Over 50 million players ● Play online (with friends) ● 14 languages ● Free and premium version

Slide 4

Slide 4 text

1:58 0 points S N B I L U S F E I T T E R P A

Slide 5

Slide 5 text

1:58 15 points S N B I L U S F E I T T E R P A FLUT +15

Slide 6

Slide 6 text

● APK (application package file), archive file, based on JAR format ● Similar to Deb packages (in Ubuntu) or MSI packages (in Windows) ● Contains program code, resources, assets, certificates, and manifest file ● Can’t be directly downloaded from App Store 1 Get the .apk Download using online “APK Downloader” (http://apps.evozi.com/apk-downloader/) - or - Install on device and download using SDK tools (adb pull downloaded.apk)

Slide 7

Slide 7 text

2 Extract the .apk ● Normal decompression using unzip fails ● Special tool: APKTool ○ Standard is APKTool 1.5.2. (not able to recompress correctly) (https: //code.google.com/p/android-apktool/downloads/list) ○ APKTool 2.0.0 Beta 9 works (http://connortumbleson.com/2014/02/apktool-2-0-0-beta-9-released/) Decrompressing: apktool d -d game.apk -o outdir

Slide 8

Slide 8 text

2 Extract the .apk

Slide 9

Slide 9 text

2 Modifying resources ● Change arbitrary resources ● Repack into .apk file and install Recrompressing: apktool b -d outdir -o com.company.game.free_patch.apk ● Recompression works, Android fails with “can’t install”, wrong certificate ○ APKTool tries to reuse as much as possible, doesn’t recompute signature

Slide 10

Slide 10 text

2 Manually sign repacked apk: ● Create custom CA ● Java JAR Signing and Verification Tool (http://docs.oracle.com/javase/7/docs/technotes/tools/windows/jarsigner.html) jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my- release-key.keystore com.company.game.free_patch.apk alias_name Modifying resources

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

.apk contains compiled code ● Dalvik bytecode interpreted by the Dalvik Process virtual machine ● Stored in .dex (Dalvik EXecutable) files APKTool translates this to “smali” (https://code.google.com/p/smali/) ● Abstraction of bytecode, closer to Java ● Dalvik opcodes (http://s.android.com/tech/dalvik/dalvik-bytecode.html) ● Can be edited directly 3 Decompilation to Smali

Slide 13

Slide 13 text

.class public LHelloWorld; .super Ljava/lang/Object; .method public static main([Ljava/lang/String;)V .registers 2 sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; const-string v1, "Hello World!" invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V return-void .end method 3 Smali Hello World

Slide 14

Slide 14 text

Interactive debugging ● Set debuggable=”true” in AndroidManifest.xml ○ Repack using APKTool ● Need to connect smali sources to binary ● Workaround: pretend we have valid Java code 4 Debugging

Slide 15

Slide 15 text

a=0;// .class public abstract La; a=0;// .super Ljava/lang/Object; a=0;// a=0;// a=0;// # instance fields a=0;// .field protected final a:Ljava/lang/Object; a=0;// a=0;// .field private final b:Landroid/os/Handler; a=0;// 4 Debugging Smali code in comments Placeholder Java

Slide 16

Slide 16 text

Two ways to obtain java code ● Convert .dex files to .jar ○ Use standard java bytecode decompilers ● Disassemble .dex directly to .java 5 Decompilation to Java

Slide 17

Slide 17 text

Using dex files ● Androguard (https://code.google.com/p/androguard/) ○ Maps DEX format into full Python objects ○ Works in memory (My 4GB machine wasn’t enough) ○ Doesn’t immediately dump code into Java files 5 Decompilation to Java

Slide 18

Slide 18 text

Using jar files ● dex2jar (https://code.google.com/p/dex2jar/) ○ dex2jar, jar2dex, apk-sign ○ Supports recreating .dex from Java ● JD-GUI (http://jd.benow.ca/) ○ Popular jar-decompiler ○ Works 100% with “Hello World” app 5 Decompilation to Java

Slide 19

Slide 19 text

Combining Java decompilation and Smali ● Java more readable than Smali ● Unfortunately Java decompilation not 100% perfect ○ Invalid Java constructs or only method signatures ○ Cannot recompile from Java sources 6 Putting it together

Slide 20

Slide 20 text

private void fixSpecialChars() { int i; char ac[]; int j; int k; i = 0; ac = tiles; j = ac.length; k = 0; _L9: if(k >= j) break MISSING_BLOCK_LABEL_161; ac[k]; JVM INSTR lookupswitch 6: default 80 // 40: 125 // 41: 137 // 47: 149 // 91: 89 // 92: 101 // 93: 113; goto _L1 _L2 _L3 _L4 _L5 _L6 _L7 _L4: break MISSING_BLOCK_LABEL_149; _L1: break; /* Loop/switch isn't completed */ _L5: break; /* Loop/switch isn't completed */ _L10: i++; k++; if(true) goto _L9; else goto _L8 _L8: 6 When Decompilation fails an example Goto not supported in Java Bare JVM instructions

Slide 21

Slide 21 text

Combining Java decompilation and Smali ● Approach: Use multiple Java decompilers ○ They tend to fail in different places 6 Putting it together 1. Find interesting parts in Java source 2. Check corresponding smali sources 3. Edit those

Slide 22

Slide 22 text

protected void roundEnd(boolean paramBoolean) { // … this.resultData.setTotalScore(this.totalScore); // … startRoundSummary(); if (!this.isPractice) { this.currentRound.setWordsInRound(this.resultData.getMoves().size()); // … this.currentRound.setPlayer1Moves(GameHelper.encodeMoves(this.resultData. getMoves())); this.currentRound.setPlayer1Score(this.totalScore); // … 6 Manipulating the score Opportunities for manipulation ● Server validation disallows this

Slide 23

Slide 23 text

a=0;// sget-boolean v0, Lcom/company/game/core/statics/Statics;->DEBUGGING:Z a=0;// a=0;// #v0=(Boolean); -a=0;// if-eqz v0, :cond_0 +a=0;// #if-eqz v0, :cond_0 a=0;// 6 Enable Logging public class Toolkit { // … public static void Logw(String s, String s1) { if(Statics.DEBUGGING) Log.w(s, s1); } // …

Slide 24

Slide 24 text

a=0;// # static fields a=0;// .field public static ROUND_DURATION_IN_SECONDS_FOR_NORMAL_GAME:I a=0;// .field public static ROUND_DURATION_IN_SECONDS_FOR_TUTORIAL:I a=0;// a=0;// .method static constructor ()V a=0;// .locals 1 … -a=0;// const/16 v0, 0x78 +a=0;// const/16 v0, 0x12c a=0;// a=0;// #v0=(PosByte); a=0;// sput v0, Lcom/company/game/core/statics/GameStatics;->ROUND_DURATION_IN_SECONDS_FOR_NORMAL_GAME:I 6 More time per round 120s 300s

Slide 25

Slide 25 text

public static boolean allowPremiumContent(PremiumType premiumtype, Context context) { if(premiumIsPurchased(context)) return true; synchronized(lock) { if(!isLicensed(context)) break MISSING_BLOCK_LABEL_31; } return true; 6 Getting Premium a=0;// .line 129 -a=0;// invoke-static {p0}, Lcom/company/game/util/PremiumCampaignHelper;->premiumIsPurchased(…;)Z +a=0;// # invoke-static {p0}, Lcom/company/game/util/PremiumCampaignHelper;->premiumIsPurchased(…;)Z a=0;// -a=0;// move-result v0 +a=0;// # move-result v0 a=0;// -a=0;// #v0=(Boolean); -a=0;// if-eqz v0, :cond_0 +a=0;// #v0=(One); +a=0;// # if-eqz v0, :cond_0

Slide 26

Slide 26 text

6 Getting Premium free version premium (stats unlocked, no ads)

Slide 27

Slide 27 text

7 Proxy Route all app traffic through custom proxy ● Used MitMProxy (https://github.com/mitmproxy/mitmproxy) ● Retrieve real server URL via Wireshark ● Redirect app traffic via /etc/hosts on device ● Custom SSL certificate ○ Install own CA in device ○ No certificate pinning ● Avoid compressed responses via HTTP header ○ Accept-Encoding: gzip;q=0,deflate,sdch

Slide 28

Slide 28 text

7 Proxy AES encryption ● Shared key in decompiled code ● No key derivation function ● AES initialization vector in HTTP header ○ Payload-session: 2e2f6a61642f7372… ○ Unencrypted // file APIConnector.java private static byte sharedKey[] = { 57, -116, 126, 39, 116, -25, -95, -106, -81, 48, -33, -19, 120, 118, 35, 40, 66, 126, 31, 30, -83, 76, 31, 93, 13, -122, -50, 68, -108, -114, 28, -80 };

Slide 29

Slide 29 text

SSL MitM Proxy SSL HTTP Server by “aLf “, thenounproject.com (CC BY 3.0 US) Spy by “Hopstarter ”, iconarchive.com (CC BY-NC-ND 4.0) #! python #decrypt AES #using IV 7 Proxy Header: AES IV AES payload HTTP # /etc/hosts # redirect # to proxy

Slide 30

Slide 30 text

7 Proxy { "cacheTimestamp": "1405377910521", "userId": "0", "conversationId": "-1", "player1MostWordsInRound": "32", "id": "6602198229545556683", "player1Score": "214", "player1LongestWord": "HEAPS", "player1User": { "username": "username", "ranking": "0", "premium": "false", "recruits": "0", "deleted": "false", "newUser": "false", "bestScoreInMatch": "0", "userId": "3005807464", "bestScoreInRound": "0", "online": "false", "facebookConnected": "false", "avatarId": "0", "matchesPlayed": "0", "useFacebookImage": "false", "mostWordsInRound": "0" }, {"rounds": [ { "seed3": "14657688", "player2MoveErrors": "0", "gameId": "6602198229545556683", "player2SwipeDistance": "681", "player2Moves": "1AB2BAE2EAB216227612AEF2DA73840127652567354013DAB723673 B7654EAB72", "player1MoveErrors": "19", "player2Done": "true", "seed1": "2073207065", "seed2": "680974433", "player1SwipeDistance": "1608", "board": { "bonus": [" ", " ", " ", " ", " ", " ", "D", " ", " ", " ", " ", " ", " ", " ", " ", "T" ], "board": ["A", "T", "E", "H", "E", "P", "O", "T", "H", "S", "A", "S", "T", "F", "T", "E" ], "words": [ "TATE", "SOTS", "HOST", "SAPS", "FATSOS", … Server response request size up to 100kB

Slide 31

Slide 31 text

8 Automation Play the game automatically ● Generic external approach ○ No modification of binary necessary ○ Works for any app Monkeyrunner (http://developer.android.com/tools/help/monkeyrunner_concepts.html) ● Test apps at the functional/framework level ● Able to simulate keystrokes, take screenshots ● Python bindings

Slide 32

Slide 32 text

8 Obtain all possible words to play correctly ● apk contains .jet “dictionary” for each language ● Btw, also a wordlist (probably) used to check for cheaters Automation

Slide 33

Slide 33 text

8 Automation Ruzzle .jet files ● Binary files ● Trie / Radix tree structure ● Optimal for the way the game is played ● No duplicate encoding of characters ● List of all excepted words constructable G GA GAM GAME GO GOD GOT G O D T A M E

Slide 34

Slide 34 text

8 Automation Achieving the highscore ● Get all 16 letters ○ Input by hand / screenshot + OCR ● Find all valid words using the extracted dictionary ● Simulate keystrokes for found words ○ Actually not enough time to enter all valid words

Slide 35

Slide 35 text

8 Automation DEMO

Slide 36

Slide 36 text

Achievements Found possibilities to: ✓ Enable logging ✓ Unlock premium features ✓ Achieve insanely high score through automation ✓ Extract protocol via man-in-the-middle attack

Slide 37

Slide 37 text

Backup slides

Slide 38

Slide 38 text

Pinned certificate (installed at dev. time) App Server Get current server certificate 1 Compare current and pinned certificates 2 if identical: establish connection else: reject 3 Certificate Pinning