Reverse engineering

Pafish (Paranoid Fish)

Dejan Lukan
July 23, 2014 by
Dejan Lukan

Introduction

In this tutorial we'll take a look at a Pafish tool, which performs anti debugger/vm/sandbox tricks to detect whether the malware is being executed in a debugger, in a virtual machine or in a sandbox.

Malware analysis today depends on a great deal of factors, but we're often using a virtual machine in order to analyze malware: this is because virtual machines are easy to reinstall and configure. There are a lot of options regarding networking, where we can use host-only, internal, NAT or bridged networking that enables virtual machines to have full, partial or none access to the network. One great feature of virtual machines is that they provide snapshots where we can create a snapshot, install malware and infect the whole system, and later revert the snapshot to restore the system to the old state (without malware infection). This happens in a matter of minutes, but imagine how long it would take to reinstall a system or restore from the last image when a physical host is involved. Malware is often also executed in a sandbox, which is how most of the online scanning solutions are analyzing malware. Additionally, we're also using different debuggers to analyse malware samples to figure out their internals.

All those techniques greatly speed up malware analysis, which is why authors are thinking of ways to detect when malware is being executed under such environment/conditions in order to make the analysis more difficult. When malware detects that it's being executed under a debugger/vm/sandbox, it can deviate from its original path and do something legitimate or terminate immediately. On the other hand, when malware doesn't detect it's being executed under debugger/vm/sandbox, it will run its malicious payload and infect the system.

Getting the Code

Pafish is being developed in wxDev-C++ as it says on its official Github website. First we have to download wxDev-C++ from [11] and click on the "wxDev-C++ Full Installer", so we will have the full environment installed and won't have to download additional parts of the installation package afterwards. The installation executable is 117MB large, so the download will take a while.

The installation of wxDev-C++ will look as presented on the picture below.

After the installation, we have to open the Pafish wxDev-C++ configuration file, which will also initialize the wxDev-C++ when run for the first time (if defaults are accepted). Once we've opened the project there will be different source files each used for different detection purposes:

  • Detect a debugger: debuggers.c
  • Detect a sandbox: gensandbox.c
  • Detect hooked functions: hooks.c
  • Detect Sandboxie: sandboxie.c
  • Detect Wine: wine.c
  • Detect VirtualBox: vbox.c
  • Detect VMWare: vmware.c
  • Detect Qemu: qemu.c

Each of the outlined source files contains checks used to detect the specific environment. In the next sections we'll take a look at each of the methods used to detect the environment.

Detecting a Debugger

First method: the IsDebuggerPresent function is a Win32 API function that can be used to determine whether the calling process is being debugged by a debugger.

The CheckRemoteDebuggerPresent function checks whether the process is debugged by a remote debugger, which doesn't necessarily reside on a separate computer, but is a separate process.

The OutputDebugString function sends a string to the debugger for display. If the application is not running under a debugger, the string will be sent to system's debugger to be displayed with the DbgPrint function. If the application is not running in a debugger and there is no system debugger, then the OutputDebugString does nothing. If the function doesn't return an error, the process is not being debugged, otherwise it's running under a debugger.

Detecting a Sandbox

Pafish contains four methods to detect the sandbox. The first method is using a quick mouse movement, where the GetCursorPos is used to retrieve the position of the mouse cursor. The GetCursorPos has the following syntax [1], where the lpPoint parameter accepts a pointer to a POINT structure where the screen's coordinates of the cursor will be saved. The function returns nonzero if it was completed successfully, otherwise it returns zero.

Let's now take a look at the actual detection function as used by Pafish to detect whether the program is running in a sandbox (seen on the picture below). The function first calls the GetCursorPos in order to receive cursor coordinates and saves them into the position1 variable. After that it sleeps for a while and then calls the GetCursorPos for the second time and saves the mouse coordinates into the position2 variable. Afterwards, the x and y coordinates of the mouse cursor are compared to each other, which determines whether the mouse cursor has changed during the last GetCursorPos function call. If the position of the mouse cursor has changed, then there was mouse activity during the sleep function, which means it's probably running in a simulated environment (most probably in a sandbox). A clear indication of the application running in a sandbox is that a human couldn't possibly move the mouse in such a short period of time.

