Logo
blank Skip to main content

Pentesting Android Applications: Tools and Step-by-Step Instructions

QA

Mobile applications often process sensitive data, which is the key target of many cybercriminals. When working with such data, developers must do their best to ensure its protection. One way to improve the security of a mobile app is to perform mobile application penetration testing.

To find flaws in their application code, developers need at least basic skills in reverse engineering and pentesting Android applications. In this article, we discuss different methods an attacker might use to hack your apps. We also explain how challenges from the Open Web Application Security Project (OWASP) Mobile Security Testing Guide (MSTG) can help you in mobile penetration testing of Android applications and what tools you can use to solve them.

In this article, we discuss how to pentest Android applications and what tools to use for improving the security of your mobile apps. This article will be helpful to Android developers who want to know more about mobile security penetration testing services and improve the security of their apps.

Strengthen your app’s security with reverse engineering

Android is quite a developer-friendly operating system (OS). Unlike other mobile OSs, Android is an open-source platform on which you can activate Developer Options and sideload applications without jumping through too many hoops. Furthermore, Android allows developers to explore its source code at the Android Open Source Project and to modify operating system functionality however they like.

However, working with Android applications also means you’ll need to deal with Java bytecode and Java native code. Some developers may see this as a disadvantage. Android developers use the Java Native Interface to improve application performance, support legacy code, and, of course, confuse those who try to look inside their apps.

When building mobile applications, one of the top priorities for a development team is to ensure a high level of data security. Developers should do their best to prevent cybercriminals from getting access to a user’s sensitive information.

Some try improving the security of their mobile apps with the help of third-party solutions. However, when working with third-party products, it’s critical to configure them properly. A misconfigured or improperly used solution will be of no help, no matter how expensive it is.

Others try to hide the application’s functionality and data in the native layer. In some cases, they build Android applications in such a way that execution jumps between the native layer and the runtime layer.

There are also developers who use more sophisticated methods, such as reverse engineering. This technique is quite helpful when it comes to ensuring proper protection of an application’s sensitive data. That’s why it’s best for a developer to have at least some basic skills in reverse engineering:

  • Unpacking APK files
  • Patching .smali files
  • Patching .so libraries
  • Using debugging tools
  • Working with frameworks for dynamic code analysis

Related services

Professional Reverse Engineering

With these skills and expertise, mobile app developers will have a better chance of detecting code flaws that might be used by attackers. For instance, in order to break into your application, hackers may use the same techniques that quality assurance (QA) specialists use when they test an application’s security and performance:

  • Dynamic analysis is used to find possible ways to manipulate application data when the application is running. For example, hackers may try to crack your app by skipping the multi-factor code check during login.
  • Static analysis is used for studying an already packaged application and detecting code weaknesses without having direct access to the source code. With static analysis, we don’t look at the application’s behavior at runtime, as we do during dynamic analysis. Hackers may use static analysis to detect the use of a weak encryption algorithm, for instance. To learn how to fix such issues, explore our article about methods of encryption in Android applications.

Developers have their methods of protecting against these types of code analysis. For example, source code can be obfuscated to protect it against static analysis: developers can change the names of application methods and classes, add calls to additional functions, and encrypt lines of code.

Note: While code obfuscation helps strengthen application code against static analysis-based attacks, it also increases the risk of introducing new bugs. Best reverse engineering tools can help you see whether code has been obfuscated and ensure thorough application testing.

There are also several methods for securing mobile applications from dynamic code analysis. In particular, developers can:

  • prevent the app from launching on rooted devices
  • use libraries that prevent the app from launching in developer mode and deny connections for dynamic analysis with Frida and other frameworks
  • apply additional protections against repacking and resigning the app.

These tasks are easy for experienced reverse engineers. Less experienced developers might need a bit of practice before pentesting Android apps with reverse engineering techniques. Thankfully, OWASP provides numerous challenges for training and improving your software reverse engineering skills. 

Later in this article, we give step-by-step solutions for two OWASP Mobile Security Testing Guide CrackMe challenges: UnCrackable App for Android Level 1 and UnCrackable App for Android Level 2. Solving these challenges will help you better understand how to improve penetration testing for mobile apps and enhance security of your Android solutions. We recommend you try to solve them on your own before reading on. But first, let’s take a look at the tools and frameworks you’ll need for reverse engineering an Android app.

