As most of us know already, reverse engineering relies on attempting to understand how a system works without having any insights into how exactly the tasks are accomplished and, oftentimes, without access to the source code. Anti-reverse engineering is an art in itself.
Anti-reverse engineering offers techniques which make it difficult to reverse engineer an application. Unfortunately, these techniques are often exploited by hackers to prevent detection. It is a good tool for hackers to gain access or knowledge about specific applications, based on which attacks can take place in the future. But a great tool for attackers would mean bad news for businesses. Well, to make things easier for you, we are having a quick crash course here on anti-reverse engineering. Read on!
The job of a cyber security professional is to make it difficult for a hacker to understand a system. With inline functions, this is exactly what is achieved. When functions are marked inline, the executables become more bloated causing confusion to the hackers.
Usually, executing a section of code should take a certain amount of time. For smaller sections, the time taken will be far lesser. If it is observed that a certain section is taking a longer time than anticipated, it is likely to have a debugger attached. The most common examples of functions used are: IA-32 RDTSC instruction (real-time stamp counter), GetTickCount, timeGetTime, and QueryPerformanceCounter. A developer has to just decide which functions need to be protected through the timing attack strategy, and surround the different blocks of code in a timing block, and compare those to a set of programmer set limits. He/she can then exit the program if the time limit is exceeded.
There are some specific anti-reverse engineering methods that use the peculiarities of Windows OS to help developers protect their codes from hackers. Some of the key functions are:
- ProcessDebugFlags: This is passed to the NtQueryProcessInformation function which will return the inverse of EPROCESS->NoDebugInherit. When a debugger is present, the function will return FALSE. A sample code:
- Debug Object Handle: When a process is debugged, a debug object is created as a rule. A similar handle is created and queried using NtQueryInformationProcess. This handle shows the presence of a debugger. Sample code:
- Thread hiding: Known as HideThreadFromDebugger, the class prevents debuggers from receiving events from threads that have NtSetInformationThread with the HideThreadFromDebugger class called on it. Breakpoints and exiting programs are very common. Sample code:
- OutputDebugString: This one looks for errors, which occur only if there are no active debuggers. Sample code:
- BlockInput: This simply blocks and prevents the mouse and keyboard messages from interacting with a said application. Sample code:
They exploit the Windows process environment and management system to implement protection. Some of the techniques are:
- Open Process: It exploits process privileges to detect a debugger. If the process privileges are not correctly reset by the debugger, the process receives the SeDebugPrivilege set.
- Parent process: Here, the parent process’s (Explorer.exe) process ID is extracted and compared with the child process.
- Self-debugging: The main process creates a child process that debugs the parent process. One additional advantage is that it becomes more difficult for other debuggers to get attached to the process.
- UnhandledExceptionFilter: This is called when no other handlers are present. If a debugger is found, the process exits instead of resuming the operation.
- NtQueryObject: It returns a slew of crucial information about the host system and the running process.
This is a technique to counter dumping, where a protected executable’s snapshot is saved onto a disk. The prevention of dumping can take place via multiple methods like the below:
- Nanomites: This technique replaces branch instructions and JCC instructions with INT 3 breakpoints while storing information like jump destination, CPU flags and size of the jump in an encrypted table. Instructions are placed in unreachable parts of codes, and entries are placed in Nanomite tables.
- SizeOfImage: Here, the size of the field in PEB is increased at runtime, and is defeated by reversing applications through the process of enumerating all pages with the MEM_IMAGE flag, starting at the application’s ImageBase page.
- Stolen Bytes (Stolen Code): Here, codes or bytes from the original process are extracted and encrypted in random places in the packing code. The area where the bytes are placed is replaced with code that will jump to a dynamically allocated buffer that has the decrypted bytes stolen from the original code. Hence, the term “stolen”.
- Virtual machines: This technique focuses on creating a unique virtual machine for every executable that is protected, thereby preventing generic attacks.
IA-32 Instruction Exploits
Debuggers often have problems dealing with IA-32 instructions, and this feature is exploited by this set of techniques. A list of some of the techniques:
- Interrupt 2D: This is a general-purpose debugger where no exception will occur if a debugger is present.
- Stack segment: This manipulates the stack segment through push ss and pop ss
- Instruction Prefixes: Here, the debugger skips the _try prefix and handles the INT 1.
This technique is used to check if WinDBG is running in the background. It is still at a nascent stage.
As can be seen above, anti-reverse engineering techniques are pretty useful to limit hackers from gaining access to an application. The only issue is that hackers are getting more sophisticated when it comes to attacks. They learn the tricks of the trade far quicker than companies can react. Hence, a robust proactive approach towards cyber security is better than a passive one. Though of course the above techniques are useful, a well-thought through application security strategy, with a special focus on mobile applications, can prove to be a smarter move. And we can help. Get in touch with us today to know more!