How to bypass debugger detection in Android/iOS using IDA Pro?

Shubham Sonani
9 min readDec 3, 2023

--

Hello guys, I hope you all are doing well.

In this blog, I will show how to use IDA Pro for Android/iOS pen testing and debug native libraries and bypass certain protection like debugger detection, Frida detection, and root detection or how to find encryption tokens, OTP, secret flag, etc. by debugging and patching native libraries using IDA Pro.

What is IDA Pro?

IDA Pro is a GUI-based commercial tool used as a disassembler and debugger for reverse engineering, malware analysis, and binary analysis.

Link —

  1. IDA Pro 7.5
  2. Debug native library using GDB debugger (open source free).
  3. How to debug android native libraries using JEB decompiler?

What is and why is native code used in apps?

Native codes are written in C/C++ and compiled into binary files that can be linked to mobile applications (in Android one can find it under the libs folder based on your device architecture). iOS apps are developed in Objective-C or Swift programming language. Some developers use this library (.so files) to hide some sensitive code like encryption keys, OTP generation, root detection, debugger detection, Frida detection, or some business logic. Now to connect Java code with Native C/C++ libraries, the JNI framework is used, which allows JVM to call and be called by native libraries. Further in detail, please check this blog to learn about native libraries.

In this tutorial, I am going to show how to bypass debug detection from native libraries using IDA Pro or how to debug native libraries using IDA Pro for Android/iOS Pentesting. I will show this tutorial on Uncrackable Level 2 from OWASP. I will explain the basic scenario, this app has root detection, and debug detection in the native library and there is one method where user input is compared to the string generated in the native library (hidden secret flag). So firstly, we need to bypass the front-end bypass which has debug flag detection set in the Android app and root detection. Secondly, it has debug detection, where the app uses the native library, and it creates one child process and attaches it to the parent process. In Android, an extra single process can get attached to the application running process. That means, if the application is creating a child process and attaching it to the parent process then as an attacker we will not be able to attach any other process like debuggers or Frida server, so we have to patch the native library and bypass this debug detection. Thirdly, we have to get a secret string generated in the native library. This is a hectic task and needs patience.

How to bypass root detection and debug flag detection?

Step 1 — Uncompress the Uncrackable Level 2 apk using apktool. Open the directory in Visual Studio code, which will load all code files, probably the smali code. Try to add a debug flag in the “manifest.xml” file and run the application, we will get the following detection error.

1. App is debuggable

Step 2 — There are two ways to bypass the detection, one is to patch the MainActivity.smali and remove the code that is checking root and debug flag detection. In the below screenshots, we can see that MainActivity.smali contains a few conditions like “b.a(), b.b(), b.c()” -> used to check “root detection” and “a.a(getApplicationContext())” -> used to check whether “App is debuggable”. You can remove these checks and set a debug flag in the “manifest.xml” file and then recompile the code, and resign the application, which will bypass these front-end detections.

2. Protection Checks

Step 3 — I will show another method to bypass these detection. Upon looking, at “a.smali” and “b.smali” we can see it contains the base logic of checking these conditions. If we remove these checks from this logic or return the value of the function as “false”, then after recompiling we can easily bypass these detections. The first and second screenshots are of “a.smali” and the rest are of “b.smali”.

3. Original Code of a.smali
4. Patched code
5. Original Root Detection
6. Original Root Detection
7. Patched Code
8. Patched Code

Step 4 — After recompiling and resigning, we can see that all the front-end detections are bypassed. But when you run “adb shell ps | findstr “owasp” “ we can see that there are two processes, which, I have explained earlier in this blog.

9. Two Processes

Step 5 — For this, we can see in the JADX-GUI, that in MainActivity we can see there is a method “system.loadlibrary(“foo”)”, which will create a child process and attach it to the parent process. Navigate to lib folder -> open the folder of your Android architecture(here x86_64) -> open the foo.so file in IDA Pro. You can open this file in Ghidra to see the actual C/C++ implementations.

Summary — This function, will first call “_fork” to create a new process and store the return value in the “cs:pid” variable. Then the code will process the code by retrieving the parent ID (_getppid) and storing it in the “ebx register”. Then, “_ptrace” is called to attach the parent process with the child process generated in the native library. Upon checking all conditions, the code will jump to “loc_99E” and if no error occurs it will proceed in checking for the parent process. This is quite complex to understand as in assembly language. For better understanding, open the file in Ghidra and check the C/C++ code.

