1 of 21

Mobile Systems and Smartphone Security(MOBISEC 2020)

Prof: Yanick Fratantonio�EURECOM

1

Native Code

2 of 21

Native Code

2

Java source code�.java

Java bytecode�.class .jar

Dalvik bytecode�.dex

Java compiler�javac

DEX compiler�dx

Executable by the JVM

Executable by the DVM

C/C++ source code�.c .cpp .h

Kotlin source code�.kt .kts

Kotlin compiler�kotlinc

Machine code�.so

3 of 21

Why Native Code?

  • Some components need to be fast

  • Android allows developers to write CPU-intensive components in C/C++
    • Much faster than Java/Dalvik components

  • Google discourages its usage...
    • ... but bad guys don't care

3

4 of 21

Native code makes security analysis tricky

  • C/C++ code is compiled to machine code / .so

  • Reliable Dalvik disassembling is "easy"
    • Mostly thanks to the Dalvik bytecode verifier
    • The bytecode NEEDS to follow some properties...
    • ... which make it easy to disassemble / analyze it

  • Reliable machine code disassembling is an open problem

4

5 of 21

Native code makes security analysis tricky

  • "Difficult to disassemble" makes it a nice trick for attackers
    • Decompilation is even trickier...

  • Great venue for code obfuscation

  • Analyzing the Java code is not enough

  • Worse: the analysis of Java code can be misleading!

5

6 of 21

Native code makes security analysis tricky

  • Both Java/Dalvik code and native code runs within the same security sandbox

  • Native code can interfere with the Dalvik code / memory

  • Native code can modify memory data structure...
    • The Java code invokes method A...
    • ...but instead it invokes method B

6

7 of 21

Native code makes security analysis tricky

  • More in general: NO barrier between Java / native code

  • If the app uses native code and you don't know what it does, you can't fully trust what you see about the Java world!

  • Everything runs as the same user

7

8 of 21

Java ⇔ C/C++ can communicate

8

Java bytecode�.class .jar

Machine code�.so

JNIJava Native Interface

9 of 21

Developing a native code component

$ cat main/java/com/mobisec/nativecodetest/MainActivity.java

...

public native String stringFromJNI();

static {

System.loadLibrary("native-lib");

}

9

10 of 21

Developing a native code component

$ cat main/cpp/native-lib.cpp

#include <jni.h>

#include <string>

extern "C"

JNIEXPORT jstring JNICALL

Java_com_mobisec_nativecodetest_MainActivity_stringFromJNI(

JNIEnv* env,

jobject /* this */) {

std::string hello = "Hello from C++";

return env->NewStringUTF(hello.c_str());

}

10

JNI does some magic to invoke this C++ method when you invoke the Java associated method

11 of 21

Java ⇔ C/C++ code

  • Java can invoke C/C++ methods

  • C/C++ methods can invoke Java methods as well!

11

12 of 21

Example of C/C++ ⇒ Java

package my.package;

class MainActivity extends Activity {

...

public String messageMe(String text) {

System.out.println(text);

return text;

}

public native String getJniString();

}

12

13 of 21

Example of C/C++ ⇒ Java

jstring Java_my_package_MainActivity_getJniString(JNIEnv* env, jobject obj) {

jstring jstr = env->NewStringUTF(env, "Created in JNI");

jclass clazz = env->FindClass(env, "my/package/MainActivity");

jmethodID messageMe = env->GetMethodID(� env, clazz, "messageMe", "(Ljava/lang/String;)Ljava/lang/String;");

jobject result = env->CallObjectMethod(env, obj, messageMe, jstr);

const char* str = env->GetStringUTFChars(env,(jstring) result, NULL);

printf("%s\n", str);

return (*env)->NewStringUTF(env, str);

}

13

14 of 21

Once compiled

  • At compilation time
    • APK content
      • classes.dex
      • ...
      • lib/armeabi-v7a/native-lib.so
      • lib/x86/native-lib.so

  • At installation time
    • The system takes all .so files it finds in libs/armeabi-v7a and it unpacks them into /data/data/<your.package>/lib directory

14

15 of 21

More on Native Code

  • You can create a Native Activity
    • No Java code at all!

  • The shared object file (.so) doesn't need to be embedded in the app
    • An app can load native libraries from other parts of the file system
    • Common malware pattern: 1) download from web, 2) load it
    • But: it needs to start with "lib" and ends with ".so" (link)

  • Is JNI the only way Java and C/C++ can talk? Nope

15

16 of 21

Android Studio setup

  • Install via SDK manager
    • NDK, CMake, and LLDB

  • Create a new project and check "C/C++ support"

  • It creates a basic app with a basic native code component

16

17 of 21

Reverse Engineering Native Code

17

18 of 21

Intro on Binary Reverse Engineering

  • A shared object / library (our .so files) is an ELF file

  • ELF files can be
    • Full-fledged programs aka they have a "main"
    • Libraries implementing functions that can be called by other programs

  • Tools
    • Metadata inspection
    • Disassemble
    • Decompilers
    • UI to explore the binary

18

19 of 21

Tools

  • readelf -a /bin/ls (open source)
    • Dumps metadata

  • objdump -d /bin/ls (open source)
    • Dumps disassembly

  • IDA (very expensive, but there is a limited free version)
    • Can disassemble and show graphs
    • No decompiler in the free version

19

20 of 21

Tools

  • radare2 (open source)
  • cutter: UI for radare2 (open source)
  • retdec (open source)
    • Decompiler from AVAST
  • GHIDRA (open source tool by NSA)
  • JEB (not open source, but free version available!)
  • Binary Ninja (not open source, but affordable)

20

21 of 21

End of quick intro

  • More details during the demo

  • This is just a super short intro on the topic

  • Only one task will touch on this topic, but it will be very basic

21