The program in the Lab18-05.exe file is Lab07-01.exe packed with WinUpack. When we load this file into PEiD, it’s recognized as being packed with WinUpack 0.39. However, the file’s PE header is badly damaged. If we load it into OllyDbg, IDA Pro, or PEview, we get several errors that make it impossible to view information from the PE header.
We load the file into OllyDbg and see an error stating “Bad or unknown format of 32-bit executable file.” OllyDbg can load the file, but it can’t find the entry point for the unpacking stub and instead breaks at the system breakpoint, which occurs well before the unpacking stub.
Because we have not even reached the unpacking stub, most of our techniques will not
work. We could step-into and step-over instructions carefully until we reach the unpacking stub, and
then work from there, but that would be a long and frustrating process. Instead, we will set
breakpoints on LoadLibrary and GetProcAddress in order to bypass the beginning of the unpacking stub.
We know that loading imported libraries and resolving the imports with GetProcAddress are a couple of the last steps performed by the unpacking
stub. If we can set a breakpoint that is triggered on the last call to GetProcAddress, we’ll be very close to the tail jump, but there’s no way to
know which call to GetProcAddress is last until after the call is
executed. Instead, we set breakpoints on LoadLibrary and GetProcAddress, and use trial-and-error to figure out which call is
last.
We begin by setting a breakpoint on the first instruction of LoadLibrary by pressing CTRL-G and entering LoadLibraryA into the dialog. This
should take us to the first instruction of LoadLibraryA, where we
press F2 to set a breakpoint. We then repeat the process with LoadLibraryW so that we have a breakpoint on both versions of LoadLibrary, and then press F9 to start the program.
We’re using the fact that LoadLibrary is called as a
way to bypass as much of the unpacking stub as possible because we want to keep running the program
until the last call to LoadLibrary. Because we don’t know
which call to LoadLibrary is the last one (until it’s too
late), each time the breakpoint is hit, we continue running the program and note the library being
loaded. If the library being loaded is not the last one, the program will stop very quickly once the
next library is loaded. When the last library is loaded, the program should continue running, and
that is how we know we have found the last call to LoadLibrary.
When we set our breakpoint on LoadLibrary, we see that the first
library loaded is kernel32.dll, followed by advapi32.dll,
and so on. The fifth and sixth calls to LoadLibrary load
commctrl.dll. After the sixth call, we continue running the program, and it
does not stop. The sixth call is the final one.
Now we restart our program. We reset our breakpoint on LoadLibrary, and then run the program until the breakpoint is hit a sixth time and the
parameter is commctrl. Next, we set a breakpoint on GetProcAddress and perform the same procedure to determine which API
function is the last to be resolved with GetProcAddress.
We run the program several times to find out which function is loaded last. After a call to
GetProcAddress with the value InternetOpenA, we see that the program continues to run without hitting our breakpoint
again. Now we restart our program once again. We reset our breakpoints on LoadLibraryA and LoadLibraryW, and run the program
until the final call to LoadLibrary. Then we run the program
until the final call to GetProcAddress.
Resolving the imports is nearly the last step in the unpacking stub. The only task remaining after resolving the imports is the transfer of control to the OEP. The unpacking stub is nearly finished, and we can step through the code to find the OEP.
We step through the rest of the GetProcAddress until
the ret instruction brings us back to the unpacking stub, and
then we continue to step through the code until we see what looks like the tail jump. The next
control transfer instruction is shown here:
00408EB4 STOS DWORD PTR ES:[EDI] 00408EB5 JMP SHORT Lab07_01.00408E9E
This is not the tail jump because it’s relatively short and goes to the following code, which doesn’t look like the start of a program.
00408E9E LODS BYTE PTR DS:[ESI] 00408E9F TEST AL,AL 00408EA1 JNZ SHORT Lab07_01.00408E9E
These instructions form a short loop, and we step through this code until the loop is finished. When the loop is complete, the code falls through to these instructions:
00408EA3 CMP BYTE PTR DS:[ESI],AL 00408EA5 JE SHORT Lab07_01.00408E91
This is also not the tail jump because it is relatively short and the code at the target doesn’t look like the start of a program.
00408E91 POP ECX 00408E92 INC ESI 00408E93 LODS DWORD PTR DS:[ESI] 00408E94 TEST EAX,EAX 00408E96 JE SHORT Lab07_01.00408EB7
The jump at this next block of code goes to a retn
instruction. A normal program would never start with a retn
instruction, so we also know that isn’t the tail jump.
00408EB7 C3 RETN
When we step-over the retn instruction, we see the code
shown in Example C-185.
Example C-185. The OEP for Lab18-05.exe
00401190 ❶PUSH EBP 00401191 MOV EBP,ESP 00401193 PUSH -1 00401195 PUSH Lab07_01.004040D0 0040119A PUSH Lab07_01.00401C58 0040119F MOV EAX,DWORD PTR FS:[0] 004011A5 PUSH EAX 004011A6 MOV DWORD PTR FS:[0],ESP 004011AD SUB ESP,10 004011B0 PUSH EBX 004011B1 PUSH ESI 004011B2 PUSH EDI 004011B3 MOV DWORD PTR SS:[EBP-18],ESP 004011B6 ❷CALL DWORD PTR DS:[40404C] ; kernel32.GetVersion 004011BC XOR EDX,EDX 004011BE MOV DL,AH 004011C0 MOV DWORD PTR DS:[405304],EDX 004011C6 MOV ECX,EAX 004011C8 AND ECX,0FF 004011CE MOV DWORD PTR DS:[405300],ECX 004011D4 SHL ECX,8 004011D7 ADD ECX,EDX 004011D9 MOV DWORD PTR DS:[4052FC],ECX 004011DF SHR EAX,10 004011E2 MOV DWORD PTR DS:[4052F8],EAX 004011E7 PUSH 0 004011E9 CALL Lab07_01.00401B21 004011EE POP ECX 004011EF TEST EAX,EAX 004011F1 JNZ SHORT Lab07_01.004011FB 004011F3 PUSH 1C 004011F5 CALL Lab07_01.00401294 004011FA POP ECX 004011FB AND DWORD PTR SS:[EBP-4],0 004011FF CALL Lab07_01.00401976 00401204 ❸CALL DWORD PTR DS:[404048] ; kernel32.GetCommandLineA 0040120A MOV DWORD PTR DS:[4057F8],EAX 0040120F CALL Lab07_01.00401844 00401214 MOV DWORD PTR DS:[4052E0],EAX 00401219 CALL Lab07_01.004015F7
This looks like the OEP for several reasons:
It’s a relatively far jump.
The code starts with a push ebp at ❶, which indicates the beginning of a function.
The code in this function calls GetVersion at ❷ and GetCommandLineA at
❸, which are commonly called at the very beginning of a
program.
Having identified the OEP, we use Plugins ▸ OllyDump ▸
Dump Debugged Process to dump the unpacked program. Next, we load the program into IDA
Pro, but, unfortunately, we get some errors. Apparently, the program’s file headers are not
fully repaired. However, IDA Pro has labeled the main function
anyway, so we can analyze the program even though the PE file isn’t fully
reconstructed.
The biggest roadblock is that we don’t have any import information. However, we
can easily spot the calls to imported functions by looking for calls to data locations. For example,
let’s look at the main method, as shown in Example C-186.
Example C-186. The main method for unpacked
Lab18-05.exe
00401000 sub esp, 10h
00401003 lea eax, [esp+10h+var_10]
00401007 mov [esp+10h+var_10], offset aMalservice ; "MalService"
0040100F push eax
00401010 mov [esp+14h+var_C], offset sub_401040
00401018 mov [esp+14h+var_8], 0
00401020 mov [esp+14h+var_4], 0
00401028 ❶call dword_404004
0040102E push 0
00401030 push 0
00401032 call sub_401040
00401037 add esp, 18h
0040103A retnThe call at ❶ jumps out as a call to an imported
function. You can click the DWORD to view the address of the
imported functions for this program, as shown in Example C-187.
Example C-187. Imported functions that have not been recognized by IDA Pro
00404000 dword_404000 dd 77E371E9h 00404004 dword_404004 dd 77E37EB1h 00404008 dword_404008 dd 77DF697Eh 0040400C align 10h 00404010 dword_404010 dd 7C862AC1h 00404014 dword_404014 dd 7C810BACh
To make the unpacked code easier to analyze, we turn to OllyDbg to find out which function is
stored at those locations. The easiest way to identify which imported function is stored at a given
address in OllyDbg is to change the value of any register to the address you want to look up. For
example, to identify the imported function stored at dword_404004, double-click eax and enter the value
0x77E37EB1. We see that OllyDbg labels the address as Advapi32.StartServiceCtrlDispatcherA. We can rename the DWORD address in IDA Pro to StartServiceCtrlDispatcherA. Now whenever the malware calls the recently renamed address,
it will be labeled as StartServiceCtrlDispatcherA, instead of
dword_404004.
We can repeat this process for each imported function, and then we will have a program that we can analyze in IDA Pro as if it were never packed. We still have not created a working version of the unpacked file, but it doesn’t really matter, because we can analyze the file without it. Looking at the file, we can tell that this is the same as Lab07-01.exe.