0% found this document useful (0 votes)
64 views49 pages

DEF CON 25 - Slava-Makkaveev-and-Avi-Bashan-Unboxing-Android

Android conference notes
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
64 views49 pages

DEF CON 25 - Slava-Makkaveev-and-Avi-Bashan-Unboxing-Android

Android conference notes
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 49

Unboxing Android

Everything you wanted to know about Android packers

Slava Makkaveev
Avi Bashan
Who Are We?
@Avi @Slava

R&D Team Leader at Check Point, former security Senior Security Researcher at Check Point, former Security
researcher at Lacoon Mobile Security. Researcher at Verint.

Experienced in OS Internal research, mobile security, linux


kernel. Vast experience in mobile OS research and linux internals.
“Boxing” Apps
● Malware authors use various “boxing” techniques to prevent
○ Static Code Analysis
○ Reverse Engineering
● This can be done by proprietary techniques or 3rd party software
● This Includes
○ Code Obfuscation
○ Anti Debugging
○ Anti Tampering
○ Anti Dumper
○ Anti Decompiler
○ Anti Runtime Injection
Maliciousness of Packed Apps

Analyzed 13,000 Apps (July 2017)


Techniques to protect an app’s
code
Apk Protection Techniques

● Obfuscators
● Packers
● Protectors
Apk Protection Techniques
pm.getClass().getMethod("getPackageSizeInfo", String.class,
Class.forName("android.content.pm.IPackageStatsObserver")).invoke(pm, packInfo.packageName,

● Obfuscators new IPackageStatsObserver.Stub() {


public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) {
}

● Packers });

● Protectors
v6.getClass().getMethod("getPackageSizeInfo", String.class,
Class.forName("android.a.a.a")).invoke(v6, ((PackageInfo)v0_5).packageName,
new a() {
public void a(PackageStats arg3, boolean arg4) {
}
});
Apk Protection Techniques

● Obfuscators
● Packers
● Protectors
Apk Protection Techniques
APK APK

● Obfuscators Packer
Loader
Packing
● Packers Original
DEX
process
Encrypted
● Protectors DEX
Apk Protection Techniques
APK APK

● Obfuscators Packer
Loader
Packer
Loader

● Packers Execution

Encrypted Original
● Protectors DEX DEX
Apk Protection Techniques

● Obfuscators
● Packers
● Protectors
Apk Protection Techniques
APK APK

● Obfuscators Protector
Loader
Protection
● Packers Original
DEX
Process
Encrypted

● Protectors Modified
DEX
Apk Protection Techniques
APK APK

● Obfuscators Protector
Loader
Protector
Loader

● Packers Encrypted
Execution

Modified
● Protectors Modified
DEX
DEX
Back to Basics!
ART - Android RunTime VM
Provided an Ahead of Time (AOT) compilation
approach
DEX to OAT

● Pre-compilation at install time


○ installation takes more time
○ more internal storage is required

● OAT vs JIT
○ Reduces startup time of applications
○ Improves battery performance
○ Uses less RAM
DEX Loading Process
● App contains minimum one DEX file
● App can load other DEX files during execution
Zygote fork() App ● Each DEX file will be compiled in OAT file
process process ● Android Runtime executes OAT files
● Android Runtime checks DEX files checksum
Load app code

dex2oat OAT version of


classes.dex
classes.dex
OAT - Ahead of Time File
OAT is ELF

● Three special symbols in dynamic section


○ oatdata
○ oatexec
○ aotlastword
● Original DEX file is contained in the oatdata
section
● Compiled native instructions are contained
in the oatexec section
Android Java Native Interface (JNI)
● Allows calling native code directly from JVM.
● Execution path starts from System.loadLibrary
● Used by some of the popular packers for the packing logic.
● Packer library is called after activity is started
How to unpack?
Possible Approaches to Unpack an Android App
● Find the algorithm
● Extract DEX from compiled OAT
● Dump DEX from memory
● Runtime environment modification
Notable Previous Work
● Android Hacker Protection Level 0
○ Tim Strazzere and Jon Sawyer
○ DEFCON 22, 2015
○ Released a set of unpacking scripts
● The Terminator to Android Hardening Services
○ Yueqian Zhang, Xiapu Luo , Haoyang Yin
○ HITCON, 2015
○ Released DexHunter - modified version of Android Dalvik/ART VM
Our Approach
Goals
● What did want
○ Find a solution that
■ Require minimal changes to Android
■ Will work on most of the packers
● How did we do it?
○ Reversed most popular packers
○ Patched few code rows of Android Runtime
Goals
● What did want
○ Find a solution that
■ Require minimal changes to Android
■ Will work on most of the packers
● How did we do it?
○ Reversed most popular packers
○ Patched few code rows of Android Runtime

