Category:Architecture/Android

From aldeid
Jump to navigation Jump to search

Description

Android OS

Android designates the OS that runs on Google devices (Android phones, tablets, ...).

Architecture

Dalvik
process virtual machine (VM) in Google's Android operating system that executes applications written for Android
DEX
Programs are commonly written in Java and compiled to bytecode for the Java virtual machine, which is then translated to Dalvik bytecode and stored in .dex (Dalvik EXecutable) and .odex (Optimized Dalvik EXecutable) files
smali/baksmali
assembler/disassembler for the dex format used by dalvik, Android's Java VM implementation. The syntax is loosely based on Jasmin's/dedexer's syntax, and supports the full functionality of the dex format (annotations, debug info, line info, etc.)

Activity diagram

Android framework

android sdk and android avd

android sdk

Android SDK Manager enables to update the framework and install additional tools.

On MobiSec, you can start the SDK Manager with the following command:

mobisec@ubuntu:/opt/mobisec/Android/sdk/tools$ ./android sdk

android avd

Android Virtual Device (AVD) Manager manges virtual devices:

On MobiSec, you can start the AVD Manager with the following command:

mobisec@ubuntu:/opt/mobisec/Android/sdk/tools$ ./android avd

emulator

On MobiSec, you can list the devices and start the emulator with the following commands:

mobisec@ubuntu:/opt/mobisec/Android/sdk/tools$ ./emulator -list-avds
MobisecLab
mobisec@ubuntu:/opt/mobisec/Android/sdk/tools$ ./emulator -avd MobisecLab -scale 0.75

Here is a screenshot of the emulator running:

adb

Description

The adb program is an interface to perform various tasks on a connected device. Only common commands are reported in this section. For a full list of supported commands, issue adb --help.

adb devices

adb devices shows connected devices:

$ adb devices
List of devices attached
emulator-5554   device

adb shell

adb shell enables one to connect to a device and run commands via a terminal:

$ adb shell
127|root@generic:/ # id
uid=0(root) gid=0(root) context=u:r:shell:s0

adb push and adb pull

  • adb push pushes a file to the connected device from the local computer
  • adb pull pulls a file from the connected device to the local computer

adb install

adb install application.apk will install an application to the connected device.

adb uninstall

List installed appplications:

$ adb shell "pm list packages" 
[REMOVED]
package:com.app.ndh
[REMOVED]

Uninstall the application:

$ adb uninstall com.app.ndh
Success

adb forward

This command enables remote debugging by forwarding the debugging session from the android device to the local computer:

$ adb forward tcp:23946 tcp:23946

APK format

Analyze an APK

apktool

You can decompile an APK with apktool:

mobisec@ubuntu:/data$ java -jar apktool_2.0.1.jar d android.apk 
I: Using Apktool 2.0.1 on android.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
I: Loading resource table from file: /home/mobisec/apktool/framework/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...

Online resources

Check if an application is debuggable

Once you have decompiled an APK, analyze the AndroidManifest.xml file. The application is debuggable only if the AndroidManifest.xml file contains the "android:debuggable" string.

It won't be debugglable if the file:

  • does not contain the "android:debuggable" string or
  • does contain the android:debuggable="false" string

Make an application debuggable

If you want to debug an application that is not debuggable, you will need to decompile it, modfiy the AndroidManifest.xml file and rebuild it. Below is the procedure to follow:

Decompile APK

  • Decompile the APK as explained above with apktool

Make application debuggable

  • Add android:debuggable="true" in the AndroidManifest.xml file as follows:
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.flare_on.flare" platformBuildVersionCode="22" platformBuildVersionName="5.1.1-1819727">
    <application android:debuggable="true" android:allowBackup="true" android:icon="@drawable/icon" android:label="@string/app_name" android:theme="@style/AppTheme">
        <activity android:label="@string/app_name" android:name="com.flareon.flare.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity android:label="@string/title_activity_validate_email" android:name="com.flareon.flare.ValidateActivity" android:parentActivityName="com.flareon.flare.MainActivity">
            <meta-data android:name="android.support.PARENT_ACTIVITY" android:value="com.flareon.flare.MainActivity"/>
        </activity>
    </application>
