A few weeks ago a friend sent me some packed malware that he was having trouble with. The malware had a number of anti-debugging techniques employed that made it difficult to unpack and my friend was in a desperate rush to create solid host-based indicators for the malware. After spending about 30 minutes trying to find all the anti-debugging techniques, I decided to try opening it in WinDbg, because most of the anti-debugging techniques were specifically targeting OllyDbg. OllyDbg is the most popular debugger for unpacking and in our book we devote an entire chapter to unpacking using OllyDbg. However, in cases like this you can use WinDbg to unpack malware and all the same strategies apply.
The basic steps for unpacking are the same with WinDbg as they are with OllyDbg; you locate the original entry point, dump the process to disk, and repair the import table. For security reasons I can’t show the exact malware in question, but I can demonstrate unpacking with WinDbg using one of the labs from out book. To demonstrate unpacking with WinDbg, we’ll unpack Lab 18-3 from our book. In the book we use OllyDbg, but this time we’ll use WinDbg.
The first step is to load the packed program into WinDbg.
ntdll!DbgBreakPoint: 7c90120e cc int 3
WinDbg stops at the system breakpoint instead of the entry point of the packed program. We’re not interested in code that runs before the entry point so we set a breakpoint on the entry point and run the program until the breakpoint is hit.
0:000> bp $iment(image00400000) 0:000> g Breakpoint 0 hit eax=00000000 ebx=7ffdb000 ecx=0012ffb0 edx=7c90e4f4 esi=00fdf55c edi=7c911440 eip=00405130 esp=0012ffc4 ebp=0012fff0 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 0:00> u eip image00400000+0x5130: 00405130 eb06 jmp image00400000+0x5138 (00405138) 00405132 6877150000 push 1577h 00405137 c3 ret 00405138 9c pushfd 00405139 60 pushad 0040513a e802000000 call image00400000+0x5141 (00405141)
Once we hit the entry point of our packed program we’ll use the same strategy that we used with OllyDbg in order to find the original entry point (OEP) of our program. We step over the pushad instruction and then set a hardware breakpoint on access. To set a hardware on access breakpoint in WinDbg, we use the command “ba r 4 esp” (if you don’t know why OEP is important or why this strategy works for finding it, see Chapter 18 of our book). The ba command stands for break on access, the ‘r’ parameter specifies that the breakpoint should be triggered when the address is read. The ‘4’ parameter specifies the size of the operand that will be read, and esp specifies the address on which to set the breakpoint.
0:000> p eax=00000000 ebx=7ffdb000 ecx=0012ffb0 edx=7c90e4f4 esi=00fdf55c edi=7c911440 eip=00405138 esp=0012ffc4 ebp=0012fff0 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 image00400000+0x5138: 00405138 9c pushfd 0:000> p eax=00000000 ebx=7ffdb000 ecx=0012ffb0 edx=7c90e4f4 esi=00fdf55c edi=7c911440 eip=00405139 esp=0012ffc0 ebp=0012fff0 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 image00400000+0x5139: 00405139 60 pushad 0:000> ba r 4 esp
After we set the breakpoint we go until the breakpoint is hit.
0:000> g Breakpoint 1 hit eax=00000000 ebx=7ffdb000 ecx=0012ffb0 edx=7c90e4f4 esi=00fdf55c edi=7c911440 eip=00407550 esp=0012ffc4 ebp=0012fff0 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 image00400000+0x7550: 00407550 50 push eax 0:000> u eip image00400000+0x7550: 00407550 50 push eax 00407551 6877154000 push offset image00400000+0x1577 (00401577) 00407556 c20400 ret 4
A few instructions away we see the push 0x401577 followed by a ret instruction. This code is using the ret instruction to transfer execution to a different address far away. This looks like the tail jump, so let’s step to see where it goes. When we assemble the code at that location it looks like the entry point for an executable and we can conclude that we’ve located OEP.
0:000> u eip L10 image00400000+0x1577: 00401577 55 push ebp 00401578 8bec mov ebp,esp 0040157a 6aff push 0FFFFFFFFh 0040157c 68c0404000 push offset image00400000+0x40c0 (004040c0) 00401581 683c204000 push offset image00400000+0x203c (0040203c) 00401586 64a100000000 mov eax,dword ptr fs:[00000000h] 0040158c 50 push eax 0040158d 64892500000000 mov dword ptr fs:,esp 00401594 83ec10 sub esp,10h 00401597 53 push ebx 00401598 56 push esi 00401599 57 push edi 0040159a 8965e8 mov dword ptr [ebp-18h],esp 0040159d ff1530404000 call dword ptr [image00400000+0x4030 (00404030)]
Now we need dump the file to disk and repair the import table. Unlike OllyDbg, WinDbg has no built in tools for this, so we’ll use the simple and freely available Import Reconstructor tool. With our malicious program stopped at OEP, we open Import Reconstructor. We use the dropbox on the top of the GUI to select the Lab18-03.exe process. Import Reconstructor contains a built-in process dumping tool, although it is hard to find. Click on the middle window and select the Advanced Command -> Select code section(s) as shown in the screen shot below.
That brings up a new dialog box that has a button labeled “Full Dump”. Click that button to create a dump of the file on disk. Then enter the OEP from our WinDbg analysis (1577) in the OEP field and click “IAT Autosearch”. This locates the import address table from the original executable. Then click “Get Imports” to locate the individual imported functions. To repair the imports in the dumped file click “Fix Dump” and select the filename that you used for the Full Dump command earlier. Once you select the filename, the file on disk is repaired so that the import table is correct. At this point you’re done. The file is unpacked and you can analyze with IDA Pro, strings, or any other static analysis tools.