Desktop Apps: How to deal with malware that uses "Anti-Debugging" techniques
Introduction
One of the common challenges that a reverse engineer encounter is to deal with “Anti-Debugging” tricks that malware or packers employ. Generally, malwares, packers use these tricks to make reversing even harder.
This part will try to explain one of the trick malware uses –ie., call to “IsDebuggerPresent” function which is exported by kernel32.dll. This function returns “NON-Zero” if the calling process is being debugged; And Returns “Zero” if the calling process is not being debugged.
More about this function here à https://msdn.microsoft.com/en-us/library/windows/desktop/ms680345(v=vs.85).aspx
IsDebuggerPresent
A piece of malware could call this function, know if there is a debugger and change its behavior accordingly. Eg., It can terminate itself right away OR Behave like a normal software OR Try to attack/kill the debugger itself. – OllyDBG had one in the past, where in a app can use “OutputDebugString” with a malformed output to crash OllyDBG.
So, it would be worth it to find a way to bypass these kind of tricks, in this part we will try to deal with “IsDebuggerPresent” function calls.
There are quite lot of ways to deal with an App that calls this function, here I have listed few (I will not go in detail about every trick, to keep this blog as precisely as possible)
- Find the “call” to “IsDebuggerPresent” function, while exe is on disk, using a Hex editor and “NOP” it out
- Find when exactly app calls “IsDebuggerPresent” NOP the function call, while app is in memory.
- Modify the return value in EAX register (most of the functions store their return value here) after function is called.
Above said methods might look relatively simple, straight forward – but the problem arises when app tries to call this multiple times OR the code is very heavily obfuscated and you are not able to find the calls to the functions.
You can also, try to re-write or modify the “IsDebuggerPresent” function itself in physical memory, so that it always returns “ZERO” no matter whatever the calling application is, how many times it calls ; so that you can be rest assured that this function is going to return “0” – saying no debugger is attached to the calling application.
This method will prove useful:
- When you have multiple pieces of EXE’s to analyze.
- Or you have a heavily obfuscated code, and you are not able to find “call” due to lack of time.
- You have a EXE which is protected by multi-threaded packer – which calls this function, would like to save some time and focus on what really matters a lot.
Now, let’s talk in detail.
Quick fix
This quick fix consists of following steps.
- 1. Attach a kernel mode debugger like Windbg to the target machine.
- 2. Find “IsBeingDebugged” function in virtual memory.
- Find the corresponding Physical Memory address of IsBeingDebugged()
- Modify the original code to always return zero. (Note: It’s a good idea to your modifications to a minimum, to reduce the possibility of a crash)
1) Attach a Kernel Debugger:
Attach a kernel debugger to the target machine, look for IsBeingDebugged()’s Virtual address.
- Steps to get Windbg here à /en-us/windows-hardware/drivers/debugger/
- Open WinDbg as Administrator. On the File menu, choose Kernel Debug. In the Kernel Debugging dialog box, open the Local Tab. Click OK.
- Once you have debug prompt, run below command
. reload
2) Find “IsBeingDebugged” function in virtual memory.
Once it completes “.reload”, type in the below command exactly to get the corresponding VM Address.
Here is a sample output from my demo env.
lkd> x kernelbase!IsDebuggerPresent
7527fc66 KERNELBASE!IsDebuggerPresent (<no parameter info>)
As you can see from above this, function is sitting at 7527fc66 (Note: this is virtual memory)
Let’s try to disassemble
lkd> u 7527fc66
KERNELBASE!IsDebuggerPresent:
7527fc66 64a118000000 mov eax,dword ptr fs:[00000018h]
7527fc6c 8b4030 mov eax,dword ptr [eax+30h]
7527fc6f 0fb64002 movzx eax,byte ptr [eax+2]
7527fc73 c3 ret
7527fc74 3d170000c0 cmp eax,0C0000017h
7527fc79 0f8423830000 je KERNELBASE!CreateFileW+0xc0 (75287fa2)
7527fc7f 3d9a0000c0 cmp eax,0C000009Ah
7527fc84 0f8418830000 je KERNELBASE!CreateFileW+0xc0 (75287fa2)
Let’s dump the bits at this address
lkd> db 7527fc66
7527fc66 64 a1 18 00 00 00 8b 40-30 0f b6 40 02 c3 3d 17 d......@0..@..=.
7527fc76 00 00 c0 0f 84 23 83 00-00 3d 9a 00 00 c0 0f 84 .....#...=......
7527fc86 18 83 00 00 6a 03 ff 15-40 10 27 75 83 c8 ff e9 ....j...@.'u....
7527fc96 51 9d ff ff 90 90 90 90-90 8b ff 55 8b ec 83 ec Q..........U....
7527fca6 1c 6a 00 6a 1c 8d 45 e4-50 6a 00 ff 75 08 ff 15 .j.j..E.Pj..u...
7527fcb6 28 13 27 75 85 c0 0f 8c-91 b8 01 00 8b 45 f0 c9 (.'u.........E..
7527fcc6 c2 04 00 83 ef 40 81 ff-00 04 00 00 0f 83 e5 bc .....@..........
7527fcd6 01 00 8b 86 94 0f 00 00-85 c0 0f 84 64 45 01 00 ............dE..
Just remember these set of opcodes, we might need this later ç
3) Get the corresponding Physical Address:
You can use !PTE <Virtual Address> to get the corresponding address in physical memory.
lkd> !pte 7527fc66
VA 7527fc66
PDE at C0601D48 PTE at C03A93F8
contains 00000000AD6F4867 contains 00000000D7B9B125
pfn ad6f4 ---DA--UWEV pfn d7b9b -G--A--UREV
Physical address should be PFN (highlighted above)+ Last 3 digits of the VM’s Address.
So, Our Physical address here d7b9bc66
More about !PTE command here è https://blogs.msdn.microsoft.com/ntdebugging/2010/02/05/understanding-pte-part-1-lets-get-physical/
Let’s dump the bytes from this physical address and compare it with the bytes we got previously (While we dumped from Virtual Memory), to confirm we got the region in Physical memory
lkd> !db d7b9bc66
#d7b9bc66 64 a1 18 00 00 00 8b 40-30 0f b6 40 02 c3 3d 17 d......@0..@..=.
#d7b9bc76 00 00 c0 0f 84 23 83 00-00 3d 9a 00 00 c0 0f 84 .....#...=......
#d7b9bc86 18 83 00 00 6a 03 ff 15-40 10 27 75 83 c8 ff e9 ....j...@.'u....
#d7b9bc96 51 9d ff ff 90 90 90 90-90 8b ff 55 8b ec 83 ec Q..........U....
#d7b9bca6 1c 6a 00 6a 1c 8d 45 e4-50 6a 00 ff 75 08 ff 15 .j.j..E.Pj..u...
#d7b9bcb6 28 13 27 75 85 c0 0f 8c-91 b8 01 00 8b 45 f0 c9 (.'u.........E..
#d7b9bcc6 c2 04 00 83 ef 40 81 ff-00 04 00 00 0f 83 e5 bc .....@..........
#d7b9bcd6 01 00 8b 86 94 0f 00 00-85 c0 0f 84 64 45 01 00 ............dE..
Sure enough, we have found the corresponding physical memory.
4) Modify the original code at the physical address to always return zero.
We will modify from its physical memory – so that we do not have to modify it every time a process gets created, terminated.
We’ll modify this
75C7FC66 > 64:A1 18000000 MOV EAX,DWORD PTR FS:[18]
75C7FC6C 8B40 30 MOV EAX,DWORD PTR DS:[EAX+30]
75C7FC6F 0FB640 02 MOVZX EAX,BYTE PTR DS:[EAX+2]
75C7FC73 C3 RETN
To
7501FC66 > 64:A1 18000000 MOV EAX,DWORD PTR FS:[18]
7501FC6C 8B40 30 MOV EAX,DWORD PTR DS:[EAX+30]
7501FC6F 0FB600 MOVZX EAX,BYTE PTR DS:[EAX+0]
7501FC72 90 NOP
7501FC73 C3 RETN
Above modification will make sure, IsDebuggerPresent function reads the first member (which will be zero) in _PEB structure rather than IsBeingDebugged Flag which is at the offset of +2
Note: You can also come up any with creative ways to make this function return 0 always, above code is the idea that I have come up with!
In my lab I must edit the following order
11th offset (from the beginning of the function) to make it “00” – that’s 0xd7b9bc66 + 0xB = 0xd7b9bc71 to 00
And
12th offset (from the beginning of the function) to make it “90” – that’s 0xd7b9bc66 + 0xC = 0xd7b9bc72 to 90
This can be accomplished by running the below commands:
!eb 0xd7b9bc71<Space>00
!eb 0xd7b9bc72<Space>90
** **
And now, we are good to verify our patch work.
Dump the function
1) You can either confirm this by dumping the function from physical address like below and verify it
lkd> !db d7b9bc66
#d7b9bc66 64 a1 18 00 00 00 8b 40-30 0f b6 00 90 c3 3d 17 d......@0..@..=.
#d7b9bc76 00 00 c0 0f 84 23 83 00-00 3d 9a 00 00 c0 0f 84 .....#...=......
#d7b9bc86 18 83 00 00 6a 03 ff 15-40 10 27 75 83 c8 ff e9 ....j...@.'u....
#d7b9bc96 51 9d ff ff 90 90 90 90-90 8b ff 55 8b ec 83 ec Q..........U....
#d7b9bca6 1c 6a 00 6a 1c 8d 45 e4-50 6a 00 ff 75 08 ff 15 .j.j..E.Pj..u...
#d7b9bcb6 28 13 27 75 85 c0 0f 8c-91 b8 01 00 8b 45 f0 c9 (.'u.........E..
#d7b9bcc6 c2 04 00 83 ef 40 81 ff-00 04 00 00 0f 83 e5 bc .....@..........
#d7b9bcd6 01 00 8b 86 94 0f 00 00-85 c0 0f 84 64 45 01 00 ............dE..
Call function
2) You can write a very simple app which will call this function, display if it detects the debugger.
References
- Assembly Source here è http://www.openrce.org/reference_library/anti_reversing_view/14/IsDebuggerPresent()%20Windows%20API/
- MASM 32 here è http://www.masm32.com/