The second method that's used by Pafish to detect a debugger is using a GetUserName function that retrieves the name of the user associated with the current thread [2]. The syntax of the function can be seen below, where it's evident that a function accepts two parameters. The first parameter lpBuffer is a pointer to the buffer where the user's logon name will be saved. The buffer must be large enough to enable the whole username to be saved to the buffer, otherwise the function will fail. The second parameter lpnSize specifies the size of the lpBuffer buffer, which is overwritten upon the function's completion – the lpnSize will contain the length of the username copied to the lpBuffer buffer. The function will return a nonzero value when successful, otherwise a zero is returned and a detailed error log can be obtained by calling GetLastError.

The code used in Pafish can be seen below, where we're first reserving a buffer on the stack to hold the username buffer of size 200 bytes and integer number i. After that we're calling the GetUserName, which will copy the logon username to the username buffer previously allocated. After that all the lowercase letters are changed to their upper-case letters and the name is compared to the following strings: SANDBOX, VIRUS and MALWARE. The strstr function returns a pointer to the first occurrence of the presented strings if found, otherwise it returns NULL. If the SANDBOX, VIRUS or MALWARE strings are found inside the username, the function will return 0, which specifies that the program is being run inside a sandbox. Otherwise 1 is returned, which signifies that the program is not being run in a sandbox: more specifically, that the logon username doesn't contain the strings SANDBOX, VIRUS or MALWARE.

The next method is using GetModuleFileName, which retrieves the fully qualified path for the file that contains the specified module [3]. The function syntax can be seen below. The function accepts three parameters, the hModule, the lpFilename and the nSize. The hModule contains a pointer to the requested module whose path we would like to obtain. If the parameter is NULL (as it is in our case), then the function will retrieve the path of the current executable instead. The lpFilename parameter is a pointer to a buffer, where the module/executable path will be stored; the buffer size must be larger than the size of the returned path if we want the function to succeed. The nSize parameter specifies the size of the lpFilename buffer, which must exceed the length of the returned path.

The actual code that checks whether the code is being executed in a debugger can be seen below. The code first reserves some space on the stack for buffer path and integer i. Then it calls the GetModuleFileName to get the path of the current executable – notice that the first parameter is NULL? The path of the executable is stored in the path buffer, which is then transformed into upper-case letters. Then the strstr function is used to check whether the retrieved path contains strings SAMPLE, VIRUS or SANDBOX and returns 0, which signifies the function is being executed inside a sandbox. Otherwise, 1 is returned, which signifies that the program is not being run in a sandbox: more specifically, that the path of the currently executing executable doesn't contain the strings SAMPLE, VIRUS or SANDBOX.

The fourth method of detecting whether we're in a sandbox is checking whether the first physical drive is larger than 50GB by using the CreateFile/DeviceIoControl functions. The syntax of the CreateFile can be seen below [4]. The CreateFile function creates or opens a file, file stream, directory, physical disk, volume, etc [4] and accepts seven parameters: lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes and hTemplateFile.

The lpFileName contains the name of the file or device to be created or opened, while the dwDesiredAccess contains a flag requesting certain access to the file/device: read (GENERIC_READ), write (GENERIC_WRITE), both (GENERIC_READ | GENERIC_WRITE) or zero. The dwShareMode specifies the requested sharing mode, which can be read (FILE_SHARE_READ), write (FILE_SHARE_WRITE), delete (FILE_SHARE_DELETE) or zero. The lpSecurityAttributes contains a pointer to the SECURITY_ATTRIBUTES structure, which determines whether the returned handle can be inherited by child processes – if the value is set to NULL, then the returned handle cannot be inherited by any child processes. The dwCreationDisposition specifies an action to perform on a file, which can be: create a file (CREATE_ALWAYS), create a new file if it doesn't exist (CREATE_NEW), open a file (OPEN_ALWAYS), open a file if exists (OPEN_EXISTING), or open a file if exists and truncate it to zero bytes (TRUNCATE_EXISTING). The dwFlagsAndAttributes specify the file attributes and flags, while the hTemplateFile has a valid handle to the template file (can be set to NULL). If the function call to CreateFile succeeds, the function returns an open handle to the specified file. Otherwise it returns INVALID_HANDLE_VALUE.