</manifest>

Rebuild APK

$ java -jar apktool_2.0.1.jar b android -o android-debug.apk
I: Using Apktool 2.0.1
I: Checking whether sources has changed...
I: Smaling smali folder into classes.dex...
I: Checking whether resources has changed...
I: Building resources...
I: Copying libs... (/lib)
I: Building apk file...

Sign modified application

You need to sign your modified application. If you don't do it, chances are that you won't be able to install the modified application. To do that, first download SignApk.zip. Unzip the content of the file that you have just downloaded and sign the application as follows:

Usage: signapk publickey.x509[.pem] privatekey.pk8 input.jar output.jar

Below is an example:

$ java -jar signapk.jar certificate.pem key.pk8 android-debug.apk android-debug-signed.apk

You should now be able to install your modified application as follows:

$ adb install android-debug-signed.apk

Debugging

Android Remote debugging

Remote Debugging

gdbserver

Architecture:

To be able to perform remote debugging with gdbserver, you will need Android NDK (different than SDK), which contains gdserver.

On MobiSec, enable port forwarding:

$ cd /opt/mobisec/Android/sdk/platform-tools/
$ ./adb forward tcp:1234 tcp:1234

Transfering gdbserver from MobiSec to the Android Virtual Device (it will be copied to the /data/ directory):

$ ./adb push -p /opt/mobisec/Android/ndk/prebuilt/android-arm/gdbserver/gdbserver /data
Transferring: 409940/409940 (100%)
2609 KB/s (409940 bytes in 0.153s)

Now, start the application to debug on your Android Virtual Device and identify the Process ID with the ps | grep app_name command.

Start gdbserver (we will attach it to the PID gathered previously, 1436 in our example)

# ./adb shell
# cd /data
# chmod 744 gdbserver
# ./gdbserver :1234 --attach 1436

Now start gdb on MobiSec:

# cd /opt/mobisec/Android/ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/
# ./arm-linux-androideabi-gdb
(gdb) target remote :1234
Remote debugging using :1234
0xb6eb1f9c in ?? ()
(gdb) x/10i $pc
=> 0xb6f075cc:  mov     r7, r12
   0xb6f075d0:  cmn     r0, #4096       ; 0x1000
   0xb6f075d4:  bxls    lr
   0xb6f075d8:  rsb     r0, r0, #0
   0xb6f075dc:  b       0xb6f22b28
   0xb6f075e0:  mov     r12, r7
   0xb6f075e4:  mov     r7, #316        ; 0x13c
   0xb6f075e8:  svc     0x00000000
   0xb6f075ec:  mov     r7, r12
   0xb6f075f0:  cmn     r0, #4096       ; 0x1000

IDA Pro remote debugging

