quybri jeremia rainor

0 views
Skip to first unread message

Yufei Labbe

unread,
Aug 2, 2024, 9:15:51 PM8/2/24
to fibcentmidsysd

At my previous employer we used a third party component which basically was just a DLL and a header file. That particular module handled printing in Win32. However, the company that made the component went bankcrupt so I couldn't report a bug I'd found.

So I decided to fix the bug myself and launched the debugger. I was surprised to find anti-debugging code almost everywhere, the usual IsDebuggerPresent, but the thing that caught my attention was this:

At the first glance I just stepped over the routine which was called twice, then things just went bananas. After a while I realized that the bit twiddling result was always the same, i.e. the jmp eax always jumped right into the mov eax, 0x310fac09 instruction.I dissected the bytes and there it was, 0f31, the rdtsc instruction which was used to measure the time spent between some calls in the DLL.

The idea behind this is, that the copy protection does not exist in ordinary x86 code and as such cannot be disassembled. You cannot remove the entire emulator either because this would remove core functionality from the program.

I've used MIPS32 for emulation because it was so easy to emulate (it took just 500 lines of simple C-code). To make things even more obscure I didn't used the raw MIPS32 opcodes. Instead each opcode was xor'ed with it's own address.

I've been a member of many RCE communities and have had my fair share of hacking & cracking. From my time I've realized that such flimsy tricks are usually volatile and rather futile. Most of the generic anti-debugging tricks are OS specific and not 'portable' at all.

In the aforementioned example, you're presumably using inline assembly and a naked function __declspec, both which are not supported by MSVC when compiling on the x64 architecture. There are of course still ways to implement the aforementioned trick but anybody who has been reversing for long enough will be able to spot and defeat that trick in a matter of minutes.

So generally I'd suggest against using anti-debugging tricks outside of utilizing the IsDebuggerPresent API for detection. Instead, I'd suggest you code a stub and/or a virtual machine. I coded my own virtual machine and have been improving on it for many years now and I can honestly say that it has been by far the best decision I've made in regards to protecting my code so far.

Spin off a child process that attaches to parent as a debugger & modifies key variables. Bonus points for keeping the child process resident and using the debugger memory operations as a kind of IPC for certain key operations.

You basically take some part of your object code, and convert it to your own bytecode format. Then you add a small virtual machine to run this code. Only way to properly debug this code will be to code an emulator or disassembler for your VM's instruction format. Of course you need to think of performance too. Too much bytecode will make your program run slower than native code.

If you really want to code the VM solution yourself (there are good programs for sale), don't use just one instruction format. Make it polymorphic, so that you can have different parts of the code have different format. This way all your code can't be broken by writing just one emulator/disassembler. For example MIPS solution some people offered seems to be easily broken because MIPS instruction format is well documented and analysis tools like IDA can already disassemble the code.

I know that I have wasted way too much time dealing with vendors that have complicated licensing schemes that only cause problems for the customers and the vendors. It is always my recommendation to avoid those vendors. Working at a nuclear power plant we are forced to use certain vendors products and thus are forced to have to deal with their licensing schemes. I wish there was a way to get back the time that I have personally wasted dealing with their failed attempts to give us a working licensed product. It seems like a small thing to ask, but yet it seems to be a difficult thing for people that get too tricky for their own good.

I second the virtual machine suggestion. I implemented a MIPS I simulator that (now) can execute binaries generated with mipsel-elf-gcc. Add to that code/data encryption capabilities (AES or with any other algorithm of your choice), the ability of self-simulation (so you can have nested simulators) and you have a pretty good code obfuscator.

The nice feature of choosing MIPS I is that 1) it's easy to implement, 2) I can write code in C, debug it on my desktop and just cross-compile it for MIPS when it's done. No need to debug custom opcodes or manually write code for a custom VM..

My personal favourite was on the Amiga, where there is a coprocessor (the Blitter) doing large data transfers independent from the processor; this chip would be instructed to clear all memory, and reset from a timer IRQ.

Calculated jumps in the middle of a legitimate looking but really hiding an actual instruction instructions are my favorite. They are pretty easy to detect for humans anyway, but automated tools often mess it up.