PROFIT
Analyzed Packers
Most popular packers encountered

● Baidu
● Bangcle
● Tencent
● Ali
● 360 Jiagu
● ... (and a few more)
Abstract Packer Model

Load protected DEX Open DEX file


Find a class Map data
libart.so
libc.so
open
Loader DEX read
mmap
...
Abstract Packer Model

Load protected DEX Open DEX file


Find a class Map data
libart.so
libc.so Decrypt DEX
open Read original data
Loader DEX read Protected DEX
mmap
...
<protector>.so
Load native part Hook calls
Bangcle - Classification
Classes Files
- ApplicationWrapper - libsecse
- FirstApplication - libsecmain
- MyClassLoader - libsecexe
- ACall - libsecpreload
- bangcle_classes (original dex)
Bangcle - Java Loader Implementation
assets/libsecexe.so → /data/data/<pkg>/.cache/libsecexe.so
assets/libsecmain.so → /data/data/<pkg>/.cache/libsecmain.so
assets/libsecpreload.so → /data/data/<pkg>/.cache/libsecpreload.so
assets/bangcle_classes.jar → /data/data/<pkg>/.cache/classes.jar

System.load("/data/data/" + getPackageName() + "/.cache/libsecexe.so");


Acall.getACall().a1(...);
Acall.getACall().r1(...);
Acall.getACall().r2(...);
...
public class MyClassLoader extends DexClassLoader {
...
}
cl = new MyClassLoader("/data/data/" + getPackageName() + "/.cache/classes.jar", ...);
realApplication = cl.loadClass(v0).newInstance();
Bangcle - Native Loader Implementation
Java Interface Native Functions Mapping
public class ACall {
public native void a1(byte[] arg1, byte[] arg2); Func Offset Func Offset
public native void at1(Application arg1, Context arg2);
a1 0x4638 set1 0xCFFC
public native void at2(Application arg1, Context arg2);
public native void c1(Object arg1, Object arg2);
at1 0x8A44 set2 0x9BC8
public native void c2(Object arg1, Object arg2);
public native Object c3(Object arg1, Object arg2);
at2 0x9184 set3 0x566C
public native void jniCheckRawDexAvailable();
public native boolean jniGetRawDexAvailable();
c1 0xF984 set3 0x8CE8
public native void r1(byte[] arg1, byte[] arg2);
public native void r2(byte[] arg1, byte[] arg2, byte[] arg3);
c2 0x103E8 set4 0x63B4
public native ClassLoader rc1(Context arg1);
public native void s1(Object arg1, Object arg2, Object arg3);
c3 0x12E48 set5 0x4AA0
public native Object set1(Activity arg1, ClassLoader arg2);
public native Object set2(Application arg1, ...); r1 0x4938 set8 0x16828
public native void set3(Application arg1);
public native void set3(Object arg1, Object arg2); r2 0xDE38 s1 0x126B4
public native void set4();
public native void set5(ContentProvider arg1); jniCheckRawDexAvailable 0x4408 rc1 0xBFE4
public native void set8();
} jniGetRawDexAvailable 0x44A0
Bangcle - libsecexe.so
Class: ELF32
Type: DYN (Shared object file)
Machine: ARM
Entry address points to compressed code (anti-debugging)
Entry point address: 0x433c
Start of section table is out of file bounders
Start of program headers: 52 (bytes into file)
No section table (anti-debugging)
Start of section headers: 92204 (bytes into file)
Size of program headers: 32 (bytes)
Exception Index Table is out of file bounders (IDA crash)
Number of program headers: 6
Size of section headers: 0 (bytes) Program headers:
Number of section headers: 0
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align

