Malwares are all around us. Newer ways of not getting detected are being employed by attackers all around the globe. Using evasion techniques, these attackers can be invisible to threat analysis professionals as well. By hiding indicators of malicious activities, such attacks continue for a longer period. One of the common methods employed to stop attacks is anti-debugging, which ensures that a program is not running under a debugger. It ensures that an application’s core functions are protected, and attacks don’t surface easily, thereby making it even more difficult to reverse engineer the code. Once a debugger is detected, the problem doesn’t stop there. The minute the malware gets to know that it has been detected by a debugger, it changes its behavior, adjusts the code execution path or simply modifies itself to evoke a crash. Once the system crashes, it is back to square one for the cyber security analysts and experts.
When developers are working on their codes, they use some best practices to protect their code and ultimately the application that they are writing the code for. A debugger helps them test their code in controlled environments against known attacks, change certain variables, upgrade configurations etc. Bugs, errors and security loopholes can be tested and fixed, so that data is ultimately protected. However, even though they are useful for developers, these debuggers can themselves open up the playground for attackers to observe the application being tested under specific conditions, thereby helping them better their attack techniques. Anti-debugging can help change the behavior of the apps to protect themselves and, in most cases, slow down the reverse engineering process. Some common methods used in anti-debugging techniques are:
- Analysis of data being exchanged between applications using a packet sniffer
- Disassembly of software binary code so it gets added in the assembly language
- Recreating source code by decompiling binary or byte-code
Best Techniques of Anti-Debugging
Looking for modifications in the code
Debugging usually relies on modifying the original code by changing instructions from code strings. Thus, the app can be programmed to identify debuggers in such cases and have defensive techniques ready.
Adding exceptions in code
When certain exceptions are not executed, it most likely would mean that a debugger is present. A good cybersecurity professional should be able to manage this
When an application is running, it is possible to create a new process that can get attached to the debugger of the original parent process. Now if another debugger is running, an alert will get generated. This might be a more strenuous process since it is to be started from scratch, but it is worth the effort.
Using system API calls
APIs are commonly provided with every application. Using these would be the easiest to know whether a debugger is running in the background.
Exploiting bugs in popular debuggers
Debuggers are not always perfect and this technique uses that point to its advantage. Every application has some flaw, and so is the case with every debugger. By introducing data values or specific instructions that can cause a crash or make the system behave in a certain manner, a debugger can be revealed.
Using hardware breakpoint detection
Breakpoints are meant to test for certain signals, and similarly, when encountered by a developer, they can help detect a debugger too
Relying on timing-based detection
Common sense tells us that an application is usually expected to execute in a set time interval. Developers are usually aware of the different steps and procedures which make up an application and its features. So a lag in execution of certain steps can give a clue towards a debugger. Of course, when multiple applications are running or interacting with each other, with different time intervals for action between them, this could get a bit tricky. The best way out would be to keenly observe the time intervals between processes and interactions to see any lags/delays which could be attributed to a debugger
Keeping an eye on certain libraries
Debuggers usually come with their specific set of libraries. A simple look at the list of libraries attached to an application can reveal the presence or absence of a debugger
Mixing and matching technique
A good mix of different techniques is what will ultimately come in handy when you want to make the lives of attackers a tad bit difficult. Reverse engineering a code will become all the more difficult when you confuse the attacker.
Some other functions which help with anti-debugging are:
- IsDebuggerPresent: One of the simplest methods, this function checks if a calling program is begin debugged by a user-mode debugger. Calling this function will help stop the debugger. A sample code:
std::cout << “Stop debugging program!” << std::endl;
- PEB (Process Environment Block): This is used in the Windows OS. Sample code for calling the function:
// Current PEB for 64bit and 32bit processes accordingly
return (PVOID)__readgsqword(0x0C * sizeof(PVOID));
return (PVOID)__readfsdword(0x0C * sizeof(PVOID));
- TLS Callback: The main function of an application is usually the place which is targeted by attackers and reverse engineering experts. Hence, performing a debugger check there may not be that fruitful. Also, the checks can be erased. The TLS Callback is thus a better place for checking for debuggers. Sample code:
#pragma section(“.CRT$XLY”, long, read)
__declspec(thread) int var = 0xDEADBEEF;
VOID NTAnopPI TlsCallback(PVOID DllHandle, DWORD Reason, VOID Reserved)
var = 0xB15BADB0; // Required for TLS Callback call
MessageBoxA(NULL, “Stop debugging program!”, “Error”, MB_OK | MB_ICONERROR);
__declspec(allocate(“.CRT$XLY”))PIMAGE_TLS_CALLBACK g_tlsCallback = TlsCallback;
- NtGlobalFlag: When it comes to system tracking, debugging and control, the global variable NtGlobalFlag is useful. Sample code:
FLG_HEAP_VALIDATE_PARAMETERS (0x40)Some other techniques are:
- Heap Flags and ForceFlags
- Trap Flag Check
- CheckRemoteDebuggerPresent and NtQueryInformationProcess
- ProcessDebugPort 0x07
- ProcessDebugObjectHandle 0x1E
- ProcessDebugFlags 0x1F
- ProcessBasicInformation 0x00
- Debugger detection using the FindWindow function
- Time calculation approach
- Self-modifying code
- Memory debug code
The above methods help to identify debuggers and protect applications from hackers. But, often prevention is better than cure (I am sure you have heard this a lot of times). This truly applies to anti-debugging strategies. It is always best to stop attacks even before they take place. Mobile app security is often an area that is overlooked. But given the vast amount of information and important data present in mobile applications, it is an area that requires more focus. With AppSealing, developers can focus on developing high-quality applications which are rich in features, while also staying in the safety net with AppSealing’s robust runtime application self protection (RASP) and threat analytics modules. Be it gaming apps, movie apps, apps in the ecommerce departments or the ones which help customers take care of their finances, our solutions help companies stay a step ahead of attackers. Real-time security checks and indicators help fix the problems even before they arise. We offer cloud-based pay-as-you-go features which require zero lines of code. Our solutions are affordable, dynamically priced and support both android and ios applications.