Allows you to use developer tools on websites which utilize anti-debug techniques such as:1. Repeated calls to debugger2. getter traps3. Console.clear() spamWhat it does:1. console.clear and console.table are set to do nothing.2. console.log is overridden to filter DOM elements, functions, and objects with custom getters3. Functions containing debugger calls are removed4. setInterval checks for anti debugging scriptsTroubleshooting Tips:1. Open devtools in new window, otherwise sites may detect using viewport width/height information2. Disable source maps in devtools. Some sites will detect when source maps are automatically loaded through the devtools3. Open devtools first, and then navigate to the sitePlease report any issues to the Github: -Anti-Debug/issuesThis extension may break some websites which rely on certain specific logging tools. It is recommended to enable the extension only when needed.Debug icons created by Alfian Dwi Hartanto - Flaticon

For this post I use AVG as an example, however I have tried to avoid focusing too much on this one product as many other AV solutions and security products use the exact same technique, so the same principals can be applied.

DebugActiveProcess is responsible for kicking off a debugging session with a target process, and takes the PID of the process as an argument. If we review the documentation for the call on MSDN, we come across this:

The debugger must have appropriate access to the target process, and it must be able to open the process for PROCESS_ALL_ACCESS. DebugActiveProcess can fail if the target process is created with a security descriptor that grants the debugger anything less than full access. If the debugging process has the SE_DEBUG_NAME privilege granted and enabled, it can debug any process.

ObRegisterCallbacks is something that I first came across in the gaming/modding community, where it has been used to thwart attempts to modify or inject functionality into games when bypassing anti-cheat and DRM drivers.

But why would this be useful in preventing debugger access to our AV process? Well one way to prevent the DebugActiveProcess call from succeeding is by filtering out access rights requested during the NtOpenProcess call. By removing the ability for the debugger to request PROCESS_ALL_ACCESS on a target process, we loose the ability to debug a process. This method would also explain the error we saw earlier in WinDBG.

So essentially if only these permissions are set, then additional checks are not performed and the OpenProcess call continues unfiltered. However, if any permission other than the above whitelisted permissions are requested, a number of further checks are performed and ultimately the permissions are filtered out before the call returns:

Malware authors have always looked for new techniques to stay invisible. This includes, of course, being invisible on the compromised machine, but it is even more important to hide malicious indicators and behavior during analysis. Malware authors assume that once the malicious file is out in the wild, its lifetime clock is ticking, and eventually, it will be detected by researchers and thoroughly analyzed.

In this blog post, I will present some examples of popular anti-debugging techniques. The techniques covered here constitute an exhaustive list of the core concepts and are based on what the research team here at Deep Instinct encounters on a daily basis.

Process Environment Block (PEB) is a data structure that resides in the user space of each process and contains data about the related process. It is designed to be accessed by the WinAPI modules, but access is not limited exclusively to them; one can access the PEB directly from memory, as we will see next:

OutputDebugString is used to send a string to a debugger. If it fails, an error occurs. Malware can use SetLastError with a defined value, then run OutputDebugString (if it fails, it will overwrite the last error value), then check the last error with GetLastError.

To sum up, Anti-Debugging can be reached by the essential features of the operating system or by the natural behavior of the debugger itself. Malware authors keep challenging researchers with new techniques in order to delay the reverse engineering process.

Protecting your IT project from threats is a vital part of the entire product lifecycle. Starting from the early development stages, your team should consider what measures to apply against different threats, including malicious reverse engineering.

Reverse engineering means dissecting a program to understand its functionality and underlying algorithms. While it can have legitimate uses like malware analysis, attackers often use reverse engineering to steal intellectual property, crack software licenses, or obfuscate malicious code.

This is where anti-debugging techniques step in. They act as a shield, making it more difficult for attackers to reverse engineer your software. By employing these techniques, you can significantly increase the time and effort required to crack your program, deterring casual attackers and making it a more daunting task even for seasoned ones.

c01484d022
Reply all
Reply to author
Forward
0 new messages