Skip to content
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
104 lines (77 sloc) 15 KB

Lab 16-1

This lab is the same as Lab 9-1, which is the same as the one in Lab 3-1.

Basic Static Analysis

  • strings: "", "Manager Service", ".exe", "%SYSTEMROOT%/system32", plus what looks like command strings and command line options.
  • PEiD: Microsoft Visual C++ 6.0.
  • Dependency Walker: ShellExecuteA, from SHELL32.DLL, service and registry-related imports from ADVAPI32.DLL, many from KERNEL32.DLL, including CreatePipe, many that relate to querying the process's environment. There are also raw socket imports from WS2_32.DLL, imported by ordinal.
  • PEview: Normal section names and headers.

Advanced Dynamic Analysis

Because other than anti-dissassembly this malware is the same as a previous, we know that the malware runs only if provided correct arguments as well as a password.

Since we know that the malware has anti-debug tactics in play, we want to see how it behaves when run in a debugger. Following through we see it call ShellExecuteA, executing cmd to delete itself. Scrolling up to the start of this function (at 0x401000) we can set a break point and re-run. Once we hit this bp we can look at the stack for the RA and jump there. As expected there is a JZ instruction just before the called. We can patch this jump and rerun to avoid this call. Despite avoiding this check, we still hit the sub_401000 bp (hereafter delete_self). We can follow this procedure as many times as needed, patching out all of the checks, but after a few it's clear that there are many. Let's see how they determine we are using a debugger to see if there's a better solution.

Right in _main, we see three immediate conditional calls to delete_self. The first checks fs:[30h] (PEB)'s second byte. This corresponds to the BeingDebugged flag. Next is a check for PEB[18][10], which corresponds to ForceFlags, and then a third checks PEB[68], which is compared to 0x70, with corresponds to NTGlobalFlag. Using IDA to find other XRefs to delete_self, we can see that each call is preceded by one of these three checks.

These three methods can be circumvented by using a plugin that changes them when the debugger starts the process, by patching them manually in memory, by patching out the calls to delete_self (though there or many), or simply NOP all but the prologue and epilogue of the function. I went for the latter, which allowed me to save it to the binary, not worrying about which debugger I may decide to use in the future.

With the patched function in place, I can easily analyze the malware and confirm that it is otherwise identical to Lab 9-1


  1. This malware uses three anti-debugging technics:
  2. Check BeingDebugged flag.
  3. Check ForceFlags flag.
  4. Check NTGlobalFlag flag.
  5. The malware attempts to delete its binary from the disk and then terminates.
  6. You can basically avoid having the delete_self function get called. This can be done many ways, the easiest two probably NOPing the function, or using a plugin that patches the PEB for you to trick the checks.
  7. You can simply open the memory that is being checked in the memory dump view and then overwrite the values with 0. You can click on the memory dump view and press <Ctrl-G> to enter an expression similar to what the checking code does (e.g. fs:[30h] + 2).
  8. 'StrongOD' and 'PhantomOm' both seem popular plugins for OllyDBG used to avoid anti-debug.

Lab 16-2

Basic Static Analysis

  • strings: "OLLYDBG", "Incorrect password, Try again.", "You entered the correct password!", "usage: %s <4 character password>".
  • PEiD: Microsoft Visual C++ 6.0.
  • Dependency Walker: FindWindowA from USER32.DLL (probably used with "OLLYDBG" we found with strings), many miscellaneous imports from KERNEL32.DLL.
  • PEview: Normal section names and headers, plus a .tls section.

Basic Dynamic Analysis

Running this malware on the command line apparently does nothing. Remembering the presence of FindWindowA, I closed OllyDBG (which was debugging the previous lab), and ran it again. This time it printed usage information if not provided an argument warns of an incorrect password otherwise. There is a small delay, likely in place to make brute forcing the password more difficult. It appears that this program checks for the existence of a debugger, not necessarily that the program itself is being debugged.

Advanced Dynamic/Static Analysis

Loading the malware into OllyDBG the process terminates before it has a change to start. That is probably has to do with the presence of the .tls section. Opening the program in IDA there doesn't appear to be any issue. We can see that _main is apparently in the .tls section for some reason. Pressing <Ctrl-E> to view TLS callbacks we see two. One, TlsCallback immediately seems suspect.

