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
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