There aren’t many useful strings in the malware other than import functions and the
strings cmd and cmd.exe.
When you run this malware, it appears to do nothing other than terminate.
You must rename the malware to peo.exe for it to run properly.
This malware uses three different anti-debugging timing techniques: rdtsc, GetTickCount, and QueryPerformanceCounter.
If the QueryPerformanceCounter check is successful, the
malware modifies the string needed for the program to run properly. If the GetTickCount check is successful, the malware causes an unhandled exception that crashes
the program. If the rdtsc check is successful, the malware will
attempt to delete itself from disk.
The anti-debugging timing checks are successful because the malware causes and catches an exception that it handles by manipulating the Structured Exception Handling (SEH) mechanism to include its own exception handler in between two calls to the timing checking functions. Exceptions are handled much more slowly in a debugger than outside a debugger.
The malware uses the domain name adg.malwareanalysisbook.com.
As noted in the lab description, this malware is the same as Lab09-02.exe, except with added anti-debugging techniques. A good place to start is by doing Lab 9-2 Solutions or by reviewing your answers to refresh your memory of this malware’s capabilities.
Static analysis of Lab16-03.exe shows it to be similar to
Lab09-02.exe, with few strings visible other than cmd.exe. When we load Lab16-03.exe into IDA Pro, we see that much of
the same functionality is present in this malware. Example C-150 shows the malware using gethostbyname to resolve a domain and using port 9999, as with Lab 9-2 Solutions.
Example C-150. Same calls from Lab 9-2 Solutions, which resolve a domain name and get a port in network byte order
004015DB call ds:gethostbyname ... 0040160D push 9999 ; hostshort 00401612 call ds:htons
Since this malware uses DNS and connects out over port 9999, we set up a dynamic environment using ApateDNS and Netcat. However, when we first run the malware, it doesn’t perform DNS or connect on port 9999. Recall from Lab 9-2 Solutions that the name of the malware needed to be ocl.exe. Let’s see if that is the case here.
Two strings appear to be created on the stack at the start of the malware’s main function: 1qbz2wsx3edc and
ocl.exe. We rename the malware to ocl.exe to
see if it connects out. It doesn’t, which means the name ocl.exe must be
modified before the comparison.
Example C-151 shows the string comparison that checks to see if the launched malware has the correct name.
Example C-151. Using strncmp for the module name comparison
0040150A mov ecx, [ebp+Str2] ❶ 00401510 push ecx ; Str2 00401511 lea edx, [ebp+Str1] ❷ 00401517 push edx ; Str1 00401518 call _strncmp
At ❶, we see Str2, which will contain the current name of the launched malware. At ❷, we see Str1. Looking back
through the code, it seems Str1 is our ocl.exe string, but it is passed to sub_4011E0 before
the comparison. Let’s load this malware into OllyDbg and set a breakpoint at the strncmp call at 0x401518.
When we set the breakpoint and click play, we get a division-by-zero exception caught by OllyDbg. You can press SHIFT-F9 to pass the exception to the program or change the options to pass all exceptions to the program.
After we pass the exception to the program, it is handled, and we arrive at the 0x401518
breakpoint. We see that qgr.exe is on the stack to be compared to
Lab16-03.exe, so we try to rename the malware to
qgr.exe. However, when we try to run it with the name
qgr.exe, the malware still doesn’t perform a DNS query or connect
out.
We need to review the sub_4011E0 function (where the
ocl.exe string was passed) before the strncmp function. Examining sub_4011E0, we see that it
calls QueryPerformanceCounter twice, as shown in Example C-152 (in bold).
Example C-152. Anti-debugging timing check using QueryPerformanceCounter
00401219 lea eax, [ebp+PerformanceCount] 0040121C push eax ; lpPerformanceCount 0040121D call ds:QueryPerformanceCounter... 0040126A lea ecx, [ebp+var_110] 00401270 push ecx ; lpPerformanceCount 00401271 call ds:QueryPerformanceCounter00401277 mov edx, [ebp+var_110] 0040127D sub edx, dword ptr [ebp+PerformanceCount] ❶ 00401280 mov [ebp+var_114], edx 00401286 cmp [ebp+var_114], 4B0h ❷ 00401290 jle short loc_40129C 00401292 mov [ebp+var_118], 2 ❸
The two calls to QueryPerformanceCounter surround code that
we will examine shortly, but for now we’ll look at the rest of the function. The malware
subtracts the first-time capture (lpPerformanceCount) from the
second-time capture (var_110) at ❶. Next, at ❷, the malware compares the
result of the time difference to 0x4B0 (1200 in decimal). If the time difference exceeds 1200,
var_118 is set to 2; otherwise, it will stay at 1 (its
initialized value).
Immediately following this check is the start of a for loop
at 0x40129C. The loop (not shown here) manipulates the string passed into the function (arg_0) using var_118; therefore, the
QueryPerformanceCounter check influences the string result. The
string used in strncmp is different in a debugger versus when run
normally. To get the correct string, we’ll make sure that var_118 is set to 1 when this loop is entered. To do this, we set a breakpoint at the
strncmp and NOP-out the instruction at ❸. Now we see that the filename must be
peo.exe in order for the malware to run properly outside a debugger.
Let’s examine the code surrounded by the two calls to QueryPerformanceCounter. Example C-153 shows
the code that starts with a call/pop combination to get the current EIP into the EAX register.
Example C-153. Malware setting its own exception handler and triggering an exception
00401223 call $+5 00401228 pop eax 00401229 xor ecx, ecx 0040122B mov edi, eax 0040122D xor ebx, ebx 0040122F add ebx, 2Ch ❶ 00401232 add eax, ebx 00401234 push eax ❸ 00401235 push large dword ptr fs:0 0040123C mov large fs:0, esp ❹ 00401243 div ecx 00401245 sub edi, 0D6Ah 0040124B mov ecx, 0Ch 00401250 jmp short loc_401262 00401252 repne stosb 00401254 mov ecx, [esp+0Ch] ❷ 00401258 add dword ptr [ecx+0B8h], 2 0040125F xor eax, eax 00401261 retn 00401262 pop large dword ptr fs:0 ❺ 00401269 pop eax
Once the malware gets the current EIP into EAX it adds 0x2C to it at ❶. This causes the EAX register to contain 0x2C + 0x401228 =
0x401254, which references the code starting at ❷. Next,
the malware modifies SEH to insert the 0x401254 address into the SEH call chain, as explained in
Chapter 15. This manipulation happens from ❸ through ❹. When the div ecx instruction executes, it causes a divide-by-zero exception to
occur because ECX is set to 0 earlier in the code, and this, in turn, causes the malware exception
handler to execute at ❷. The next two instructions
process the divide-by-zero exception before returning execution to just after the division by zero.
Execution will eventually lead to ❺, where the SEH chain
is restored by removing the malware’s exception handler.
The malware goes through all of this trouble to execute code that has a drastic time difference inside a debugger versus outside a debugger. As we explained in Chapter 8, exceptions are handled differently when running in a debugger and take a little bit longer to process. That small time delta is enough for the malware to determine if it is executing in a debugger.
Next, we set a breakpoint at gethostbyname at 0x4015DB in
order to see the domain name used by the malware, and we see that the malware terminates without
hitting the breakpoint. Examining the code in the main function,
we see two calls to GetTickCount, as shown in Example C-154 (in bold).
Example C-154. Anti-debugging timing check using GetTickCount
00401584 call ds:GetTickCount0040158A mov [ebp+var_2B4], eax 00401590 call sub_401000 ❶ 00401595 call ds:GetTickCount0040159B mov [ebp+var_2BC], eax 004015A1 mov ecx, [ebp+var_2BC] 004015A7 sub ecx, [ebp+var_2B4] 004015AD cmp ecx, 1 ❷ 004015B0 jbe short loc_4015B7 ❹ 004015B2 xor eax, eax 004015B4 mov [eax], edx ❸ 004015B6 retn
Between the two calls to GetTickCount, the call to
sub_401000 at ❶
contains the same SEH manipulation code we saw in the QueryPerformanceCounter method we analyzed previously. Next, at ❷, the malware compares the result of the time difference in
milliseconds. If the time difference exceeds one millisecond, the code executes the instruction at
❸, which is illegal because EAX is set to 0 in the
previous instruction. This causes the malware to crash. To fix this, we just need to make sure that
the jump at ❹ is taken.
Examining the decoding method sub_401300, we see that the
code in Lab 16-3 Solutions differs from the decoding method in Lab 9-2 Solutions. In Lab 16-3 Solutions, we find that the rdtsc instruction is used twice, and the familiar SEH manipulation code is
in between. The rdtsc instructions are shown in Example C-155 (in bold), and we have omitted the SEH
manipulation code from the listing.
Example C-155. Anti-debugging timing check using rdtsc
00401323rdtsc00401325 push eax ❶ ... 0040136Drdtsc0040136F sub eax, [esp+20h+var_20] ❷ 00401372 mov [ebp+var_4], eax 00401375 pop eax 00401376 pop eax 00401377 cmp [ebp+var_4], 7A120h ❸ 0040137E jbe short loc_401385 00401380 call sub_4010E0 ❹
The malware pushes the result of the rdtsc instruction onto
the stack at ❶, and later executes the rdtsc instruction again, this time subtracting the value it previously
pushed onto the stack from the result (EAX) at ❷. IDA
Pro has mislabeled the first result as a local variable, var_20.
To correct this, right-click var_20 and change the instruction to
appear as sub eax, [esp].
Next, the time difference is stored in var_4 and compared
to 0x7A120 (500000 in decimal) at ❸. If the time
difference exceeds 500000, sub_4010E0 is called at ❹. The sub_4010E0 function
attempts to delete the malware from disk, but fails since it is running inside the debugger.
Nevertheless, the malware will terminate because of the call to exit at the end of the function.
Lab 16-3 Solutions uses three different anti-debugging techniques to thwart
analysis of the malware inside a debugger: QueryPerformanceCounter, GetTickCount, and rdtsc. The easiest way to beat this malware at its own game is to NOP-out
the jumps or force them to be taken by changing them from conditional to nonconditional jumps. Once
we figure out how to rename the malware (to peo.exe) in a debugger, we can exit
the debugger, rename the file, and effectively use basic dynamic analysis techniques.