10 — Native Code
11 — Native Code for attaching child process to parent process

Step 6 — To stop the native library from creating a child process and attaching it to the parent process, we need to patch the library using IDA Pro, which then will allow us to attach any debugger process with the running application. Before proceeding, the following are the values in assembly language to NOP(No-operation) out the process, which will skip the process or that code. For NOPping out, there are 4 different values(OPCODE) based on a different architecture.

  1. X86/x86_64–0x90
  2. ARM64–0xD503201F -> 1F 20 03 D5
  3. ARM7–0xE1A00000

Step 7 — Now patching the library, is a complex task, as we need to skip two “_ptrace” method that is used to “attach process” to parent ID. Click on the first “_ptrace” method at this location “0000000000000922” -> go to the “Edit” menu -> go to “Patch program” -> click the “Change byte” method, the original value of this first “_ptrace” method will be “E8 E9 FE FF FF 48 85 C0 75 72 4C 8D 74 24 08 31”. Now to perform No-Operation (NOP), change the bytes to -> 90 90 90 90 90 48 85 C0 75 72 4C 8D 74 24 08 31. This will skip 5 steps in the code of process attachment. Repeat the same process with the other “_ptrace” method. The modified value of the second “_ptrace” method will be -> 90 90 90 90 90 31 D2 89 DF 4C 89 F6 E8 55 FE FF -> these are based on x86_64 architecture. Please refer above values depending on your architecture. Now, go to “Edit” -> click the “Apply patches to input file”.

12 — Original Code
13 — Original code for _ptrace
14 — Replacing the values.
15 — NOPing out the method

Step 8 — Close IDA Pro -> pack the database and save the file. It will save the files as shown below and now copy that “libfoo.so” to the android lib folder under /data/app/<package_name>/libs/ using Android Studio or File Manager in Android. In case, this doesn’t work, replace the file “libfoo.so.i64” with “libfoo.so” and then copy this file to the lib folder.

16 — File Directory

Step 9 — Further, in the native library we can see there is a JNI call -> “Java_sg_vantagepoint_uncrackable2_MainActivity_init” which compares the user input and native library generated secret string, at “_strncmp” method.

Step 10 — Now that we have bypassed all the checks, we can connect the IDA pro debugger with the process. To do this, open the directory of IDA Pro -> open the “dbgsrv” folder -> copy “Android_server” based on your architecture to /data/local/tmp. Now run the app, we can see there is no dialog box, that prevents us from using the app. Go back to the terminal and start the android_server copied after changing the permission using “chmod +x <android_server_name”>. This will start the server at the port — 23946. Now, forward this port using this command “adb forward tcp:23946 tcp:23946”.

17 — Bypassed the protection
18 — Android server connected.

Step 11 — Open the IDA Pro -> open libfoo.so file -> click the function “Java_sg_vantagepoint_uncrackable2_MainActivity_init”. Add breakpoint as shown below. Select the debugger as “Remote Linux Debugger”. Go to the “Debugger” menu -> click the “Process Option” -> add the host name “127.0.0.1”. Again, open the “Debugger” menu -> click the “attach to process” (note- run the application first) -> select the process of uncrackable2. IDA Pro will load all the symbols and connect to the app. Click the play button to resume the app process.

19 — Breakpoints
20 — Attaching to Process

Step 12 — There is one check in the native library that the user input should be of 23 characters then only, the native library will compare the user input with a secret string. On clicking “verify” it will stop at the breakpoint. Use the “debugger” menu to move down or step over the function like normal debugging. Navigate to this method where the value of the “rsp” register will store the value in the “rsi” register at this location “000074876E8AC186”. Now double-click/hover on that “rsi” register and our secret string “Thanks for all the fish” flag is revealed. You can set a breakpoint at “_strncmp” to see the value inside this method. It’s all your choice.

21– character string
22 — Getting the string
23 — secret string

Step 13 — BOOM !! we have successfully bypassed all the protection and found the secret string using IDA Pro.

24 — Got the flag.

That’s all, thank you guys for reading the blog.

#iOSpentesting #androidpentesting #penetrationtesting #android #ios

--

--