Read also:
Web Application Penetration Testing: Minimum Checklist Based on the OWASP Testing Guide

A basic toolset for Android reverse engineering

Before you start solving the OWASP CrackMe challenges for Android developers, you need to make sure you have two things:

  • Knowledge of Android environments. You need to have some experience working with Java and Linux environments as well as with Android kernels.
  • The right set of tools. Working with bytecode and native code running on a Java virtual machine (JVM) requires specific tools.

In this section, we list and briefly describe tools that you can use to solve the OWASP CrackMe challenges and upgrade your reverse engineering skills.

Note: For the purposes of this article, we’ve chosen only tools and frameworks that are either free or have free trial versions.

  1. Android Studio — The official integrated development environment (IDE) for Android. This is the primary IDE for building native Android apps; it includes an APK analyzer, code editor, visual layout editor, and more. In particular, we’ll use the command-line Android Debug Bridge (adb) tool.
  2. Apktool — This is a popular free tool for reverse engineering closed, third-party, and binary Android applications. It can disassemble Java bytecode to the .smali format as well as extract and disassemble resources from APK archives. Also, you can use Apktool for patching and changing the manifest file.

Note: Application code is stored in the APK file, which contains the .dex file with Dalvik binary bytecode. Dalvik is a data format understandable by the Android platform but completely unreadable for humans. So for a developer to be able to work with .dex files, they need to be converted to (and from) a readable format, such as .smali.

  1. Cutter — An open-source cross-platform framework that provides a customizable, easy-to-use reverse engineering platform. This framework is powered by radare2 and is supported by a large community of professional reverse engineers.
  2. Hex Workshop Editor — A popular set of Windows hexadecimal development tools. This toolset makes editing binary data nearly as simple as working with regular text documents. Hex Workshop Editor is commercial but has a free trial version.

Note: Hex Workshop Editor can only be used on Windows. If you’re working with a Linux-based virtual machine, you can choose any Linux hex editor.

  1. dex2jar — A free tool for converting bytecode from the .dex format into Java class files.
  2. JD-GUI — One of the tools created by the Java Decompiler project. This graphical utility makes Java source code readable, displaying it as Java class files.

Read also:
How to Reverse Engineer an iOS App and macOS Software

  1. Mobexler — An Elementary-based virtual machine for iOS and Android pentesting. Mobexler comes with a set of preinstalled tools and scripts for testing the security of a mobile app, including some of the tools from this list.
  2. Java Debugger (jdb) — A free command-line tool for debugging Java classes.

Note: In Android applications, debugging can be performed on two layers:

  • Runtime layer — Java runtime debugging can be performed with the help of the Java Debug Wire Protocol (JDWP).
  • Native layer — Linux/Unix-style debugging can be performed based on ptrace.

JDWP is a standard debugging protocol that helps the debugger communicate with the target JVM. This protocol is supported by all popular command-line tools and Java IDEs, including Eclipse, JEB, IntelliJ, and, of course, jbd.

The JDWP debugger allows you to explore Java code, set breakpoints in Java methods, and check and modify both local and instance variables. It’s often used for debugging regular Android apps that doesn’t make many calls to native libraries.

  1. GNU Debugger (gdb) — A useful tool for analyzing an application’s code. 

We used these tools to solve two reverse engineering challenges for Android apps. In the next section, we’ll give you a basic Android pentesting tutorial based on the standard OWASP challenges.

Read also:
Top 7 Methods of Data Encryption in Android Applications

Solving UnCrackable Apps challenges

We’ll show you how to solve two OWASP MSTG CrackMe challenges: UnCrackable App for Android Level 1 and UnCrackable App for Android Level 2. These apps were specifically designed as reverse engineering challenges with secrets hidden in the code. Our task is to find these secrets. While solving these challenges, we’ll use static analysis for analyzing the decompiled code and dynamic analysis for modifying some of the application parameters.

Solving UnCrackable App for Android Level 1

First, we need to look inside our training application. A regular Android application is, in fact, a properly packaged APK file containing all the data the application needs to operate normally. 

