다음을 통해 공유


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)

  1. Find the “call” to “IsDebuggerPresent” function, while exe is on disk, using a Hex editor and “NOP” it out
  2. Find when exactly app calls “IsDebuggerPresent” NOP the function call, while app is in memory.
  3. 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:

  1. When you have multiple pieces of EXE’s to analyze.
  2. Or you have a heavily obfuscated code, and you are not able to find “call” due to lack of time.
  3. 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. 1. Attach a kernel mode debugger like Windbg to the target machine.
  2. 2. Find “IsBeingDebugged” function in virtual memory.
    1. Find the corresponding Physical Memory address of IsBeingDebugged()
    2. 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

  1. Assembly Source here è http://www.openrce.org/reference_library/anti_reversing_view/14/IsDebuggerPresent()%20Windows%20API/
  2. MASM 32 here è http://www.masm32.com/