We should also describe the DeviceIoControl function, which sends a control code to the device driver instructing the device to perform a certain action [5]. The function accepts eight parameters: hDevice, dwIoControlCode, lpInBuffer, nInBufferSize, lpOutBuffer, nOutBufferSize, lpBytesReturned and lpOverlapped.

The hDevice contains a handle to the device on which the action will be executed, which can be a file, a directory, or a stream of a volume (the handle is obtained by previous call to the CreateFile function). The dwIoControlCode specifies the operation to be executed on the device. The lpInBuffer specifies the buffer, which contains the data required to perform the requested operation, but can be set to NULL if the dwIoControlCode doesn't require any input data (the nInBufferSize contains the size of the input buffer). The lpOutBuffer specifies the buffer where the data of the operation will be written to, while the nOutBufferSize specifies the number size of the buffer. If the function completes successfully, a nonzero return value will be returned.

The code that tries to determine whether the program is being executed in a sandbox can be seen below. The function first reserves some space on the stack for local variables. Then it calls the CreateFile to read the \.PhysicalDrive0 device only if it exists. If the device exists, the CreateFile will return the open handle to the opened device, otherwise the INVALID_HANDLE_VALUE will be returned. If the CreateFile returns INVALID_HANDLE_VALUE, the file will be closed and the function will return 1 to signify that the program is not being executed in a sandbox. If the device exists, the DeviceIoControl will be called to send the IOCTL_DISK_GET_LENGTH_INFO action to the device to get the length of the device. If the length of the device has been successfully obtained, the result should be a nonzero number. In such case, the actual size of the device is compared to 50GB. If the size of the device is smaller than 50GB, a value 0 is returned to signify the program is being run under a sandbox.

Detecting Hooked Functions

The hooks.c source code contains the following function, which detects whether the DeleteFileW has been hooked. The code first stores the address of the DeleteFileW function into the dwAddress variable. Then it checks whether the first two bytes of the function are 0xff25, which represent the assembly instruction for jump instruction. Usually the functions create a new stack frame upon being called, but the jmp instruction at the beginning of a function clearly indicates the function has been hooked. If the first instruction is jmp, we're storing the address to jump to into the dwAddress and checking whether the address starts with 0x8bff. In that is the case, the jmp actually jumps to a valid instruction in the kernel code; otherwise the address is invalid and we can conclude the function has been hooked and jmp will jump to a hooked procedure.

Detecting Sandboxie

Sandboxie is a program that enables us to run a program in a sandbox to prevent malware from making permanent changes to our machine [6]. Sandboxie can easily be downloaded from the official Sandboxie website and installed on our machine. After Sandboxie has been installed, it will start a window like the one below.

I won't go into the details about how Sandboxie works, but let's just look in the C:Program FilesSandboxie folder to check what it contains. I've specifically highlighted the SbieDll.dll library, which is used by Sandboxie.

Now, since we know Sandboxie uses the SbieDll.dll library, we can try to get a handle to that library by using the GetModuleHandle function. The GetModuleFunction retrieves the module handle for a specified module, which must have been previously loaded by the calling process [7]. The function accepts one parameter lpModuleName, which specifies the name of the loaded module: either .dll or .exe. If the function succeeds, it will return a handle to the specified module, otherwise NULL value will be returned.