To look at the application from the inside and solve this challenge, we’ll need:

  • adb for communicating with our mobile device and the training application
  • Apktool for disassembling the APK files of our training app into separate .smali classes
  • jdb for debugging our training app
  • dex2jar for converting APK files to the JAR format
  • JD-GUI for working with the JAR files

Now let’s move on to solving the first challenge.

Read also:
Anti Debugging Protection Techniques with Examples

We’ll begin by installing UnCrackable-Level1.apk on our device or emulator with the following command:

ShellScript
adb install UnCrackable-Level1.apk

We’ll solve this challenge and debug the Release Android app with the help of the jdb tool. Follow along to find the hidden secret.

1. Unpack the application and decode the manifest file using Apktool:

ShellScript
apktool d -s  UnCrackable-Level1.apk -o temp

2. Using a text editor, put the app into debugging mode by changing the manifest file and setting android:debuggable to "true":

XML
<application android:allowBackup="true" android:debuggable="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme">

3. Use Apktool to repack the APK file:

ShellScript
apktool b temp -o UnCrackable-Level1-Repackage.apk

4. Resign the newly created APK file. You can do this using a bash script for resigning Android apps.

5. Install the new APK file on your device or emulator with the following command:

ShellScript
adb install UnCrackable-Level1-Repackage-Resigned.apk

At this point, we face the first big challenge. The UnCrackable App is designed to resist debugging mode. So when we enable it, the app simply shuts down.

You’ll see a modal dialog with a warning. The dialog can be closed by tapping OK, but this will be your last action before the app is terminated.

pentesting android 1

Fortunately enough, there’s a way to fix this.

6. Launch the application on your device or emulator in Wait For Debugger mode. This mode allows you to connect the debugger to the target application before the application runs its detection mechanism. As a result, the app won’t deactivate the debugging mode. 

Run the application in Wait For Debugger mode with this command:

ShellScript
adb shell am start -D -n "owasp.mstg.uncrackable1/sg.vantagepoint.uncrackable1.MainActivity"

You’ll see the following dialog window:

pentesting android 2

7. Now display the process IDs (PIDs) of all processes that run on the connected device:

ShellScript
adb shell ps

8. And list only debuggable processes:

ShellScript
adb jdwp

9. Open a listening socket on your host machine and forward its incoming TCP connections to the JDWP transport of a chosen debuggable process:

ShellScript
adb forward tcp:4321 jdwp:PID

10. Note that attaching the debugger (in our case, jdb) will cause the application to resume from the suspended state, and we don’t want that. We need to keep the app suspended for a while to explore it in detail. To prevent the process from resuming, pipe the suspend command into jdb:

ShellScript
< "%JAVA_HOME%\bin\jdb" -connect com.sun.jdi.SocketAttach:hostname=localhost,port=4321
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
Initializing jdb ...
< suspend
All threads suspended.
<

11. Now we need to bypass that moment when the application crashes in the runtime after you tap OK. Decompile the APK file to review the application code using dex2jar and JD-GUI:

1) Use the dex2jar tool to convert the original APK file to a JAR file:

ShellScript
d2j-dex2jar.bat -f UnCrackable-Level1.apk

2) Using the JD-GUI tool, open the newly created JAR file:

C#
public class MainActivity extends Activity {
private void a(String str) {
    AlertDialog create = new Builder(this).create();
    create.setTitle(str);
    create.setMessage(“This is unacceptable. The app is now going to exit.”);
    create.setButton(-3, “OK”, new OnClickListener() {
        public void onClick(DialogInterface dialogInterface, int i) {
            System.exit(0);
        }
    }};
    create,setCancelable(false);
    create.show();
}

After reviewing the code, you’ll see that the MainActivity.a method displays the message:

"This is unacceptable..."

The MainActivity.a method creates an alert dialog and sets a listener class for the onClick event. The listener class has a callback method that shuts down the application when the user taps the OK button. To prevent a user from canceling the dialog, the system calls the setCancelable method.

At this point, our best-case scenario is to suspend the application in a state where the secret string we’re looking for is stored in a plaintext variable. Unfortunately, that’s impossible unless you can figure out how the application detects root and tampering.

12. Try tampering with the runtime a little bit to bypass application termination. Set a method breakpoint on android.app.Dialog.setCancelable when the application is still suspended, then resume the app:

ShellScript
All threads suspended.
< stop in android.app.Dialog.setCancelable
Set breakpoint android.app.Dialog.setCancelable
< resume
All threads resumed.
<
Breakpoint hit: “thread-main”, android.app.Dialog.setCancelable(), line-1, 205 bci-0
 
main[1] _

13. The application is suspended at the first setCancelable method instruction. You can use the locals command to print the arguments passed to the setCancelable method:

ShellScript
main[1] locals
Method arguments:
flag = true
Local variables:
main[1] _

As you can see in the code above, the system called the setCancelable(true) method, so that’s not the call we need. Let’s resume the process with the resume command:

ShellScript
main[1] resume
All threads resumed.
<
Breakpoint hit: “thread-main”, android.app.Dialog.setCancelable(), line-1, 205 bci-0
 
main[1] locals
Method arguments:
flag = false
Local variables:
main[1] _

We’ve reached a call to the setCancelable method with the false argument. At this point, we need to use the set command to change the variable to true and resume the app:

ShellScript
main[1] set flag = true
flag = true = true
main[1] resume
All threads resumed.
<
Breakpoint hit: “thread-main”, android.app.Dialog.setCancelable(), line-1, 205 bci-0
 
main[1] _

Continue setting flag to true each time you reach a breakpoint until the alert window is finally displayed. It may take about five or six attempts before you see this window. At this point, you should be able to cancel the app without causing the application to terminate — just tap the screen next to the dialog window and it will close.

pentesting android 3

14. Finally, it’s time to extract the secret string. Look at the application code once more. You’ll notice that the string we’re looking for is decrypted with the Advanced Encryption Standard and is compared with the string the user enters into the message box. The application uses the equals method of the java.lang.String class to determine if the string input matches the secret string.

Now set a method breakpoint on java.lang.String.equals, enter random text in the edit field, and tap VERIFY. You can read the method argument with the locals command once you reach the breakpoint:

ShellScript
<> stop in java.lang.String.equals
Set breakpoint java.lang.String.equals
<
Breakpoint hit: “thread-main”, java.lang.String.equals(), line=944 bc1=2
 
main[1] locals
Method arguments:
anObject = “b”
Local variables:
main[1] cont
<
Breakpoint hit: “thread-main”, java.lang.String.equals(), line=944 bci=2
 
main[1] locals
Method arguments:
anObject = “i”
Local variables:
main[1] _
………
main[1] locals
Method arguments:
anObject = “I want to believe”
Local variables:
main[1] _

Bingo! Our secret string is “I want to believe.” You can easily check if that’s right by entering this phrase into the message box and clicking the VERIFY button.

pentesting android 4

Well done! Now we can move to solving the second challenge. 

Read also:
How to Reverse Engineer Software (Windows) the Right Way

Solving UnCrackable App for Android Level 2

As in the previous task, there’s a secret string hidden somewhere in the code of this training application, and our goal is to find it. However, in this case, our secret string may have some traces of the native code.

To solve this challenge, we’ll use the following tools:

  • adb for communicating with our mobile device
  • Apktool for disassembling APK files
  • Cutter for working with libraries
  • gbd for analyzing application code
  • Hex Workshop Editor for editing binary data
  • dex2jar for converting APK files to the JAR format
  • JD-GUI for working with JAR files

Let’s move right to solving this challenge:

1. Using dex2jar and JD-GUI, analyze the UnCrackable-Level2.apk file.

1) First, convert the APK file to a JAR file with dex2jar:

ShellScript
dex2jar-2.xu003ed2j-dex2jar.bat -f UnCrackable-Level2.apk

2) Then open the converted file with JD-GUI. Start with the MainActivity class:

C#
public class MainActivity extends c {
private CodeCheck m;
 
static {
    System.loadLibrary(“foo”);
}
/* access modifies changed from: private */
private void a(String str) {
    AlertDialog create = new Builder(this).create();
    create.setTitle(str);
    create.setMessage(“This is unacceptable. The app is now going to exit.”);
    create.setButton(-3, “OK”, new OnClickListener() {
        public void onClick(DialogInterface dialogInterface, int i) {
                System.exit(0);
        }
    }};
    create,setCancelable(false);
    create.show();
}
 
private native void init();
/* access modifies changed from: protected */
public void onCreate(Bundle bundle) {
init();
if (b.a() || b.b() ||b.c()) {
    a(“Root detected!”);
}
if (a.a(getApplicationContext())) {
    a(“App is debuggable!”);
}
new AsyncTask<void, String, String>() {
/* access modifiers changed from protected
renamed from: a */

The system loads a native foo library. Then we call the native init function in the onInit method of the MainActivity class.

Next, the CodeCheck class imports a function from the bar library:

C#
public class CodeCheck {
private native boolean bar(byte[] bArr);
 
public boolean a(String str) {
    return bar(str.getBytes());
}
}

The a method of CodeCheck (which is most likely obfuscated) uses this function for password verification.

C#
public void verify(View view) {
String str;
String obj = ((EditText)
findViewById(R.id.edit_text)).getText().toString();
AlertDialog create = new Builder(this).create();
if (this.m.a(obj)) {
    create.setTitle(“Success!”);
    ctr =This is the correct secret.”;
} else {
    create.setTitle(“Nope...”);
    str =Thats not it. Try again.”;
}
create.setMessage(str);
create.setButton(-3, “OK”, new OnClickListener() {
    public void onClick(DialogInterface dialogInterface, int i) {
        dialogInterface.dismiss();
    }
}};
create.show();
}

2. Use Apktool to extract the foo library and then decompile it to machine code with Cutter:

ShellScript
apktool d UnCrackable-Level2.apk -o temp

In the temp/lib folder, look for the files with the .so extension. There should be files for different types of processor architectures: arm64-v8a, armeabi-v7a, x86, and x86_64. Choose the library that matches the processor architecture of the device or emulator that you’re using. You can check the architecture of your device with the CPU-Z application. 

Analyze the chosen library with Cutter. Start by checking the functions tab to understand the library’s functionality.

For example:

  • The pthread_create and pthread_exit functions suggest that the functions library uses threads.
pentesting android 5
  • If you see the strncmp string comparator, that’s probably used for validating the passed password. If we’re lucky, the string we’re looking for is hardcoded and the inputted password is compared to it. However, judging by the use of the thread, we may assume that the library calculates the secret string at runtime.
pentesting android 6

Let’s analyze the strncmp function. Use Cutter to find the address of the strncmp function (0xffb).

pentesting android 7
  • ptrace is a Linux system call used for debugging. In our case, however, it’s used as an anti-debugging technique.
pentesting android 8

Let’s analyze the functionality of ptrace. This call is used twice: first at 0x777 and then at 0x7a7. In the first case, it’s the current process’s PID, and in the second case, it’s the PTRACE_ATTACH flag, 0x10.

The ptrace function call attaches an Android process to itself. However, an Android process can only have one process attached to it. This means that for now, we can’t attach gdb to an Android process.

pentesting android 9

3. The problem of attaching gdb can be handled by NOP-ing out two ptrace syscalls. You can change the byte using Hex Workshop Editor. Insert the address of the ptrace function into the go-to field of the tool and change the value:

pentesting android 10

4. Open the original APK file as a zip archive and replace the original libfoo.so library with the changed one.

5. Resign the modified APK file using the same script as in the previous challenge.

6. Install the application on a rooted device: 

ShellScript
adb install UnCrackable-Level2_resigned.apk

Now when you launch the app, you’ll see an alert saying that it can’t be used because the device has been rooted.

pentesting android 11

7. Remove root and debugger detection from the application code. To do so, find the MainActivity.smali file in the decompiled APK file and remove all contents of the a method.

The a method is called when a problem is detected. It shows a user an alert dialog and then exits the application. To prevent our app from exhibiting such behavior, we need to patch the a method.

This is what the MainActivity.smali file looks like after removing the a method content:

ShellScript
.method private a(Ljava/lang/String;)V
    .locals 3
 
    return-void
.end method

8. Now repack the APK file with the modified MainActivity.smali file:

ShellScript
apktool b temp -o UnCrackable-Level2-Repackage.apk

9. Next, open the UnCrackable-Level2_resigned.apk file as a ZIP archive and replace the classes.dex file in it with the patched one from our repackaged APK file.

10. Resign the initial UnCrackable-Level2.apk file. At this step, our APK file has two replaced patched files: MainActivity.smali from step 8 and libfoo.so from step 4.

11. Install the resigned APK file on your device:

ShellScript
adb install UnCrackable-Level2_resigned.apk

This time, our app launches without any warning messages.

pentesting android 12

Next, we need to attach gdb to an Android process. Start with setting up the gdb server on your device or emulator.

12. Open a root shell on your device:

ShellScript
adb shell
su

13. Using the command-line interface on your computer, copy the gdb server into the /data/local/tmp/gdb-server folder:

ShellScript
adb push ./gdb-server-android-arm /data/local/tmp/gdb-server

14. Now use the command line on your mobile device to launch the gdb server:

ShellScript
# cd /data/local/tmp/gdb-server
# chmod 755 gdb-server
# ./gdb-server

15. Make sure the server is running:

ShellScript
# ./gdb-server --version

16. Determine the PID of the application:

ShellScript
# ps | grep 'owasp.mstg.uncrackable2'
u0_a59    4925  1159  1250756 37536 ffffffff b7561ed5 S owasp.mstg.uncrackable2

As you can see, the PID of our application is 4925.

17. Attach the gdb server to the chosen Android process:

ShellScript
# ./gdb-server :8888 --attach 4925
Attached; pid = 4925
Listening on port 8888

18. Using the command line on the PC, forward port 8888:

ShellScript
adb forward tcp:8888 tcp:8888

19. On your PC, launch the gdb client.

20. Attach the gdb client to the remote process:

ShellScript
(gdb) target remote :8888
Remote debugging using :8888

21. Using the command line on your device, display the list of all imported libraries:

ShellScript
cat /proc/4925/maps
pentesting android 13

22. Now let’s get back to the strncmp function (0xffb) address that we found in step 2. The breakpoint should be set to the address 0x000000b2d8dffb. On your PC, run this command from the launched gdb client:

ShellScript
(gdb) b *0x000000b2d8dffb
Breakpoint 1 at 0xb2d8dffb
(gdb) c
Continuing.
 
Thread 1 “nt.uncrackable2” hit Breakpoint 1, 0x000000b2d8dffb in Java_sg_vantagepoint_uncrackable2_CodeCheck_bar () from target: /data/app/sg.vantagepoint.uncrackable2-1/lib/x86/libfoo.so
(gdb) info registers
x0    0xb2d834470        547311273344
x1    0xb2dd49170        549585261184
x2    0x17    23

23. Similarly, run this command from your gdb client on your PC to dump the contents of the registers:

ShellScript
(gdb) info registers

As you can see in step 22, the application passes the addresses of the inputted password and the calculated secret string to the strncmp function as two parameters, passed in the x0 and x1 registers.

Finally, we need to dump these two strings:

ShellScript
(gdb) x/s 0xb2d834470    
0xb2d834470    : “12345678901234567890123”
(gdb) x/s 0xb2dd49170
0xb2dd49170: “thanks for all the fish”
(gdb) _

Note: The positive outcome of solving this challenge may be unstable due to the differences in processor architectures of the Android devices and the versions of the tools used. 

We did it! Our second secret string is “thanks for all the fish.”

Related services

Specialized Quality Assurance & Testing Solutions

Conclusion

Android app penetration testing is a complex yet important stage of mobile application development. Developers need to build a sophisticated mobile application pentest methodology and make sure that sensitive data their apps work with will remain secure no matter what.

To find hidden flaws and vulnerabilities, developers need to be able to look at their applications from the inside. This requires basic reverse engineering skills, which you can acquire by solving the OWASP MSTG CrackMe challenges.

At Apriorit, we have experienced teams of Android developers, QA specialists, and reverse engineers who know how to make your mobile apps robust and secure. Get in touch with us to pentest your Android app and make it resistant to attacks.

Read more about how reverse engineering can augment your project below!

Tell us about your project

Send us a request for proposal! We’ll get back to you with details and estimations.

By clicking Send you give consent to processing your data

Book an Exploratory Call

Do not have any specific task for us in mind but our skills seem interesting?

Get a quick Apriorit intro to better understand our team capabilities.

Book time slot

Contact us