Dynamic section: EXIDX 0x028584 0x00028584 0x00028584 0x00568 0x00568 R 0x4

0x0000000c (INIT) 0x125A9 LOAD 0x000000 0x00000000 0x00000000 0x131ec 0x131ec RE 0x8000
0x00000019 (INIT_ARRAY) 0x30C1C
... LOAD 0x018c1c 0x00030c1c 0x00030c1c 0x00520 0x01538 RW 0x8000

DYNAMIC 0x018c80 0x00030c80 0x00030c80 0x00108 0x00108 RW 0x4

Real entry point GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4

GNU_RELRO 0x018c1c 0x00030c1c 0x00030c1c 0x003e4 0x003e4 R 0x1


Bangcle - libsecexe.so
0xf4
HASH
0x9d0
SYMTAB
0x1cb0
STRTAB
0x3b79
REL
0x43e0 0x433c 0x433c
Compressed code
0x4638
a1 Registration
0x4938
r1 com.secneo.guard.ACall
Copy code sections to an 0xde38
r2 native methods: a1, r1, r2, ...
allocated buffer. 0xe050
JNI_OnLoad
Decompress 0x247b0 bytes
to 0x433c

0x125a9 0x12590
TEXT (init code)
0x13150

0x28aec
Bangcle - Processes
Extract ELF /data/data/<pkg>/.cache/<pkg> from apk (Assets)
Function a1

fork app process


execl /data/data/<pkg>/.cache/<pkg> <pkg> -1114751212 1 /data/app/<pkg>/base.apk 34 <pkg> 43 44 0
fork pkg process (from libsecmain.so::so_main)
anti-debugging thread
fork pkg process if .cache/classes.dex (OAT) does not exist
LD_PRELOAD=/data/data/<pkg>/.cache/libsecpreload.so
Function r2
LD_PRELOAD_ARGS=<pkg> 9 13
LD_PRELOAD_SECSO=/data/data/<pkg>/.cache/libsecmain.so
execl /system/bin/dex2oat
–zip-fd=9 –zip-location=/data/data/<pkg>/.cache/classes.jar –oat-fd=13
–oat-location=/data/data/<pkg>/.cache/classes.dex –instruction-set=arm
Bangcle - libc.so hook
Function r1

libc func Offset libc func Offset

munmap 0x15BD8 close 0x14FAC

msync 0x15F88 __openat 0x14DA4

read 0x15118 pread64 0x162F8


Protection was changed
__mmap2 0x15420 pwrite64 0x166DC

__open 0x14B9C write 0x152FC


Bangcle - Summary
● Creates a stub in Java activity to load native library.
● Native library is protected with different anti research techniques.
● Native library hooks libc for handling the opening of the OAT file.
Baidu - Classification

Classes Files
- StubApplication - libbaiduprotect
- StubProvider - baiduprotect1 (original dex)
Baidu - Native Loader Implementation
public class A implements Enumeration {
public static native byte B(int arg0, Object arg1, ...);
public static native char C(int arg0, Object arg1, ...); Func Offset
public static native double D(int arg0, Object arg1, ...);
public static native float F(int arg0, Object arg1, ...); a 0x23459
public static native int I(int arg0, Object arg1, ...);
public static native long J(int arg0, Object arg1, ...); b 0x2345d
public static native Object L(int arg0, Object arg1, ...);
public static native short S(int arg0, Object arg1, ...); c 0x23461
public static native void V(int arg0, Object arg1, ...);
public static native boolean Z(int arg0, Object arg1, ...); V, Z, B, C, S, I, J, F, D, L 0x25861
public static native void a();
public static native void b();
public static native String[] c();
}
Baidu - libbaiduprotect.so

0x1000

0x2e6d

0x2ea4
JNI_OnLoad

Change self protection Change self protection


0x0 - 0x1000 0x2000 - 0x3d000
0x23459 a
Remove ELF header Decrypt code
0x2345d b
0x2e6d - 0x3ca78
0x23461 c
0x25861 V, Z, B, C, S, I, J, F, D, L

0x3ca78