The function below calls the GetModuleHandle by passing it the "sbiedll.dll" module name in order to obtain its handle. If the function returns a handle, the current program was started inside the Sandboxie environment.

Detecting Wine

The code to detect a Wine environment is first getting a handle to the kernel32.dll and then calling the GetProcAddress to retrieve the address of the wine_get_unix_file_name exported function/variable specified in the kernel32.dll. If the function succeeds, it will return the address of the exported function, otherwise it will return NULL.

Detecting VirtualBox

The code below uses the RegOpenKeyEx function to open the registry key seen on the picture below. If the function finds the specified registry entry, it will return ERROR_SUCCESS, otherwise a value of nonzero will be returned. Afterwards the type and data of "Identifier" registry key is retrieved: the value is stored in the lpData parameter. The retrieved data is then converted to uppercase letters and compared to the VBOX string. The value represents the SCSI disk name, which contains VBOX when the program is running under VirtualBox, otherwise it is not.

The method below is actually very similar to the method above, except that we're checking whether the SystemBiosVersion value contains the string VBOX, in which case the program is running under VirtualBox.

The function below is practically the same as the function above, which checks whether the VideoBisVersion contains a string VIRTUALBOX, in which case the program is running in a debugger.

The code below checks whether the "SOFTWAREOracleVirtualBox Guest Additions" exists in a registry, in which case the program is running under VirtualBox; otherwise it is not.

The code below checks whether the mouse driver for the VirtualBox exists: C:WINDOWSsystem32driversVBoxMouse.sys. If the file exists, the program is running in a VirtualBox.

Detecting VMWare

The function below is the same as the one used to detect VirtualBox, but it compares the SCSI hard drive identifier with a string VMWARE. If the string is found inside the identifier, the program is running under VMWare, otherwise it is not.

The function below checks whether the "Vmware Tools" registry key exists in a registry, which means the system is running under VMWare.

The function below checks wherer the mouse driver vmmouse.sys exists in the C:WINDOWSsystem32drivers directory, in which case the operating system is running under VMWare.

The function below checks whether the driver which enables the shared folders exists in the C:WINDOWSsystem32drivers directory, in which case the operating system is running inside VMWare.

Detecting Qemu

The function below checks whether the SCSI hard driver identifier contains the word QEMU, in which case the operating system is running inside Qemu.

Let's now check whether the specified registry key exists in the operating system running in Qemu. We've run regedit and opened the DEVICEMAP entry, where there is no such field as Scsi, which is being checked by the previous function. Since the Scsi entry doesn't exist, that Pafish function won't be able to detect that our Windows operating system is running inside Qemu. It should be noted that the Scsi identifier is available only if SCSI disk is enabled by using the "-drive file=file,if=scsi" option in qemu. Since we're not using the SCSI interface, that option is not available under the registry and the function above won't be able to detect Qemu.

The next function checks whether the SystemBiosVersion contains the string QEMU, in which case the operating system is running inside Qemu.

If we open registry editor and open the HKLMHARDWAREDescriptionSystemSystemBiosVersion key, we'll see the "ACRSYS – 1" value as presented below. Since the value doesn't contain a substring QEMU, the function above won't be able to detect Qemu.

Now it's finally the time to run pafish.exe to check whether any of the checks inside Pafish can actually detect whether the code is being executed in debugger/vm/sandbox. When pafish.exe is being run inside Windows 7 running under the Qemu virtual machine, none of the functions will be able to detect it (as we've already figured out). The output from running the pafish.exe can be seen on the picture below, where the most interesting part is the Qemu section – this is where the already identified checks are executed, but neither is able to detect that the Qemu virtual machine is used.

Conclusion

In this article we've presented different methods of checking whether the program is running under a debugger, virtual machine or a sandbox, where we've specifically taken a look at each function used to check that.