In TlsCallback is the call to FindWindowA, passed "OLLYDBG" as the window name target (as expected). If a window handle is returned, _exit is called immediately. In another branch, sub_401020 is called. The decision of which check to perform is based on the value of the second argument. Looking on MSDN, we see that the second argument to a TLS Callback is dwReason. Reason 1 is when a process starts (which matches the behaviour we have seen) and reason 2 is that a thread is created. To verify this, I want to test it in Olly. We can avoid having Olly terminate the process immediately by going into the settings, events, and changing where Olly first pauses from "Entry point of main module" to "System breakpoint".

Once we have a way to set a breakpoint on the TLS callback, it's trivial to avoid quitting. To confirm the behaviour, we set the breakpoint and run the program, avoiding the first check. While we do see this function called again, the reason this time is 2, and the second check is called.

This second check, sub_401020, calls SetLastError, OutputDebugString, and then GetLastError are called. These calls are used to check whether an error emitted will be received. Since a debugger will receive from OutputDebugStringA, the call will change the value returned from GetLastError. If the value is different, then a flag is set at 0x40A968.

Now we can trace through the main function and it appears very simple. We see a strncmp that compares the first four letters of our input with the first four letters located at 0x408030. In the binary this appears to be 'P}\xff\xff', but when the comparion function is called, we see its value is actually "bzqrp@ss". It appears that the password is dynamically generated. To see how this is done, we will set a watch point on the string. When this breakpoint is hit, we see it's in a function at 0x401090. This address is used as the target of a thread created just before the string comparison in main. Setting a breakpoint and running it a few times we see that its output appears to be deterministic, and we can even enter 'bzqr' as our password and it is accepted. Closing Olly and running it standalone, however, we see that it is no longer accepted. This must mean that the function behaves differently when being debugged.

Because the function doesn't take any arguments, we must look at any global state it uses. The string itself is obviously used, but it is not written to from anywhere else in the program. Near the top, though, a global at 0x40A968 is read from, and its value very slightly influence how the decoding works. This is the value that was set in our second debugger check, so we can avoid the issue by NOPing the point where the flag is set to 1. Testing again without the debugger, we again fail, which means we missed more shared state. Looking closer, we see that the PEB is read from at 0x40112B and then the value of BeingDebugged is read from it into BL. With this NOPed out (now three sets of patches) the generated password is "byrr", and running the program without a debugger running finally confirms that we have worked around all anti-debug tricks present.


  1. Nothing seems to happen when run from the command line is olly is running (even not attached). Otherwise it tells you to enter a 4 character password.
  2. When provided a password, the malwal tells you whether or not it is correct.
  3. "byrr".
  4. strncmp is called in _main at 0x40123A.
  5. When run in OllyDBG normally, the process terminates before reaching the first breakpoint.
  6. This binary is unique because it has a .tls section, which includes a TLS callback.
  7. The TLS callback is located at 0x401060.
  8. The TLS callback is used to run before the first OllyDBG breakpoint (when run with default settings). This callback uses FindWindowA to determine if a window with the name "OLLYDBG" is running. If so, it calls _exit.
  9. The first password we see when debugging (by setting a breakpoint on the string compare) is "bzqr".
  10. This password doesn't work on the command line when run without a debugger because the password generation function changes when a debugger is present.
  11. Two techniques are used in the password generation function, and both can be easily avoided by NOPing the instruction that sets the register to 1.
  12. The first is done in the TLS callback, but when a new thread is created. It checks whether the GetLastError return value is changed after calling OutputDebugString, which would be the case if there's a debugger there to receive the message.
  13. The second is done in the decoder itself, and simply mixes in the value of the flag BeingDebugged, which is read from the PEB.

Lab 16-3

Basic Static Analysis

  • strings: "cmd.exe", " >> NUL", "/c del".
  • PEiD: Microsoft Visual C++ 6.0.
  • Dependency Walker: WSASocketA, connect, htons, gethostbyname, WSAStartup from WS2_32.DLL (some by ordinal), ShellExecuteA from SHELL32.DLL, plus many including QueryPerformanceCounter from KERNEL32.DLL.
  • PEview: Normal section names and headers.

Basic Dynamic Analysis