0x4286c
TEXT (Entry point 1)
Baidu - JNI_OnLoad

Anti-debugging

Registration of native methods: a, b, c, …

Extract packed DEX /Assets/baiduprotect1.jar to /data/data/<pkg>/.1/1.jar


Create empty DEX file /data/data/<pkg>/.1/classes.jar

Hook libart.so

Create DexClassLoader(/data/data/<pkg>/.1/classes.jar) + Merge with main class loader by


extending BaseDexClassLoader::pathList::dexElements
Baidu - Anti-debugging
● Obfuscation
● Logs disabling
● For each /proc/ check that /proc/<pid>/cmdline does not contain gdb, gdbserver, android_server
● For each /proc/self/task check that /proc/self/task/<pid>/status does not contain TracerPid
● For each /proc/self/task check that /proc/self/task/<pid>/comm does not contain JDWP
● Check android.os.Debug.isDebuggerConnected
● select call (timer) based technique
● inotify watch (IN_ACCESS + IN_OPEN) of
○ /proc/self/mem
○ /proc/self/pagemap
○ For each /proc/self/task
■ /proc/self/task/<pid>/mem
■ /proc/self/task/<pid>/pagemap
Baidu - libart.so hook
libc func Libart hook Offset

read 0x309BC8 0xA75C

open 0x309BDC 0x8FAC


Function __android_log_print
No logs close 0x309BE4 0x9168

Function execv mmap 0x309BE8 0x9474


dex2oat hook:
Add environment variable ANDROID_LOG_TAGS=*:f strstr 0x309C58 0x8BD8
Prevent code compilation: add --compiler-filter=verify-none command line parameter
fork 0x309F3C 0x92DC

Function open waitpid 0x309F40 0xA5E4


Decrypt /data/data/<pkg>/.1/1.jar in case of /data/data/<pkg>/.1/classes.jar file loading
execv 0x309F4C 0xA324

__android_log_print 0x309FAC 0xA750


Baidu - Summary
● Creates a stub in Java activity to load native library.
● Native library is protected with different anti research techniques .
● Native library hooks libc for handling the opening of the DEX file.
libc::open == decryption

Bangle Baidu
Filter by file path:

/data/data/<pkg>/.cache/classes.dex /data/data/<pkg>/.1 /classes.jar

Expect to see:

OAT DEX
Using the DEX Loading Process to Unpack Apps
Where is first call of DEX/OAT file opening?

DEX OAT
dalvik.system.DexClassLoader::DexClassLoader
dalvik.system.DexFile::DexFile
DexFile::openDexFileNative

DexFile_openDexFileNative
ClassLinker::OpenDexFilesFromOat
OatFileAssistant::MakeUpToDate
OatFileAssistant::OatFileIsUpToDate

OatFileAssistant::GivenOatFileIsUpToDate OatFileAssistant::GetOatFile
OatFileAssistant::GetRequiredDexChecksum OatFile::Open
DexFile::GetChecksum OatFile::OpenElfFile → DexFile::DexFile
OpenAndReadMagic
platform/art/runtime/dex_file.cc patch
DEX OAT
static int OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* error_msg) DexFile::DexFile(const uint8_t* base, size_t size,
{ const std::string& location,
CHECK(magic != nullptr); uint32_t location_checksum,
ScopedFd fd(open(filename, O_RDONLY, 0)); MemMap* mem_map,
… const OatDexFile* oat_dex_file)
: begin_(base),
char* fn_out = new char[PATH_MAX]; size_(size),
strcpy(fn_out, filename); ...
strcat(fn_out, "__unpacked"); {
...
int fd_out = open(fn_out, O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
std::ofstream dst(location + "__unpacked", std::ios::binary);
struct stat st; dst.write(reinterpret_cast<const char*>(base), size);
if (!fstat(fd.get(), &st)) { dst.close();
char* addr = (char*)mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd.get(), 0);
write(fd_out, addr, st.st_size); ...
munmap(addr, st.st_size); }
}

close(fd_out);
delete fn_out;

...
}
Demo Time!
Unpacking modification
instructions to AOSP can
be found @checkpoint
github repo
Questions?

You might also like