Reverse Engineering Android - Part II

Romain KRAFT

Whoami

  • Areizen tavu
  • Aperi'Kube member
  • Android lover
  • A beautiful otter <3

Smali

apktool d sample.apk 
.class public Landroid/support/a/c/a;
.super Ljava/lang/Object;


# direct methods
.method public static a()Z
    .locals 2

    sget v0, Landroid/os/Build$VERSION;->SDK_INT:I

    const/16 v1, 0x18

    if-lt v0, v1, :cond_0

    const/4 v0, 0x1

    goto :goto_0

    :cond_0
    const/4 v0, 0x0

    :goto_0
    return v0
.end method

Smali : Types

Z -> boolean
B -> byte
S -> short
C -> char
I -> int
J -> long (64 bits)
F -> float
D -> double (64 bits)

Types de base

Dans le cas d'un tableau, type est prefixé par '['

[B -> byte[] 

Si le type représente un objet :

L + package_name.replace('.','/') + class_name + ';'
Ex : String -> Ljava/lang/String;

Si le type représente une class interne à une autre :

outerclass_notation + '$' + class_name
Ex : com.sample.Voiture.Pneu -> Lcom/sample/Voiture$Pneu;

Smali : Appels

Lpackage/name/ObjectName;->MethodName(III)Z
  • On appelle la méthode 'MethodName' d'un objet 'ObjectName'
  • Elle prend en entrée 3 paramètres de type Integer
  • retourne un Boolean

Smali : Opcodes

https://source.android.com/devices/tech/dalvik/dalvik-bytecode

Java :  Smali to java

.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
public class HelloWorld extends Object{
	public static main(String args[]){
    	System.out.println("Hello World!");
    }
}

Java

Outils utiles :

  • Jadx / Jadx-gui
  • ByteCodeViewer
  • Androguard
Jadx example

Exemple : jadx-gui

Java : Obfuscation

Solutions :

  • Se référer au Manifest pour avoir les points d'entrée
  • `jadx -deobf [file.apk]` puis simplifier le code obfusqué manuellement dans Android-Studio

Java : Packing

Java : Packing -Detection

  • Utilisation de ClassLoader : DexClassLoader, JarClassLoader, URLClassLoader, etc.
  • Utilisation de Reflection : Field, Method, Class, etc.
  • Référence à du code non-existant dans l'apk

Exemple :

URLClassLoader child = new URLClassLoader(
        new URL[] {myJar.toURI().toURL()},
        this.getClass().getClassLoader()
);
Class classToLoad = Class.forName("com.MyClass", true, child);
Method method = classToLoad.getDeclaredMethod("myMethod");
Object instance = classToLoad.newInstance();
Object result = method.invoke(instance);

Java : Packing - Solution

  • Analyse statique :
    • Localisation de la donnée unpackée
    • Si n'est pas au format 'jar' ou 'dex', recherche de l'algorithme de déchiffrement du payload
    • Le payload devrait s'ouvrir directement dans un décompilateur Java
  • Analyse Dynamique :
    • Hook des ClassLoader puis dump de la charge
    • Dump de la mémoire de l'application puis "binwalk"
    • Dump des fichiers appartenants à l'application

https://pentest.blog/n-ways-to-unpack-mobile-malware/

Java : Techniques d'anti-debug

Plusieurs types :

  • Anti-emulation
  • Anti-root
  • Anti-modifications
  • Anti-MITM

Aucun problème en statique.

Java : Detection - Solution

Deux solutions :

 

  • Statique : modification du code 'smali' et repackaging de l'application
  • Dynamique : Hook des fonctions afin de modifier la détection.

Repackaging d'app

Permet d'avoir un bypass constant des protections

  • Décompilation avec 'apktool'
  • Modification du code smali
  • Compilation avec 'apktool'
  • Signature de l'application

Repackaging - Exemple

Repackaging - Exemple

➜  apktool d exemple.apk
I: Using Apktool 2.4.0-29355f-SNAPSHOT on exemple.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
S: WARNING: Could not write to (/home/romain/.local/share/apktool/framework), using /tmp 
instead...
S: Please be aware this is a volatile directory and frameworks could go missing, 
please utilize --frame-path if the default storage directory is unavailable
I: Loading resource table from file: /tmp/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...


➜  grep -lre "Oh no" exemple
exemple/smali/areizen/antiemulator/myapplication/MainActivity.smali

Repackaging - Exemple

Repackaging - Exemple

 .line 45
invoke-virtual {p0, p0}, Lareizen/antiemulator/myapplication/MainActivity;
	->antiemulation(Landroid/content/Context;)Z

move-result p1

const-string v0, "Anti-emulation"

if-eqz p1, :cond_0

const-string p1, "Oh no ! It\'s an emulator :("
if-eqz p1, :cond_0
if-nez p1, :cond_0

Repackaging - Exemple

apktool b example -o repackaged.apk

Repackaging - Exemple

java -jar sign.jar repackaged.apk

https://github.com/appium/sign

Repackaging - Exemple

Frida FTW

Java.perform(function(){
  
    // On recupère la classe String
    const String = Java.use('java.lang.String');
    
    // On va affiche dés qu'une chaine est comparée
    String.equals.implementation = function(x){
    	console.log(this.toString() + ' == ' + x.toString())
    	return this.equals(x)
    }
    
})

Frida - Installation

pip install frida-tools
pip install frida-push

frida-push # Push the Frida Server to the emulator / device

Frida - Exemple

Java.perform(function(){
	
    
    
    
    
    // Basic template
    
    
    
    
 
})

Frida - Exemple

Frida - Exemple

Java.perform(function(){
	
    const MainActivity = Java.use("areizen.antiemulator.myapplication.MainActivity")
    
    MainActivity.antiemulation.implementation = function(x){
        console.log("Bypassed Anti-emulator")
    	return false;
    }
})

script.js

frida -U -l script.js -f areizen.antiemulator.myapplication --no-pause

Frida - Exemple

Frida - Exemple

Java.perform(function(){
    
    const Log = Java.use("android.util.Log")
    const System = Java.use("java.lang.System")

    var tsLong = System.currentTimeMillis()/1000;
    var timestamp = tsLong.toString();

    Log.i("Timestamp", "Printing timestamp from Frida :" + timestamp)

})

Java Native Interface

#include <jni.h>

JNIEXPORT jstring JNICALL
Java_com_google_sample_helloandroidjni_MainActivity_helloWorld(JNIEnv *env, jobject instance)
{
	// Put your code here
    return (*env)->NewStringUTF(env, "Hello From JNI");
}

Permet de créer des fonctions en C appelables par le code Java

Java Native Interface : Implémentation

static {
	// Will load libexample.so
	System.loadLibrary("example"); 
}

Charger une librairie

Declarer une fonction native en Java

public native String helloWorld();

Nom de la fonction dans la librairie C

JNIEXPORT jstring JNICALL
Java_com_google_sample_helloandroidjni_MainActivity_helloWorld(JNIEnv* env);

Java Native Interface : From C to Java

https://android.googlesource.com/platform/libnativehelper/+/master/include/nativehelper/jni.h

Java Native Interface  : Analyse statique

https://gist.github.com/Areizen/13eb7c7d0de7e0577a296a74508663b2

Dans IDA : File -> Parse C Header File

Java Native Interface  : Analyse statique

"Y" pour declarer les types d'une fonction, ici, on met JNIEnv en premier arg

Java Native Interface  : Analyse statique

On propage la structure JNIEnv avec "T" dans IDA .

Cela permet de savoir quelle methode est appelée à partir de l'offset chargé.

Java Native Interface  : Analyse dynamique

Interceptor.attach(null, "strlen",{
	onEnter: function(args){
    	console.log("strlen called on : " + Memory.readCString(args[0]));
    }
})

Hooker du code native C avec Frida :

(En cas de problème,

merci de ne pas envoyer de message par DM à @Areizen_ ;) )

Questions

?

Reverse Engineering Android - Part II

By areizen

Reverse Engineering Android - Part II

  • 313