Malware authors today are using the same checks to check whether their malware malicious sample is being executed under a controller environment, in which case the malware will not perform any malicious checks to avoid detection for as long as possible. Malware authors are often using VirtualBox or VMWare virtual machines where the analysis of malware samples is taking place. We've seen a number of checks used to detect whether malware is being executed under VirtualBox/VMWare, which need to be bypassed in order perform a complete analysis of the malicious malware sample. Since bypassing all those checks takes a lot of time and knowledge, wouldn't it be better if we could simplify things? One of the options is using Qemu as virtualization technology, which is a very customizable program; I'm not saying that VirtualBox/VMWare can't be customized, but it's far easier to do so with Qemu (especially when wanting to change an option not usually supported by a graphical user interface used by VirtualBox/VMWare).

When analyzing a malicious sample we have to remember that any of the above checks can be employed by an attacker to determine whether the malware is being run in a controlled environment, in which case, the malware will most probably terminate its execution. If we suspect that some of the above checks are being used by the malware sample, we can install the appropriate operating system (usually Windows) into Qemu and try to analyze the malware there. That means only that we've bypassed the WMWare/VirtualBox/Qemu checks, but if we want to use a debugger, we must also bypass the debugger checks, but that is a story for another time.

This short introduction was meant for all malware analysts out there to improve their knowledge about analyzing malicious malware samples. Remember that an attacker can put arbitrary checks into the malware in order to check whether the code is being executed in other environments, like different online malware scanners: VirusTotal, Anubis, etc.

When a user receives an executable, he usually submits it to the online malware scanner, which is used to give the first insight into the program's execution. If the executable was able to detect whether it is being executed in such an environment, incomplete data could be given back to the user as a result of an online malware scan. The user could conclude that the executable is not malicious and execute it, unknowingly installing a backdoor on their own system. This is particularly useful for spreading malware among normal users, who are not malware analysis experts. We have to remember that such checks play an important role in malware analysis, and we have to check whether they are contained in an executable to confirm whether the executable is malicious or not. Remember that even if all automatic analysis tools are reporting that an executable is not malicious, that might not necessarily be the case, as we've seen in this article. An executable might still be malicious with proper checks in place to deter the analysis systems into flagging the executable as a valid non-malicious executable.

References:

[1] GetCursorPos function,

http://msdn.microsoft.com/en-us/library/windows/desktop/ms648390(v=vs.85).aspx.

[2] GetUserName function,

http://msdn.microsoft.com/en-us/library/windows/desktop/ms724432(v=vs.85).aspx.

[3] GetModuleFileName function,

http://msdn.microsoft.com/en-us/library/windows/desktop/ms683197(v=vs.85).aspx.

[4] CreateFile function,

http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx.

[5] DeviceIoControl function,

http://msdn.microsoft.com/en-us/library/windows/desktop/aa363216(v=vs.85).aspx.

[6] Protection from rogue software, spyware and malware is just a click away...,

http://www.sandboxie.com/.

[7] GetModuleHandle function,

http://msdn.microsoft.com/en-us/library/windows/desktop/ms683199(v=vs.85).aspx.

[8] GetProcAddress function,

http://msdn.microsoft.com/en-us/library/windows/desktop/ms683212(v=vs.85).aspx.

[9] RegOpenKeyEx function,

http://msdn.microsoft.com/en-us/library/windows/desktop/ms724897(v=vs.85).aspx.

[10] Github Pafish,

https://github.com/a0rtega/pafish.

[11] wxDev-C++ Downloads,

http://wxdsgn.sourceforge.net/?q=node/4.

Dejan Lukan
Dejan Lukan

Dejan Lukan is a security researcher for InfoSec Institute and penetration tester from Slovenia. He is very interested in finding new bugs in real world software products with source code analysis, fuzzing and reverse engineering. He also has a great passion for developing his own simple scripts for security related problems and learning about new hacking techniques. He knows a great deal about programming languages, as he can write in couple of dozen of them. His passion is also Antivirus bypassing techniques, malware research and operating systems, mainly Linux, Windows and BSD. He also has his own blog available here: http://www.proteansec.com/.