To be able to perform remote debugging of an Android application with IDA-Pro, you will need to get android_server from your [[[IDA-Pro]] installation directory (it should be in C:\Program Files (x86)\IDA 6.6\dbgsrv\) and push it to your Android Virtual Device:

$ ./adb push android_server /data

Then ensure the server will be executable and start it (it will listen to tcp:23946 by default):

$ ./adb shell
$ cd /data/
$ chmod 744 android_server
$ ./android_server -v -PmyAwesomePassword

Back to your MobiSec distribution, enter the following commands in a terminal:

$ ./adb forward tcp:23946 tcp:23946
$ sudo apt-get install redir
$ redir --lport=23946 --laddr=192.168.65.130 --cport=23946 --caddr=0.0.0.0

Now, from your Windows VM, open IDA-Pro and go to Debugger > Attach > Remote ARMLinux/Android debugger:

You should be presented with the list of running processes:

Debugging Android native shared libraries

To be able to debug Android native shared libraries, we need to get the system libraries from the Android device (will be used to find debug symbols).

(mobisec)$ mkdir /data/flareon/system_lib/
(mobisec)$ cd /data/flareon/system_lib/
(mobisec)$ adb pull /system/lib

Also eventually get the shared libraries specific to the Android application to debug (in my example libvalidate.so)

(mobisec)$ adb pull /data/app-lib/com.flare_on.flare-1/libvalidate.so

Once this is done, push gdbserver to the Android device:

(mobisec)$ adb push /opt/android-ndk-r10e/prebuilt/android-arm/gdbserver/gdbserver /data/

Enable port forwarding from the Android device to your local debugger:

(mobisec)$ adb forward tcp:1234 tcp:1234

Make gdbserver executable:

(mobisec)$ adb shell
(avd)# cd /data/
(avd)# chmod 744 gdbserver

Start the application to be debugged on the Android device, get its PID and attach the debugger to it:

(avd)# ps | grep flare
u0_a56    1278  57    196576 28304 ffffffff b6ed65cc S com.flare_on.flare
(avd)# ./gdbserver :1234 --attach 1278
Attached; pid = 1278
Listening on port 1234

Now, you can start debugging:

(mobisec)$ cd /opt/mobisec/Android/ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/
(mobisec)$ ./arm-linux-androideabi-gdb
(gdb) target remote :1234
Remote debugging using :1234
0xb6ed65cc in ?? ()

Load symbols from your local directory:

(gdb) set solib-search-path /data/flareon/system_lib/
[REMOVED]
Reading symbols from /data/flareon/system_lib/libvalidate.so...(no debugging symbols found)...done.
Loaded symbols for /data/flareon/system_lib/libvalidate.so
[REMOVED]

Now, check the memory location the shared library has been loaded to:

(gdb) info sharedlibrary
From        To          Syms Read   Shared Object Library
[REMOVED]
0xab143e20  0xab145038  Yes (*)     /data/flareon/system_lib/libvalidate.so
(*): Shared library is missing debugging information.

Display the disassembled code at this location:

(gdb) x/20i 0xab143e20
   0xab143e20:  ldr     r0, [pc, #4]    ; 0xab143e2c
   0xab143e24:  add     r0, pc, r0
   0xab143e28:  b       0xab143da8
   0xab143e2c:  ldrdeq  r4, [r0], -r4   ; <UNPREDICTABLE>
   0xab143e30:  cmp     r0, #0
   0xab143e34:  push    {r3, lr}
   0xab143e38:  popeq   {r3, pc}
   0xab143e3c:  blx     r0
   0xab143e40:  pop     {r3, pc}
   0xab143e44:  mov     r1, r0
   0xab143e48:  ldr     r2, [pc, #12]   ; 0xab143e5c
   0xab143e4c:  ldr     r0, [pc, #12]   ; 0xab143e60
   0xab143e50:  add     r2, pc, r2
   0xab143e54:  add     r0, pc, r0
   0xab143e58:  b       0xab143d9c
   0xab143e5c:  andeq   r4, r0, r8, lsr #3
   0xab143e60:                  ; <UNDEFINED> instruction: 0xffffffd4
   0xab143e64 <Java_com_flareon_flare_ValidateActivity_validate>:       push    {r4, r5, r6, r7, lr}
   0xab143e66 <Java_com_flareon_flare_ValidateActivity_validate+2>:
    ldr r4, [pc, #320]  ; (0xab143fa8 <Java_com_flareon_flare_ValidateActivity_validate+324>)
   0xab143e68 <Java_com_flareon_flare_ValidateActivity_validate+4>:     adds    r5, r0, #0

That's it, we can now set a breakpoint at the entry point:

(gdb) b Java_com_flareon_flare_ValidateActivity_validate
Breakpoint 1 at 0xab143e74
(gdb) c
Continuing.

On the Android device, you should be able to perfom the actions (e.g. provide a password and click on a validate button) that will trigger the breakpoint. Once the breakpoint is reached, the Android application freezes and the debugger shows the following output:

Breakpoint 1, 0xab143e74 in Java_com_flareon_flare_ValidateActivity_validate () from /data/flareon/system_lib/libvalidate.so

From now, you are able to put other breakpoints to continue debugging the native shared library:

(gdb) b *0xab143f3c
Breakpoint 2 at 0xab143f3c
(gdb) c
Continuing.

Breakpoint 2, 0xab143f3c in Java_com_flareon_flare_ValidateActivity_validate () from /data/flareon/system_lib/libvalidate.so
(gdb) x/10i $pc
=> 0xab143f3c <Java_com_flareon_flare_ValidateActivity_validate+216>:
    bcc.n       0xab143ecc <Java_com_flareon_flare_ValidateActivity_validate+104>
   0xab143f3e <Java_com_flareon_flare_ValidateActivity_validate+218>:
    b.n 0xab143f6e <Java_com_flareon_flare_ValidateActivity_validate+266>
   0xab143f40 <Java_com_flareon_flare_ValidateActivity_validate+220>:   adds    r3, r6, r3
   0xab143f42 <Java_com_flareon_flare_ValidateActivity_validate+222>:   ldrb    r3, [r3, #1]
   0xab143f44 <Java_com_flareon_flare_ValidateActivity_validate+224>:   adds    r4, r2, #0
   0xab143f46 <Java_com_flareon_flare_ValidateActivity_validate+226>:   cmp     r3, #0
   0xab143f48 <Java_com_flareon_flare_ValidateActivity_validate+228>:
    beq.n       0xab143ee0 <Java_com_flareon_flare_ValidateActivity_validate+124>
   0xab143f4a <Java_com_flareon_flare_ValidateActivity_validate+230>:   lsls    r4, r2, #8
   0xab143f4c <Java_com_flareon_flare_ValidateActivity_validate+232>:
    ldr r2, [pc, #112]  ; (0xab143fc0 <Java_com_flareon_flare_ValidateActivity_validate+348>)
   0xab143f4e <Java_com_flareon_flare_ValidateActivity_validate+234>:   orrs    r4, r3

Android Anti-debug techniques

Check IMEI

The Android emulator has no IMEI as depicted on the following screenshot. An Android application could verify that the IMEI is not zero, as shown on the following code extract:

To bypass this anti-debugging technique, you should patch the Android application. Below are the steps to do it.

First decompile the APK with apktool:

$ java -jar /data/tools/apktool_2.0.1.jar d NDH.apk
I: Using Apktool 2.0.1 on NDH.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
I: Loading resource table from file: /home/unknown/apktool/framework/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...

Then locate the file in the smali directory where it is checked:

$ grep -R getDeviceId NDH/smali
NDH/smali/com/app/ndh/NDHActivity$2.smali:    invoke-virtual {v1}, Landroid/telephony/TelephonyManager;->getDeviceId()Ljava/lang/String;

Edit the smali file to replace the conditional jump to an unconditional one:

.method public onClick(Landroid/view/View;)V
    .locals 4
    .param p1, "v"    # Landroid/view/View;

    .prologue
    .line 48
    iget-object v1, p0, Lcom/app/ndh/NDHActivity$2;->val$test:Landroid/telephony/TelephonyManager;

    invoke-virtual {v1}, Landroid/telephony/TelephonyManager;->getDeviceId()Ljava/lang/String;

    move-result-object v1

    invoke-static {v1}, Ljava/lang/Integer;->decode(Ljava/lang/String;)Ljava/lang/Integer;

    move-result-object v1

    invoke-virtual {v1}, Ljava/lang/Integer;->intValue()I

    move-result v1

-   if-nez v1, :cond_0
+   goto :cond_0

    .line 49
    iget-object v1, p0, Lcom/app/ndh/NDHActivity$2;->val$builder:Landroid/app/AlertDialog$Builder;

    const-string v2, "Bad Password"

Recompile the Android application:

$ apktool b -o NDH_patched.apk NDH/
I: Using Apktool 2.0.0-RC3 on NDH
I: Checking whether sources has changed...
I: Smaling smali folder into classes.dex...
I: Checking whether resources has changed...
I: Building resources...
I: Copying libs...
I: Building apk file...
I: Copying unknown files/dir...

You will also need to sign the modified version. Refer to this section to check how to achieve this.

Now, uninstall the previous version and reinstall the new one.

Time checks

Another anti-debugging check consists in placing 2 time checks along with a call to the difftime function to check the time elapsed between the 2 checkpoints, as shown below:

  • First checkpoint:
.text:00000F90 MOVS    R0, #0          ; timer
.text:00000F92 BLX     time
.text:00000F96 MOVS    R3, R0          ; R3 = time (1st checkpoint)
  • Second checkpoint:
.text:000010C2 MOVS    R0, #0          ; timer
.text:000010C4 BLX     time
.text:000010C8 MOVS    R3, R0          ; R3 = time (2nd checkpoint)
  • Check time elapsed between the 2 checkpoints:
.text:000010D8 LDR     R2, [SP,#0x140+time1]
.text:000010DA LDR     R3, [SP,#0x140+time0]
.text:000010DC MOVS    R0, R2          ; time1
.text:000010DE MOVS    R1, R3          ; time0
.text:000010E0 BLX     difftime
.text:000010E4 MOVS    R2, R0          ; R2 = difftime (should be 1sec)
.text:000010E4                         ; If >1 sec, app being debugged)

Shared library

Identify arguments

In a shared library, arguments are often as follows:

  • R0: JNIEnv ((a pointer to pointers to function tables).
  • R1: argument from JNI
  • R2: argument passed to the function (e.g. user input)

Example:

.text:00000F60 PUSH    {R4,R5,LR}
.text:00000F62 SUB     SP, SP, #0x134
.text:00000F64 LDR     R4, =(_GLOBAL_OFFSET_TABLE_ - 0xF6A)
.text:00000F66 ADD     R4, PC ; _GLOBAL_OFFSET_TABLE_
.text:00000F68 STR     R0, [SP,#0x140+var_134] ; JNIEnv
.text:00000F6A STR     R1, [SP,#0x140+var_138]
.text:00000F6C STR     R2, [SP,#0x140+var_13C] ; user_input

Resolve JNI functions

Each function is accessible at a fixed offset through the JNIEnv argument. The JNIEnv type is a pointer to a structure storing all JNI function pointers. It is defined as follows:

typedef const struct JNINativeInterface *JNIEnv; 

We can resolve the JNI function by looking at the vtable definition here: https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html.

Here is an example:

.text:00000F68 STR     R0, [SP,#0x140+var_134] ; JNIEnv
.text:00000F6A STR     R1, [SP,#0x140+var_138]
.text:00000F6C STR     R2, [SP,#0x140+var_13C] ; user_input
.text:00000F6E LDR     R3, =(__stack_chk_guard_ptr - 0x3AD0)
.text:00000F70 LDR     R3, [R4,R3] ; __stack_chk_guard
.text:00000F72 LDR     R3, [R3]
.text:00000F74 STR     R3, [SP,#0x140+var_14]
.text:00000F76 LDR     R3, [SP,#0x140+var_134]
.text:00000F78 LDR     R2, [R3]
.text:00000F7A MOVS    R3, #0x2A4
.text:00000F7E LDR     R3, [R2,R3]
.text:00000F80 LDR     R1, [SP,#0x140+var_134]
.text:00000F82 LDR     R2, [SP,#0x140+var_13C]
.text:00000F84 MOVS    R0, R1
.text:00000F86 MOVS    R1, R2
.text:00000F88 MOVS    R2, #0
.text:00000F8A BLX     R3

At offset 0xF7E, R3 holds R2 + 0x2A4 with R2 pointing to the data referenced in R3 (JNIEnv). As we are dealing with 32-bit ARM, we obtain the correct index into the JNI vtable by dividing the offset by 4, which results in:

0x2A4 / 4 = 0xa9 = 169

Looking in this page for 169 leads to the GetStringUTFChars function. Hence, we deduce that the BLX R3 instruction at offset 0xF8A is a call to the GetStringUTFChars function.

GetStringUTFChars
Syntax
const char * GetStringUTFChars(JNIEnv *env, jstring string, jboolean *isCopy);
Description

Returns a pointer to an array of bytes representing the string in modified UTF-8 encoding. This array is valid until it is released by ReleaseStringUTFChars().

If isCopy is not NULL, then *isCopy is set to JNI_TRUE if a copy is made; or it is set to JNI_FALSE if no copy is made.

Linkage Index 169 in the JNIEnv interface function table.
Parameters
  • env: the JNI interface pointer.
  • string: a Java string object.
  • isCopy: a pointer to a boolean.
Returns Returns a pointer to a modified UTF-8 string, or NULL if the operation fails.

Subcategories

This category has the following 2 subcategories, out of 2 total.

Pages in category "Architecture/Android"

The following 9 pages are in this category, out of 9 total.