Running with the normal syscall and network monitoring, not much is seen. Procmon sees an unusally low number of sys calls made. We expected at least a dns lookup, and the fact that it didn't happen even when run without a debugger suggests there are extra checks.

Advanced Dynamic/Static Analysis

Opening in IDA we see two strings built on the stack, "1qbz2wsx3edc" and "ocl.exe", and later a call to GetModuleFileNameA, which then has the filename pulled out using strrchr, and then compared using strncmp. Setting a breakpoint at this point we see that it is compared to "qgr.exe" (not "ocl.exe"). Renaming the malware does not make it run successfully, implying that the string modification must behave differently when a debugger is running. Watching the address of the string while we step through main, we see it is both passed to and modified in sub_4011E0.

Looking in sub_4011E0 we see a call to QueryPerformanceCounter, and then we see them modifying the SEH as we saw in labs in the previous chapter. What's interesting here is how they get the address of the handler and what the handler does. call $+5 is used to call the next address so that that address can be popped into EAX (0x401228). 0x2C is then added to it, and then that value (0x401254) is pushed onto the stack, followed by fs:[0], which serves as the prev pointer. ESP is then written into fs:[0] which sets 0x401254 as the first exception handler. This is immediately followed by a DIV ECX, which triggers the exception handler (since ECX was set to 0 previously). The exception handler patches the return address on the stack by incrementing it by 2, so that when the exception handlers finish, control resumes just past the problematic div. Next is a jump over the handler, followed by some code to restore the stack and the SEH back to normal. This is followed by a second call to QueryPerformanceCounter, whose result is compared to the previous, and if greater than 0x4B0, a local is set to 2. We see that this local is used in decoding the string, so we should be able to NOP is to get the real target filename back. Indeed, with the mod NOPed out instead of "qgr.exe", we see the string is not "peo.exe". Unfortunately, we still cannot get the malware to run with this filename.

Continuing down, now that we can pass the filename check, we see another exception happen. This one happens between two calls to GetTickCount, in sub_401000. This function's behaviour is almost identical to the exception code found between the QueryPerformacneCounter calls in sub_4011E0, and can pretty much be ignored.

Very similar to the last anti debug check, the difference between two calls to GetTickCount is compared to 1, and will try to dereference a null pointer if this check fails. This is easily patched by making the jump unconditional.

Just below this just we see a call to sub_401300, which looks like yet another anti-debug check. There is the same exception, which is meant to slow down the execution when in a debugger, but this time, the rdtsc instruction is used to get the count of cycles since reboot. It is used twice, and if the count is over 500'000, then sub_4010E0 is called, which attemps to delete the binary from disk. Otherwise, it decrypts a string and yields "".

We can finally see some network activity! gethostbyname is called on "", then a connection is made to that address on port 9999. Next sub_401060 is called and passed the socket. sub_401060 creates a StartupInfo struct on the stack, providing the socket for stdin, stderr, and stdout. This struct is passed to CreateProcessA, along with "cmd": This malware acts as a reverse shell. This can be confirmed by saving the patches to a new binary named peo.exe and running it with fakedns and nc running on port 9999. I am not sure why it would fail when run outside of a debugger, but it could be because the timing of everything executing in a VM doesn't pass the delicate timing checks.


  1. Only strings used to remove the malware itself when run incorrectly: "cmd.exe", " >> NUL", "/c del".
  2. Nothing happens when the binary is run with its default name.
  3. You can get the malware to run by renaming the binary to "peo.exe"
  4. This malware counts on the debugger slowing down execution to detect its presence. It uses three different methods to measure elapsed time: QueryPerformanceCounter, GetTickCount, and the rdtsc instruction. It also sets up a simple exception handler in each case presumably to cause the debugger to slow down execution even without breakpoints because the debugger must pass on the exceptions that it catches.
  5. Each check is very similar, but ends the program in different ways.
  6. If the QueryPerformanceCounter check detects a debugger, it will expect a different filename.
  7. If GitTickCount detects a debugger it tries to dereference 0, causing a crash.
  8. If the rdtsc check fails, the malware attempts to delete itself from disk.
  9. Because exception handling is much slower in a debugger than when run normally, the timing checks can detect the presence of the debugger even if there are not breakpoints set.
  10. The malware uses the domain "".
You can’t perform that action at this time.