BLOG
- Posted on: Apr 28, 2022
- By Ajay Pandita
- 3 Mins Read
- Last updated on: Sep 17, 2024
Threat actors utilize numerous anti-analysis techniques, one of the most common of which is Anti-Debugging, to make post-detection analysis more difficult. In the malware they create and the ways they use to avoid detection and analysis by cybersecurity experts and solutions, threat actors have shown to be more inventive.
On the other hand, anti-debugging is a burden to malware analysts since it can slow down the process of reverse-engineering the code, making it more difficult to figure out how it works.
What is Anti-Debugging: How to Protect your code?
Static analysis and dynamic analysis techniques are often used by hackers to learn more about an application and how it works. This assists them in identifying attack vectors that can be exploited to uncover app flaws. In most cases, this is accomplished by attaching a debugger to the app.
Debuggers are useful in the development process, but they can also be used for malicious purposes. They can provide hackers access to the app's code and the logic behind the code. Anti-debugging is a way of preventing debuggers from attaching to the application.
Top 3 Anti-Debugging Techniques
Anti-debugging techniques allow programs to defend themselves even when they are not being developed in a secure environment. To enable an app to identify the existence of a debugger, a number of alternative strategies are used. We'll look at some of the most essential ones here.
Anti-JDWP Debugging
JDWP stands for "Java Debug Wire Protocol". It is the debug protocol used for communication between a debugger and the Java virtual machine. Java-based Android applications are not difficult to debug. Some of the methods to debug them are listed below.
1) Using the Debuggable Flag
The "android: debuggable" attribute in the manifest file of the Android app determines if the debugging using JDWP is enabled for the app. If the value is set to true, that means the debugging is allowed. This means the app has been tampered with.
The code below shows how we can check the same programmatically.
public static boolean isDebuggable(Context context){ return ((context.getApplicationContext().getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0); } |
2) Timer Checks
Debugging an app slows down its process execution. We can analyze the time difference between executions to confirm if the debugger is attached or not. Programmatically, we can check the value of Debug.threadCpuTimeNanos as it is the time that the app is using to execute.
The following code can be used to do timer checks.
static boolean detect_threadCpuTimeNanos(){ long start = Debug.threadCpuTimeNanos(); for(int i=0; i<1000000; ++i) continue; long stop = Debug.threadCpuTimeNanos(); if(stop - start < 10000000) { return false; } else { return true; } } |
3) JDWP Related Data Structures
"DvmGlobals" structure in Dalvik machine can be used to access the global virtual machine state. It gives information about the debugger. Similarly, Android RunTime (ART) has a similar structure which can be exploited to get information about debuggers. One such example of the structure is shown below.
struct DvmGlobals { bool jdwpAllowed; // debugging allowed for this process? bool jdwpConfigured; // has debugging info been provided? JdwpTransportType jdwpTransport; bool jdwpServer; char* jdwpHost; int jdwpPort; bool jdwpSuspend; Thread* threadList; bool nativeDebuggerActive; bool debuggerConnected; /* debugger or DDMS is connected */ bool debuggerActive; /* debugger is making requests */ JdwpState* jdwpState; }; |
Anti-Native Debugging
Apart from JAVA debuggers, there is another category of debuggers based on pTrace similar to Linux. To prevent these types of debuggers from attaching to the applications, we can try the below methods.
1) Monitoring TracerPid
We can monitor the TracerPid field that can be found in the status file of the process. If the value of TracerPid is 0, then that means there's no attached debugger. But if the value of TracerPid is anything other than 0, then that means some debugger is trying to attach to the process.
We can see an example of checking the status file in the following example.
$ adb shell ps -A | grep com.example.test u0_a271 17657 573 4302108 50600 ptrace_stop 0 t com.example.test $ adb shell cat /proc/17657/status | grep -e "^TracerPid:" | sed "s/^TracerPid:\t//" TracerPid: 11839 $ adb shell ps -A | grep 11839 u0_a271 11839 11837 14024 4548 poll_schedule_timeout 0 S lldb-server |
2) Creating a Dummy Process
Another way we can prevent ptrace based debuggers is by creating a dummy process. That way if any debugger tries to attach to the process then it'll be forced to attach to the dummy child process.
Below is an example of code that can be used to fork a child process.
void fork_and_attach() { int pid = fork(); if (pid == 0) { int ppid = getppid(); if (ptrace(PTRACE_ATTACH, ppid, NULL, NULL) == 0) { waitpid(ppid, NULL, 0); ptrace(PTRACE_CONT, NULL, NULL); } } } |
Final Thoughts
Almost every software attack starts with reverse-engineering the code to figure out how it works and locate vulnerabilities to exploit. Attackers can tamper with the code, evade security constraints, change app behavior, and steal secret keys and sensitive data once they understand the internal dynamics of the target application. Utilizing the above-mentioned anti-debugging techniques will help in preventing this exploitation of vulnerabilities and will be a way to keep your app secure.
Ajay Pandita
Subscribe now for growth-boosting insights from Appknox
We have so many ideas for new features that can help your mobile app security even more efficiently. We promise you that we wont mail bomb you, just once in a month.