Dynamic Analysis - Malware Analyst’s Cookbook and DVD: Tools and Techniques for Fighting Malicious Code (2011)

Malware Analyst’s Cookbook and DVD: Tools and Techniques for Fighting Malicious Code (2011)

Chapter 8. Dynamic Analysis

Dynamic analysis is the process of executing malware in a monitored environment to observe its behaviors. This technique can quickly yield information such as created files, created registry keys, contacted websites, and so on. If you’re not an experienced IDA Pro user or simply don’t have time to perform a thorough static analysis of the code, you can use dynamic analysis to get a quick initial perspective of the malware’s capabilities.

The purpose of this chapter is not to provide a comprehensive list of actions that you should perform during a dynamic analysis. For example, capturing network traffic, which is discussed in Chapters 7 and 8, is not discussed again here. The purpose is to show you how dynamic analysis tools work, so you can understand their strengths, weaknesses, and, ultimately, how you can choose the right tool for the job. Additionally, we will provide you with a number of new tools and techniques for capturing a malware sample’s behaviors or interacting with it as it executes.

Before you begin reading and following along with the material in this chapter, make sure you set up a safe, isolated lab environment such as the ones described in Chapter 7.

Detecting the changes that malware makes to a system is a key aspect of dynamic analysis. However, the number of files and registry keys that are modified while a system is idle, or as a result of running your monitoring tools, can be excessive and overwhelming. To get the most out of your efforts, you’ll need to become familiar with “normal” changes so that you can distinguish them from artifacts left by the malware. A good way to do this is by determining the changes that occur when you execute non-malicious code, such as notepad.exe, calc.exe, or Internet Explorer.

Here is a brief introduction to the different methods of change detection:

· Hook-based tools: These tools hook API functions in user mode or kernel mode to show changes being made on a system. Examples of these tools include Process Monitor (Recipe 9-1) and pymon.py (Recipe 11-12).

· Difference-based tools: These tools, also known as install monitors, take a snapshot of the file system and registry before and after a program executes, then compare the two snapshots to show what changed. Examples of these tools include Regshot, InCtrl5, and Winanalysis (Recipe 9-2).

· Notification-based tools: These tools register notification routines that the system automatically calls when certain events occur, such as directory creation, file deletion, and so on. Examples of these tools include Process Monitor (it uses this technique in conjunction with hooks) and Preservation (Recipe 9-10).

Table 9-1 shows a comparison of the features.

Table 9-1: Comparison of Change Detection Tools

Table 9-1

Table 9-1 Continued

The recipes in this section show examples of using change detection tools from each of the categories represented in Table 9-1. Before we begin, you must be aware of the fact that all methods share a common weakness—they can be bypassed (or disabled) by rootkits that are installed during execution of the malware that you’re analyzing. Rootkit detection is discussed later in Chapter 10 rather than this chapter. However, you can still leverage rootkit-scanning tools as part of your dynamic analysis procedure.

Recipe 9-1: Logging API calls with Process Monitor

Process Monitor1 is a combination of the well-known Filemon and Regmon tools from Sysinternals. You can use this tool to log verbose information on activity related to the file system, registry, network, processes, and threads. Process Monitor is a hybrid between a hook-based tool and a notification-based tool. It loads a kernel driver that hooks functions such as ZwDeleteKeyand ZwSetValueKey for monitoring the registry. However, it uses Event Tracing for Windows (ETW) to capture network activity, which isn’t based on hooks. It also uses notification routines to monitor process and thread activity (see Recipe 9-10 for more information).

The following list shows the default data columns displayed by Process Monitor:

· Time of day: The time that the logged behavior occurred. You can also change this column to show a delta (amount of time since the previous behavior).

· Process: Name of the process that produced the behavior being logged.

· PID: Process ID of the process.

· Operation: The API function called (or in some cases, just a short description of the activity, such as Process Create).

· Path: The path of the object (file or registry key) on which an action is being performed.

· Result: The success or failure status of an operation.

· Details: Operation-specific details. For example, this column contains the desired access level (read or write) for file open operations.

Figure 9-1 shows how to create a filter so that Process Monitor records only changes made by processes named cmd.exe. You can set filters based on other criteria as well, such as process ID or the operation being performed.

After applying the filter, click the magnifying glass icon to start the capture. Then, execute the malware that you want to analyze. If you’re looking for indications of particular behaviors, you can conduct a search with Process Monitor’s GUI. Alternately, you can export the results to a text file and use findstr (Windows) or grep (Unix).

Logging Boot Time Activity

Malware samples survive reboots in various ways to remain persistent on an infected machine. Malware that starts automatically when the system boots is problematic from an analysis point of view, because the malware can complete its malicious actions before you start your monitoring tools. However, if you click Options⇒ Enable Boot Logging, then Process Monitor will begin capturing APIs the next time you reboot the system. This is significant, because it logs activity starting with the creation of smss.exe—the first user mode process. Thus, you can record what happens on a system even before processes like csrss.exe, winlogon.exe, and explorer.exe start. Figure 9-2 shows an example of the boot time logging.

Figure 9-1: Filtering API calls based on process name

f0901.eps

Figure 9-2: Logging the boot sequence

f0902.eps

For another example of using Process Monitor, see Recipe 13-4. That recipe also provides a video (which you can find on the DVD) showing how to set up Process Monitor filters and how to isolate and highlight specific activity.

1 http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx

Recipe 9-2: Change Detection with Regshot

Regshot2 is a difference-based change detection tool that focuses on the file system and registry. Similar alternatives to Regshot include InCtrl53 and Winalysis.4 Regshot has a few benefits over its competition in that it is open source, tends to be much faster, and is a standalone executable (i.e., it does not require any installation). Here is a description of the technique used by Regshot:

· When you initiate the first (i.e., baseline) snapshot with Regshot, it uses RegEnumValue and RegEnumKeyEx to build an in-memory list of existing registry keys and values.

· Regarding the file system, it recursively searches from any number of top-level directories and builds an in-memory list of files using FindFirstFile and FindNextFile. For each file, it records the size in bytes, the file’s attributes (hidden, system, archived, and so on), and the file’s last write time.

· Upon taking the second snapshot and performing a comparison, Regshot alerts on any created, modified, or deleted registry keys, values, or files.

Using Regshot

To use Regshot, enter the top-level directories (separated by a semicolon) that you want to monitor. For the most comprehensive results, you must include the root drive (C:\). To detect malware attempting to spread via autorun, you can connect a USB drive or secondary hard disk to your analysis machine and monitor that as well by entering something like C:\;F:\;G:\. Registry changes are monitored automatically, so there is no configuration required for that component.

To create a baseline, click the first shot button and wait for Regshot to finish enumerating all of the required information. Then you can execute the malware, wait a desired amount of time, and click the second shot button, as shown in Figure 9-3.

Figure 9-3: Taking a snapshot of the file system and registry with Regshot

f0903.tif

After the second snapshot completes, you can click the compare button to see the results. Figure 9-4 shows an example of the changes recorded by Regshot:

Figure 9-4: An example of Regshot results

f0904.tif

As you can see, each section of the Regshot report contains useful information about the malware’s behavior. You can make the following conclusions:

· Registry changes: The malware changes the NoFolderOptions setting in the registry, which prevents users from being able to control how Windows Explorer displays folders. In particular, users cannot configure Explorer to show files with the hidden attribute set. It also changes the DisableRegistryTools setting, which prevents users from starting the default registry editor(s) that Windows provides (so that users cannot remove registry entries added by the malware).

· Files added: The malware adds a file named csrssc.exe to the user’s temporary directory. Two new files exist in the Prefetch directory. However, these are indirect artifacts of the malware. In other words, the Windows OS created the Prefetch files, not the malware. The Prefetch files are good sources of forensic evidence. They tell you that files named 944983008.exe and csrssc.exe executed on the system during the malware’s execution. Without the Prefetch file, you can only tell that csrssc.exe was created, not that it actually ran.

· Files deleted: The malware deleted a file named 944983008.exe from the user’s desktop. This file is the original malware sample. Thus, you can conclude that the malware deletes itself after executing.

· Files (or file attributes) modified: The malware does not directly modify any files. The files that you see in Figure 9-4 are all indirectly changed. For example, the Internet Explorer history files were probably changed because one of the malicious processes (944983008.exe or csrssc.exe) used the WinINet API. Thus, the WinINet API functions automatically updated the index.dat (IE history files) with the sites accessed.

2 http://sourceforge.net/projects/regshot/

3 http://www.pcmag.com/article2/0,2817,9882,00.asp

4 The tool’s original homepage (www.winalysis.com) is offline, but you can find it on Google.

Recipe 9-3: Receiving File System Change Notifications

dvd1.eps

You can find supporting material for this recipe on the companion DVD.

Notification-based tools can detect changes to the file system by registering callback functions. The callback function is a programmer-defined action that Windows executes when any process makes changes to files in a directory being monitored. The tool that we present in this recipe (found on the book’s DVD and called RegFsNotify.exe) monitors all top-level directories of fixed drives (local hard disks) and removable drives (USB) for new files, deleted files, changes in file size, and changes to file attributes. In its callback function, RegFsNotify.exe reports the behaviors that occurred.

File System Change Notifications

Registering change notifications requires the following Windows API functions:

· FindFirstChangeNotification

· FindNextChangeNotification

· ReadDirectoryChangesW

The first argument to FindFirstChangeNotification is the name of a directory to monitor. The second argument specifies if you want to monitor for changes in subdirectories (i.e., recursively). The third argument is a value representing the types of notifications that you want to receive. If the function succeeds, it returns a handle. Here is the API prototype for the function:

HANDLE WINAPI FindFirstChangeNotification(

__in LPCTSTR lpPathName, // path of a directory to monitor

__in BOOL bWatchSubtree, // true to monitor recursively

__in DWORD dwNotifyFilter // one or more values from Table 9-2

);

Table 9-2 shows the possible values for the dwNotifyFilter parameter.

Table 9-2: Possible Values for the dwNotifyFilter Argument

Value

Description

FILE_NOTIFY_CHANGE_FILE_NAME

Triggers when files are renamed, created, or deleted

FILE_NOTIFY_CHANGE_DIR_NAME

Triggers when directories are created or deleted

FILE_NOTIFY_CHANGE_ATTRIBUTES

Triggers on any attribute change to files in the watched directory

FILE_NOTIFY_CHANGE_LAST_WRITE

Triggers when the last write time of any file in the watched directory is updated

FILE_NOTIFY_CHANGE_LAST_ACCESS

Triggers when the last access time of any file in the watched directory is updated

FILE_NOTIFY_CHANGE_CREATION

Triggers when the creation time of any file in the watched directory is updated

FILE_NOTIFY_CHANGE_SECURITY

Triggers when the security descriptor of any file in the watched directory is updated

FILE_NOTIFY_CHANGE_SIZE

Triggers when any file in the watched directory changes size

If you want to register notifications for multiple directories using different filters, you can do that, too. For example, you may want to detect created files in C:\WINDOWS\system32, but only detect changes to existing files in C:\Users. To do this, you call FindFirstChangeNotification twice and then pass an array of the returned handles to WaitForMultipleObjects. This puts your program to sleep until a process triggers one of the notifications. When the waiting function returns, your program can use ReadDirectoryChangesW to gather details on the change. Here is the prototype for this API function and the structure of data that it returns.

BOOL WINAPI ReadDirectoryChangesW(

__in HANDLE hDirectory, // open handle to watched directory

__out LPVOID lpBuffer, // output buffer

__in DWORD nBufferLength, // length of lpBuffer

__in BOOL bWatchSubtree, // true to monitor recursively

__in DWORD dwNotifyFilter,// one or more values from Table 9-2

__out_opt LPDWORD lpBytesReturned, // # bytes written to lpBuffer

__inout_opt LPOVERLAPPED lpOverlapped, // required for overlapped mode

__in_opt LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine

);

typedef struct _FILE_NOTIFY_INFORMATION {

DWORD NextEntryOffset; // offset to next structure

DWORD Action; // action (modified, deleted, created, etc)

DWORD FileNameLength; // number of bytes in FileName array

WCHAR FileName[1]; // variable sized buffer for the file/directory name

} FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION;

The hDirectory parameter is a handle to the directory you’re monitoring. The lpBuffer parameter is a buffer in which the output is placed. The output is an array of FILE_NOTIFY_INFORMATION structures—one for each change that occurred. To report on the changes, you just need to cycle through the array of structures and print the Action and FileName fields. You can find the full source code for RegFsNotify.exe on the book’s DVD.

Using RegFsNotify

To use RegFsNotify.exe, just call it from command line—no arguments are needed. It has only been tested on Windows XP and Windows 7, but may work on other versions of Windows as well. When you want to stop the monitor, type Ctrl+C into the command prompt. All logs are saved to a file named RegFsNotify.txt in your current working directory. Figure 9-5 shows example output from RegFsNotify.exe. You can also find a video of using the tool on the book’s DVD.

Figure 9-5: Analyzing malware behaviors with RegFsNotify

f0905.tif

Each line of the RegFsNotify.exe output begins with [ADDED], [REMOVED] or [MODIFIED] to indicate the type of activity that occurred. Based on the data shown in Figure 9-5, you can make the following conclusions:

· Registry changes: The malware makes several changes to the Image File Execution Options registry key (monitoring the registry with change notification is discussed in Recipe 9-4). Any time you see malware adding new values to this key, it is likely an attempt to prevent antivirus products from running on the system. For more information, see the McAfee blog.5

· Added files: During execution of the malware, the following files were created:

· A new prefetch file (C:\Windows\Prefetch\RUNDLL32.EXE): The most likely explanation is that the malware dropped or downloaded a DLL and then used rundll32.exe to execute the DLL (see Recipe 13-2).

· An autorun file (C:\AUTORUN.INF): This indicates an attempt to spread to other computers.

· Removed files: The malware deleted a file named tete23418937t.dll. Based on the suspicious name, the file was probably created by the malware shortly before it was deleted (i.e., it didn’t exist on the system before running the malware). This is an example of a temporary file, as discussed in Table 9-1, and it would likely not be detected by difference-based tools such as Regshot.

Note An interesting note about the RegFsNotify.exe output is that two files (rav32.exe and safe..) were reportedly added, but look at the full path—they were added to the recycle bin. This behavior could have two explanations. One possibility is that the files were deleted and moved to the recycle bin. However, files deleted on command line or by direct calls to DeleteFile will bypass the recycle bin. A user certainly didn’t delete the files from Explorer, because all of this happened on a virtual machine that wasn’t being used at the time. Therefore, there is only one explanation left—the malware intentionally adds files to the recycle bin in an attempt to hide. Most users don’t empty or look inside their recycle bins very often, so it is a reasonable place to drop files (as opposed to, say, the user’s desktop where the malware would certainly be spotted).

RegFsNotify Limitations

In addition to the limitations described in Table 9-1, the API functions required for producing notifications can sometimes “miss” changes. For example, if you delete a directory that contains 20 files, you might only receive notification about the directory and 12 of its files. This is a documented weakness and occurs when many changes are made at once. Also, you cannot register notifications for remote or shared network drives.

5 http://www.avertlabs.com/research/blog/index.php/2008/12/09/image-file-execution-options/

Recipe 9-4: Receiving Registry Change Notifications

dvd1.eps

You can find the supporting material for this recipe on the companion DVD.

Registry change notification works a bit differently than the file system change notification. You can receive notification when a change is made to a registry key or any of its subkeys, but it’s up to you to figure out which key changed. In other words, there is no ReadDirectoryChangesW equivalent for the registry. You can cope with this issue by building an in-memory list ahead of time (similar to Regshot) and then seeing what was added, modified, or deleted; or you can recursively parse the registry and check the last-written timestamps when you receive a notification.

Note Malware can change a file’s timestamps by calling SetFileTime or it can prevent the NTFS file system from updating last access times by altering the NtfsDisableLastAccessUpdate registry key. However, as far as we know, there’s no stable method of altering timestamps on registry keys or preventing them from being recorded. See Recipe 10-2 for an example of detecting file timestamp-altering malware.

Registry Change Notifications

Here is the API prototype for RegNotifyChangeKeyValue:

LONG WINAPI RegNotifyChangeKeyValue(

__in HKEY hKey, // handle to top-level registry key

__in BOOL bWatchSubtree, // watch subtree (recursive)

__in DWORD dwNotifyFilter, // one or more values from Table 9-3

__in_opt HANDLE hEvent, // event to signal upon change

__in BOOL fAsynchronous // true for asynchronous mode

);

The dwNotifyFilter can be one or more of the values shown in Table 9-3.

Table 9-3: dwNotifyFilter Values

Value

Description

REG_NOTIFY_CHANGE_NAME

Triggered when a subkey is added or deleted

REG_NOTIFY_CHANGE_ATTRIBUTES

Triggered when the attributes of a key are changed

REG_NOTIFY_CHANGE_SECURITY

Triggered when a key’s security descriptor changes

REG_NOTIFY_CHANGE_LAST_SET

Triggered when values in a key are added, deleted, or modified

The authors have built the registry notification code into RegFsNotify.exe, which was introduced in Recipe 9-3. By default, it monitors for changes to any key under HKLM\Software or HKCU\Software. You can add as many top-level keys as you want. Some antivirus products rely on this type of change notification so they can immediately restore their registry settings if malware tries to delete them. Likewise, many malware families use the same technique to restore their own registry settings if antivirus products delete them. Now, you can add the technique to your tools as well.

Recipe 9-5: Handle Table Diffing

dvd1.eps

You can find the supporting material for this recipe on the companion DVD.

The tools discussed thus far in the chapter are based on detecting changes to persistent, non-volatile data such as files and registry keys. Unless the files and registry keys are deleted, they will exist after a reboot. However, other types of data are more volatile in nature, such as desktop, mutex, and event objects. If you don’t monitor changes to these types of objects, you can miss some critical aspects of a malware sample’s behavior. This recipe introduces the concept of handle table diffing and describes how we built the tool called HandleDiff.exe, which you can find on the book’s DVD.

Windows Objects

Windows is an object-oriented OS, which means that through the kernel’s eyes, everything is an object. Before an application can perform an operation on an object (such as reading from or writing to a file), it must first open a handle to the file object. Figure 9-6 shows how you can use the SysInternals tool named WinObj6 to view the different types of objects that exist on a system.

Figure 9-6: Using WinObj to view object types

f0906.tif

When analyzing malware, you can learn a lot about its behavior based on which objects of each object type it accesses. For example, the fact that it opens a handle to a file doesn’t tell you much. You want to know the name of the file and the access granted (read-only, write access, and so on). One of the tools you can use to capture handle information is handle.exe from Sysinternals. Using the –p and –a flags, you can print all handles for a particular process, as shown in Figure 9-7.

Figure 9-7: Open handles for process with PID 1200

f0907.tif

Notice that the name field for some objects is blank. This is normal for objects such as threads and timers that simply don’t have associated names. Other objects, such as mutexes, events, and semaphores can be named or unnamed, depending on whether the process that created them wants to allow other processes on the system to access the objects. Another tool you can use to inspect a process’s open handles is Process Hacker.7 As shown in Figure 9-8, Process Hacker’s handles tab hides unnamed handles by default, but you can change that by deselecting the box.

Figure 9-8: Viewing open handles with Process Hacker

f0908.eps

One weakness of using these tools is that they only show currently open handles for a process. If you’re analyzing malware dynamically and it closes its handle to an object before you view its open handles, then you will miss certain activity. Another problem is the sheer volume of open handles that each process on the system has open at any time. If other processes on the system close or open handles to objects as a result of something that the malware does, how do you determine exactly what changed?

Note Just how many handles can a given process have open concurrently? As Mark Russinovich explains in his blog titled Pushing the Limits of Windows: Handles (http://blogs.technet.com/b/markrussinovich/archive/2009/09/29/3283844.aspx), the number is just over 16 million. In the blog, Mark also describes a method of determining changes to a process’s handle table using the !htrace extension for WinDbg (see Chapter 14).

The indirect changes, or side effects of malware activity, are critical artifacts that you want to record during an analysis. Every program, malicious or not, is responsible for several unintentional and uncontrollable changes to the system on which it runs. For example, csrss.exe is involved in the creation of user mode processes. It has an open handle to every new process that starts, and the handle remains open for as long as the process is running. The process can try to hide many ways, but you can detect it by inspecting csrss.exe’s open handles (this is known as an alternate process listing). The process can try to manipulate csrss.exe’s handle table (see Recipe 8-7 for an example), but that requires opening a handle to csrss.exe. Thus, in order to hide one artifact, the malware must create another artifact.

Developing a Handle-Diffing Program

To address the problem, we created a program called HandleDiff.exe. It works by comparing the handles that are open in each process before and after running a malware sample. In other words, it’s a difference-based change detection tool, but focused on newly opened and closed handles. The following list gives a slightly more technical description of how HandleDiff.exe works. The full source code for the program is also available on the DVD that accompanies this book.

· Enumerates processes on the system using the CreateToolhelp32Snapshot API with the TH32CS_SNAPPROCESS flag.

· Uses NtQuerySystemInformation with the SystemHandleInformation class for each process. The output of this function is a SYSTEM_HANDLE_INFORMATION structure, which contains an array of SYSTEM_HANDLETABLE_ENTRY_INFO structures (one for each open handle on the system). The UniqueProcessid field identifies the PID of the owning process.

typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO

{

USHORT UniqueProcessId;

USHORT CreatorBackTraceIndex;

UCHAR ObjectTypeIndex;

UCHAR HandleAttributes;

USHORT HandleValue;

PVOID Object;

ULONG GrantedAccess;

} SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO;

typedef struct _SYSTEM_HANDLE_INFORMATION

{

ULONG NumberOfHandles;

SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];

} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

· Opens each process using OpenProcess and requests PROCESS_DUP_HANDLE permissions. HandleDiff.exe creates a duplicate copy of the process’s open handles using the DuplicateHandle API call.

· Passes each duplicated handle to NtQueryObject with the ObjectTypeInformation and ObjectNameInformation flags. The output of this API is the type of the handle (i.e., Process, Thread, File, and so on) and the name of the object that the handle describes.

· Records all of the gathered handle information into a C++ vector (dynamically sizeable array) and performs all of the steps again during the second snapshot, thus creating two vectors of handles.

· Compares which handles exist in one vector but not the other. This determines exactly what changed.

Note One of the documented disadvantages to using the NtQueryObject API is that a program will hang when querying the names of Pipe objects that have been opened for synchronous access and that have pending read or write operations. To prevent hanging, HandleDiff.exe looks up names for Pipe objects in a separate thread, which it can then terminate if the thread doesn’t complete quickly.

Using HandleDiff.exe

The following syntax shows how you can use the HandleDiff.exe program:

C:\> HandleDiff.exe -h

------------------------------

HandleDiff v0.2

------------------------------

Usage: HandleDiff.exe [OPTIONS]

OPTIONS:

-h show this message and exit

-d diffing mode

-s <SECS> take 2nd snapshot after SECS seconds

-f <FILE> save results to file

-q quiet, only show handles with names

To enumerate all handles on the system and print to STDOUT:

C:\> HandleDiff.exe

To only enumerate handles with names (quiet mode):

C:\> HandleDiff.exe -q

To only enumerate handles with names, but save to a file:

C:\> HandleDiff.exe -q -f log.txt

To use diffing mode with manual timer (you press a key when you’re ready for the second snapshot):

C:\> HandleDiff.exe -d

To use diffing mode with automatic timer (60 seconds) and save output to a file (good for use in automated sandboxes):

C:\> HandleDiff.exe -d -s 60 -f log.txt

The next few recipes show practical demonstrations of using HandleDiff.exe to investigate malware such as Zeus and Bankpatch.C. You can also find a video on the book’s DVD that walks you through the steps for using HandleDiff.exe and how to interpret its output.

6 http://technet.microsoft.com/en-us/sysinternals/bb896657.aspx

7 http://processhacker.sourceforge.net/

Recipe 9-6: Exploring Code Injection with HandleDiff

dvd1.eps

You can find the supporting material for this recipe on the companion DVD.

Zeus (also known as Zbot, PRG, ntos, and wsnpoem) is a trojan that relies heavily on code injection. The code that Zeus injects into a target process requires access to DLLs (for dependencies), files, registry keys, mutexes, and so on. As a result, the target process will open handles to those resources. This recipe shows how to use HandleDiff.exe to explore the artifacts created by Zeus when it infects a system.

Using HandleDiff with Zeus

To determine exactly which handles a target process opens as a result of Zeus’s injected code, you can set up HandleDiff.exe with an automated timer. Before the timer expires, you can infect the system with Zeus. Here is a snippet of the results:

C:\> HandleDiff.exe –d –s 60 –f zeus.txt

-------------------------------

winlogon.exe (pid 684)

OldHandles: 516

NewHandles: 530

[+] 0x148 File \WINDOWS\system32\lowsec\local.ds

[+] 0x14c File \WINDOWS\system32\lowsec\user.ds

[+] 0x1bc Key \REGISTRY\USER\.DEFAULT\Software\Microsoft\

Windows\CurrentVersion\Internet Settings

[+] 0x5e8 File \WINDOWS\system32\sdra64.exe

[+] 0x7a0 File \lsass

[+] 0x7e4 Mutant \BaseNamedObjects\_AVIRA_2109

[+] 0x878 Semaphore \BaseNamedObjects\shell.{210A4BA0-\

3AEA-1069-A2D9-08002B30309D}

[+] DLL C:\WINDOWS\system32\wininet.dll

[+] DLL C:\WINDOWS\system32\wsock32.dll

-------------------------------

spoolsv.exe (pid 1704)

OldHandles: 135

NewHandles: 139

[+] 0xc4 Key \REGISTRY\USER\.DEFAULT\Software\Microsoft\

Windows\CurrentVersion\Internet Settings

[+] 0x298 Mutant \BaseNamedObjects\13CE123C01CAE16D000006A82

[+] DLL C:\WINDOWS\system32\psapi.dll

[+] DLL C:\WINDOWS\system32\wininet.dll

[+] DLL C:\WINDOWS\system32\wsock32.dll

For each process, the output shows the process ID, process name, and number of handles in the baseline snapshot and comparison snapshots. You’ll also see a line displaying a + (plus) sign for newly created handles or a – (minus) sign for recently closed handles, along with the handle value, object type, and object name.

As you can see, winlogon.exe started with 516 open handles before running Zeus and ended up with 530. Without further inspection, you can’t say for sure that Zeus directly caused the extra 14, but if you take a look at the object names, you can make a better assessment:

· The open file handles to local.ds and user.ds are directly caused by Zeus—those are the files in which the trojan stores its configuration and stolen data.

· The open registry handle to the Internet Settings key is an artifact produced by wininet.dll loading, which is a networking DLL that Zeus uses to contact its command and control sites, along with wsock32.dll, the Winsock library.

· The _AVIRA_2109 mutex is created by Zeus to mark its presence on the system.

· The open file handle to sdra64.exe is the Zeus executable on disk, which the infected winlogon.exe process locks so that other processes cannot delete it.

The video on the book’s DVD for this recipe shows several other artifacts left by Zeus.

Recipe 9-7: Watching Bankpatch.C Disable Windows File Protection

dvd1.eps

You can find the supporting material for this recipe on the companion DVD.

Detecting newly created handles is only one possibility with HandleDiff.exe. You can also detect recently closed handles in any process. Why would you ever be interested in knowing which handles were closed? Consider the following example based on a trojan called Bankpatch.C.8 This malware acts as a file infector and introduces malicious code into DLLs such as kernel32.dll and wininet.dll. However, on systems with Windows File Protection (WFP), the DLLs are “protected” against changes. Bankpatch.C disables Windows File Protection (WFP) in the exact manner described in 2004 by Daniel Pistelli.9 To summarize the method:

· Enumerates handles with NtQuerySystemInformation and the SystemHandleInformation class.

· Gets the object name for each of winlogon.exe’s open handles using NtQueryObject and the ObjectNameInformation class.

· Converts the object name to uppercase and then checks if it contains WINDOWS\SYSTEM32 or WINNT\SYSTEM32. If so, the code duplicates a handle to the object with DUPLICATE_CLOSE_SOURCE rights. These are the handles that winlogon.exe needs to have open in order to monitor the directories for changes (using the same file system change notification technique described in Recipe 9-3).

· Uses CloseHandle on the duplicated handle, which essentially closes winlogon.exe’s copy of the handle. Once winlogon.exe’s handle to the system32 directory is closed, it can no longer receive notifications about changes to protected files in the system32 directory. If winlogon.exe can’t find out a file was modified, it cannot initiate a fix. Therefore, Bankpatch.C’s file infection becomes permanent.

Figure 9-9 shows a de-compilation of Bankpatch.C’s WFP-disabling code, as produced by IDA Pro and Hex-Rays. If you reviewed Daniel Pistelli’s proof-of-concept code, you’ll see an obvious resemblance.

Figure 9-9: Hex-Rays de-compilation of Bankpatch.C’s WFP-disabling code

f0909.tif

To demonstrate the effects of Bankpatch.C’s WFP-disabling code, you can set up HandleDiff.exe with an automatic timer. Before the timer expires, you can install Bankpatch.C onto the system. Here is the command we used and an example of HandleDiff.exe’s output:

C:\> HandleDiff.exe –d –s 60 –f bankpatch.txt

-------------------------------

winlogon.exe (pid 684)

OldHandles: 582

NewHandles: 580

[-] 0x200 0x160001 File \WINDOWS\system32

[-] 0x7fc 0x100020 File \WINDOWS\system32

After installing Bankpatch.C, winlogon.exe had two fewer handles than before. In particular, the two missing handles were to file objects named “WINDOWS\system32” (actually they are directories opened with CreateFile). Now you have a good idea why closed handles, as well as created handles, are very valuable during dynamic analysis.

8 http://mnin.blogspot.com/2009/02/bankpatchc-detection-tool.html

9 http://www.ntcore.com/files/wfp.htm

API Monitoring/Hooking

API monitors are classic tools for reverse engineers and malware analysts. They provide a wealth of information about a program’s runtime behavior by intercepting calls to API functions and logging the relevant parameters. Many tools exist for this purpose, including Process Monitor, as mentioned in the previous section. Why would you want to create your own? Here are the most common reasons people create their own API-hooking tools:

· Most existing tools are GUI-only (no command-line version or batch mode).

· The existing tools might hook functions you don’t care about or not hook functions you care about.

· The existing tools might not output results in the exact format you want (for example, XML, SQL, CSV, binary dump, and so on).

· You might want to configure custom actions for a hook. For example, you can hook DeleteFile to make a copy before the file gets deleted. Or you can hook Sleep to reduce the amount of time a trojan waits before infecting the system.

Just because you hook a function doesn’t mean you do so for monitoring purposes. For example, we once had a few hundred packed variants of the same trojan and needed to extract a hard-coded encryption key from each binary. The encryption key wasn’t available until after the program was unpacked. The problem was that shortly after unpacking, the program infected the system on which it ran and then didn’t allow other variants to execute on the same system. Therefore, we needed to get the keys without infecting the system, or we’d have to revert the virtual machine for each sample.

The solution we came up with involved finding a common API function (for example, CreateEvent) that all trojans called after unpacking but before infecting the system. We built a DLL (using one of the following API-hooking libraries) that hooked CreateEvent. When the hook was triggered, the DLL scanned the process memory for the encryption key, dumped it to disk, and then terminated the process before it could proceed with infection. A command-line loader cycled through each sample in a directory and executed them with the API-hooking DLL. In less than a minute, we could extract the keys from hundreds of samples. This is just an example of how you can leverage API-hooking libraries even if you don’t plan on monitoring APIs or inspecting parameters in the conventional way.

Recipe 11-12 shows how to build an API monitor in Python using the WinAppDbg debugger framework. In some cases, that method isn’t desirable. For example, you may be dealing with malware that doesn’t run in a debugger or you may be designing a tool that needs to run on machines without Python. The recipes in this section show how to build API monitors that don’t require a debugger or any other frameworks. You can use one of the following libraries:

· Microsoft Detours: http://research.microsoft.com/en-us/projects/detours/

· WinAPIOverride32: http://jacquelin.potier.free.fr/winapioverride32/

· Mhook: http://codefromthe70s.org/mhook22.aspx

· madCodeHook: http://www.madshi.net/madCodeHookDescription.htm

· EasyHook: http://easyhook.codeplex.com/

· Nektra Devaire/Trappola: http://www.nektra.com/products/

Recipe 9-8: Building an API Monitor with Microsoft Detours

dvd1.eps

You can find supporting material for this recipe on the companion DVD.

Microsoft Detours is available for free with a noncommercial license, but only supports x86. For commercial use or for full x64 support, you must purchase a license. Detours supports development in C/C++, includes API functions to facilitate getting your DLL into the memory of the target process, and comes with a lot of source code examples for creating your own programs. This recipe shows how to build an API monitor with Detours and Microsoft Visual Studio.

Creating the API-Hooking DLL

1. Download and install Detours. It comes as an MSI (*.msi) and by default exists in a path such as C:\Program Files\Microsoft Research\Detours Express 2.1, which this example refers to as $DTHOME in the remainder of the steps.

2. Use Visual Studio to create a new solution. Choose Win32 Console Application and give your solution a name (this example uses DetoursHooks), as shown in Figure 9-10.

Figure 9-10: Creating a new project with Visual Studio

f0910.tif

3. Click Application Settings on the wizard and choose DLL as the Application type. This is shown in Figure 9-11. Then click Finish.

Figure 9-11: Choosing a DLL for your application type

f0911.tif

4. Copy the Detours header file ($DTHOME\include\detours.h) and library files ($DTHOME\lib\detours.lib and $DTHOME\lib\detoured.lib) into your Visual Studio project’s directory. In this example, a shared directory for these files was created so that other projects that you add to the same solution can access them. The location of our files is C:\Documents and Settings\Administrator\My Documents\Visual Studio 2008\Projects\DetoursHooks\Shared.

5. Modify your dllmain.cpp to include the detours.h header file and link with the detours.lib and detoured.lib libraries.

#include <windows.h>

#include <stdio.h>

#include "..\\Shared\\detours.h"

#pragma comment (lib, "..\\Shared\\detours.lib")

#pragma comment (lib, "..\\Shared\\detoured.lib")

6. For each function that you want to hook, create a variable for the target pointer (stores the address of the un-instrumented API) and the detour function (your hook code). You need to use the same prototype as defined in the Windows header files (or as displayed on MSDN) for the functions that you hook. Here is example code for DeleteFileA that copies the file to be deleted into an archive directory of your choosing (C:\archive).

// target pointer to un-instrumented API

static BOOL (WINAPI *RealDeleteFileA)(LPCSTR) = DeleteFileA;

// detours function

BOOL WINAPI HookDeleteFileA(LPCSTR lpFileName)

{

// save the last error

DWORD dwLastError = GetLastError();

// check if the parameter is valid

if (lpFileName != NULL && strrchr(lpFileName, '\\') != NULL)

{

// allocate memory for copied file name

PCHAR lpNewFile = new CHAR[MAX_PATH*2];

if (lpNewFile != NULL)

{

sprintf_s(lpNewFile,

MAX_PATH,

"c:\\archive\\",

strrchr(lpFileName, '\\') + 1);

// copy the file to be deleted into an archive

printf("Copy %s => %s\n", lpFileName, lpNewFile);

CopyFileA(lpFileName, lpNewFile, FALSE);

delete[] lpNewFile;

}

}

// restore last error

SetLastError(dwLastError);

return RealDeleteFileA(lpFileName);

}

7. You must add at least one exported function to your DLL. The function can be completely empty. This is a requirement of the Detours API. If you are using a hooking library other than Detours, you do not need to perform this step.

extern "C" __declspec(dllexport) void DummyFunc(void)

{

return;

}

8. Modify the DllMain function to install your hooks when a process loads the DLL. In addition, modify it to uninstall the hooks when a process unloads the DLL. You can do this with DetourAttach and DetourDetach, respectively. For example:

BOOL APIENTRY DllMain(HMODULE hModule,

DWORD dwReason,

LPVOID lpReserved)

{

// install the hook(s)

if (dwReason == DLL_PROCESS_ATTACH)

{

DetourTransactionBegin();

DetourUpdateThread(GetCurrentThread());

DetourAttach(&(PVOID&)RealDeleteFileA, DeleteFileA);

DetourTransactionCommit();

}

// uninstall the hook(s)

else if (dwReason == DLL_PROCESS_DETACH)

{

DetourTransactionBegin();

DetourUpdateThread(GetCurrentThread());

DetourDetach(&(PVOID&)RealDeleteFileA, DeleteFileA);

DetourTransactionCommit();

}

return TRUE;

}

9. In Visual Studio, click Build⇒ Build Solution. If there are no errors, you should have a compiled DLL named according to your project (DetoursHooks.dll in our case) in your Debug or Release directory.

Creating the DLL Injection Program

Now that you have created a DLL, you need to get it inside the process you want to monitor. If your target process is already running, you can inject the DLL in a number of ways—see Chapter 13. If you want to create a new process (such as your malware sample) and have your DLL injected into it upon startup, before any of the malware’s code executes, then you can use the method described next.

1. Add a new project to your existing Visual Studio solution. This way, you can manage all projects from the same place and compile them all at once. To do this, right-click the existing project name (e.g., DetoursHooks) in Visual Studio’s Solutions Explorer, click Add⇒ New Project, as shown in Figure 9-12. Give your injection program a name (this example uses DetoursInjection) and click Finish.

2. Add the Detours header and library files to your new project. It should look exactly the same as the code in Step 5 for creating the DLL.

Figure 9-12: Adding a new project to Visual Studio

f0912.tif

3. Use DetourCreateProcessWithDll within your injection program. The simple example that follows accepts the name of your DLL and the path to the process to execute. Anything after the process name on the command line is supplied as a command-line argument to the process being created. For simplicity, the program assumes your DLL (DetoursHooks.dll) and detoured.dll are in the same directory as your injection program.

int _tmain(int argc, _TCHAR* argv[])

{

STARTUPINFO si;

PROCESS_INFORMATION pi;

LPTSTR szCmdLine = NULL;

CHAR szDllName[MAX_PATH];

CHAR szDetouredDll[MAX_PATH];

BOOL bStatus;

if (argc < 3)

{

_tprintf(_T("\nUsage: %s <DLL> <PROCESS [ARGS]>\n"), argv[0]);

return -1;

}

if ((szCmdLine = GetArguments()) == NULL)

{

_tprintf(_T("Failed to parse command line!\n"));

return -1;

}

GetCurrentDirectoryA(MAX_PATH, szDetouredDll);

GetCurrentDirectoryA(MAX_PATH, szDllName);

strcat_s(szDetouredDll, MAX_PATH, "\\detoured.dll");

strcat_s(szDllName, MAX_PATH, "\\");

#ifdef _UNICODE

WideCharToMultiByte(CP_ACP, 0, argv[1], -1,

szDllName+strlen(szDllName),

MAX_PATH, NULL, NULL);

#else

strcat_s(szDllName, MAX_PATH, argv[1]);

#endif

memset(&si, 0, sizeof(si));

si.cb = sizeof(si);

bStatus = DetourCreateProcessWithDll(

NULL, // application name

szCmdLine, // full command line + arguments

NULL, // process attributes

NULL, // thread attributes

FALSE, // inherit handles

0, // creation flags

NULL, // environment

NULL, // current directory

&si, // startup info

&pi, // process info

szDetouredDll, // path to detoured.dll

szDllName, // path to dll to inject

NULL); // use standard CreateProcess API

if (bStatus) {

_tprintf(_T("Created process PID %d!\n"), pi.dwProcessId);

} else {

_tprintf(_T("Error creating process!\n"));

}

return 0;

}

4. Click Build⇒ Build Solution in Visual Studio. You should now have DetoursHooks.dll and DetoursInjector.exe in your Build or Release directory. Copy $DTHOME\detoured.dll into your Build or Release directory also.

Testing Your Hooks

We like to test out our hooks before using them on real malware. To create a test program, follow these steps:

1. Add a new project to your existing solution, just as you did before. This example uses the name TestProject.

2. Use this program to call the API function(s) that your DLL hooks. The following is an example of the test program.

#include <windows.h>

int _tmain(int argc, _TCHAR* argv[])

{

DeleteFileA("C:\\windows\\system32\\notepad.exe");

return 0;

}

3. Click Build ⇒ Build Solution in Visual Studio. Make sure you see TestProject.exe in your Debug or Release directory.

4. Execute your test program under the influence of your API-hooking DLL. The commands that follow show that all of the programs are gathered in a single location and that the C:\archive directory is empty to start. After running the test, C:\archive contains a copy of notepad.exe—the file that the test program attempted to delete.

C:\Test>dir

Volume in drive C has no label.

Volume Serial Number is B09B-EE95

Directory of C:\Test

05/17/2010 07:58 PM <DIR> .

05/17/2010 07:58 PM <DIR> ..

10/15/2009 06:38 PM 4,096 detoured.dll

05/17/2010 07:34 PM 218,624 DetoursHooks.dll

05/17/2010 07:34 PM 226,816 DetoursInjector.exe

05/17/2010 07:34 PM 30,720 TargetProject.exe

4 File(s) 480,256 bytes

2 Dir(s) 12,360,187,904 bytes free

C:\Test>dir C:\archive

Volume in drive C has no label.

Volume Serial Number is B09B-EE95

Directory of C:\archive

05/17/2010 07:24 PM <DIR> .

05/17/2010 07:24 PM <DIR> ..

0 File(s) 0 bytes

2 Dir(s) 12,360,187,904 bytes free

C:\Test>DetoursInjector.exe

Usage: DetoursInjector.exe <DLL> <PROCESS [ARGS]>

C:\Test>DetoursInjector.exe DetoursHooks.dll TargetProject.exe

Created process PID 920!

Copying C:\windows\system32\notepad.exe => c:\archive\notepad.exe

C:\Test>dir C:\archive

Volume in drive C has no label.

Volume Serial Number is B09B-EE95

Directory of C:\archive

05/17/2010 07:59 PM <DIR> .

05/17/2010 07:59 PM <DIR> ..

05/14/2010 04:28 PM 69,120 notepad.exe

1 File(s) 69,120 bytes

2 Dir(s) 12,360,097,792 bytes free

Recipe 9-9: Following Child Processes with Your API Monitor

Malware frequently creates new processes. The new process might be dropped or downloaded by the malware, or it might be an instance of an existing program, such as Internet Explorer or cmd.exe. In these cases, you need to “follow” the newly created processes in order to monitor them as well. Otherwise, you’ll only log a portion of the malware’s behaviors. The ability to recursively inject DLLs into new processes is one of the most sought after features in an API-monitoring tool. This recipe describes some of the techniques you can use to follow new processes.

Hooking Process-Creation APIs

Many users will hook process-creation API functions such as CreateProcessW, and insert code to inject the DLLs into the newly created process. The following is an example of that technique:

static BOOL (WINAPI *RealCreateProcessW)(

LPCWSTR, LPWSTR,

LPSECURITY_ATTRIBUTES,

LPSECURITY_ATTRIBUTES,

BOOL, DWORD, LPVOID, LPCWSTR,

LPSTARTUPINFOW,

LPPROCESS_INFORMATION) = CreateProcessW;

BOOL WINAPI HookCreateProcessW(LPCWSTR lpApplicationName,

LPWSTR lpCommandLine,

LPSECURITY_ATTRIBUTES lpProcessAttributes,

LPSECURITY_ATTRIBUTES lpThreadAttributes,

BOOL bInheritHandles,

DWORD dwCreationFlags,

LPVOID lpEnvironment,

LPCWSTR lpCurrentDirectory,

LPSTARTUPINFOW lpStartupInfo,

LPPROCESS_INFORMATION lpProcessInformation)

{

DWORD dwLastError = GetLastError();

BOOL bResult = FALSE;

CHAR szDetouredDll[MAX_PATH];

CHAR szDllName[MAX_PATH];

HMODULE hMod1 = NULL, hMod2 = NULL;

// get the full path to the detours DLL

hMod1 = GetModuleHandleA("detoured.dll");

GetModuleFileNameA(hMod1, szDetouredDll, MAX_PATH);

// get the full path to the hooking DLL

GetModuleHandleEx(

GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,

(LPCTSTR)&HookCreateProcessW,

&hMod2);

GetModuleFileNameA(hMod2, szDllName, MAX_PATH);

// route creation of new process through

// the detours API

bResult = DetourCreateProcessWithDll(

lpApplicationName,

lpCommandLine,

lpProcessAttributes,

lpThreadAttributes,

bInheritHandles,

dwCreationFlags,

lpEnvironment,

lpCurrentDirectory,

lpStartupInfo,

lpProcessInformation,

szDetouredDll,

szDllName,

(PDETOUR_CREATE_PROCESS_ROUTINEW)RealCreateProcessW);

SetLastError(dwLastError);

return bResult;

}

In most cases, this trick works fine, but there are so many API functions that can create a process. Figure 9-13 shows the relationship between 12 user mode API functions that can create processes, spread across four DLLs (kernel32.dll, shell32.dll, advapi32.dll, and ntdll.dll). You could hook all of the functions, but that would be quite tedious. You could only hookNtCreateProcessEx, but you’d lose some context (i.e., there would be no easy way to tell if the malware initially called WinExec or ShellExecuteA). Depending on your goals, you may not care about the extra work involved in hooking all functions or you might not care about the higher-level context. You also have to consider the fact that it’s possible to create processes with special API functions such as CreateProcessWithLogonW and CreateProcessWithTokenW, which utilize RPC. In these cases, the RPC server calls one of the process-creation APIs instead of the process in which your monitoring DLL is loaded.

Using AppInit_DLLs

Instead of individually hooking the process-creation APIs, another option is to leverage the AppInit_DLLs registry value. You can find this value under the following key: HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows. If you enter the paths to your DLLs separated with spaces or commas, as shown in Figure 9-13, then newly created processes will load the DLLs in the specified order.

Figure 9-13: Possible API functions for creating processes

f0913.eps

Note One “alternate” method of creating a process that we saw recently involved Microsoft Word. The malware called CoCreateInstance with the CLSID of Word.Application, which forced the svchost.exe running the DcomLaunch (DCOM Server Process Launcher) service to create a WINWORD.EXE process. Then the malware automated the execution of a VB script from within Word. The VB script launched a process that the malware dropped, thus making it a child process of WINWORD.EXE. This is just an example of how you cannot expect to follow processes by hooking API functions alone.

Figure 9-14: Using AppInit_DLLs to load your DLLs

f0914.tif

A drawback to using AppInit_DLLs is that the DLLs will only load into processes that also load user32.dll. All GUI applications and a majority of malware samples load user32.dll, but some command-line programs do not. Therefore, malware can still create a process without you being able to follow and monitor it.

Alternate Methods

An alternate method you can use involves registering a process-creation callback function in the kernel, which is described in Recipe 9-10. In this case, you can detect when malware creates new processes regardless of how it happens. Also, Recipe 8-9 showed you how to automatically inject DLLs into new processes with Sandboxie.

Recipe 9-10: Capturing Process, Thread, and Image Load Events

dvd1.eps

You can find supporting material for this recipe on the companion DVD.

A notification routine is a callback function that the system executes when certain events occur. The events discussed in this recipe are process creation, thread creation, and image loading. Over the past few years, malware with rootkit components such as Mebroot,10 BlackEnergy v2,11 Rustock,12 and TDL313 have exploited notification routines. The payloads of such rootkits commonly include forcing new processes to load a malicious DLL, terminating a process immediately after it starts (for anti-debugging/anti-detection), or switching a new thread’s SSDT to point at an alternate table (see Recipe 17-6).

Using Notification Routines

There are a few legitimate uses for notification routines. Many antivirus products register callback functions that check processes for harmful strings, instructions, or known signatures. In this manner, the antivirus product can prevent execution of the process or prevent a process from loading an infected DLL. Another legitimate use involves creating an event monitor for dynamic analysis of malware. This recipe shows you how to implement a driver that alerts you when any events occur on the system while your malware sample executes.

The following prototypes describe the API functions that drivers use for registration. All of the necessary header files are included in the Windows Driver Kit (WDK).

NTSTATUS PsSetCreateProcessNotifyRoutine(

IN PCREATE_PROCESS_NOTIFY_ROUTINE NotifyRoutine,

IN BOOLEAN Remove

);

NTSTATUS PsSetCreateThreadNotifyRoutine(

IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine

);

NTSTATUS PsSetLoadImageNotifyRoutine(

IN PLOAD_IMAGE_NOTIFY_ROUTINE NotifyRoutine,

);

The first parameter to each API function is a pointer to a user-defined callback function of the specified type. Here are the prototypes for the callback functions:

VOID (*PCREATE_PROCESS_NOTIFY_ROUTINE)(

IN HANDLE ParentId,

IN HANDLE ProcessId,

IN BOOLEAN Create);

VOID (*PCREATE_THREAD_NOTIFY_ROUTINE)(

IN HANDLE ProcessId,

IN HANDLE ThreadId,

IN BOOLEAN Create);

VOID (*PLOAD_IMAGE_NOTIFY_ROUTINE)(

IN PUNICODE_STRING FullImageName,

IN HANDLE ProcessId,

IN PIMAGE_INFO ImageInfo);

The following rules apply to notification routines:

· Process creation: When a process is created, the process-creation callback executes in the context of the thread that created the new process. The ProcessId and ParentId parameters identify the process and its parent.

· Thread creation: When a thread is created, the thread-creation callback executes in the context of the thread that created the new thread. The ThreadId parameter identifies the newly created thread ID.

· Image load: The image load callback is called whenever an executable image is loaded or mapped into memory. Images are loaded when the main executable for a process is mapped into memory, when the process loads a DLL, or when a kernel driver loads. The image load callback receives the path on disk to the image being loaded and a pointer to an IMAGE_INFOstructure, which specifies the image’s base address in memory and its size.

The following code shows an example driver that uses these API functions for monitoring purposes:

#include "ntddk.h"

#include "stdio.h"

NTSTATUS DriverEntry(

IN PDRIVER_OBJECT DriverObject,

IN PUNICODE_STRING theRegistryPath)

{

//Driver initialization…

PsSetCreateProcessNotifyRoutine(

(PCREATE_PROCESS_NOTIFY_ROUTINE)ProcessNotifyRoutine,

FALSE);

PsSetCreateThreadNotifyRoutine(

(PCREATE_THREAD_NOTIFY_ROUTINE)ThreadNotifyRoutine);

PsSetLoadImageNotifyRoutine(

(PLOAD_IMAGE_NOTIFY_ROUTINE)LoadImageNotifyRoutine);

return STATUS_SUCCESS;

}

//This function looks up a process's name given its EPROCESS

VOID GetProcessName(PCHAR pEprocess, PCHAR szProcess)

{

strncpy(

szProcess,

pEprocess + g_ProcessNameOffset,

MAX_PROCESS);

szProcess[MAX_PROCESS] = 0;

return;

}

//This function executes when the system starts a new process

VOID ProcessNotifyRoutine (

IN HANDLE ParentId,

IN HANDLE ProcessId,

IN BOOLEAN Create)

{

CHAR szProcess[MAX_PROCESS];

CHAR szParent[MAX_PROCESS];

PEPROCESS peProcess = NULL;

memset(szProcess, 0, sizeof(szProcess));

memset(szParent, 0, sizeof(szParent));

GetProcessName((PCHAR)PsGetCurrentProcess(), szParent);

PsLookupProcessByProcessId(ProcessId, &peProcess);

if (peProcess != NULL) {

GetProcessName((PCHAR)peProcess, szProcess);

ObDereferenceObject(peProcess);

}

if (Create) {

DbgPrint("[PROCESS START] %s (PID %d) started %s (PID %d)\n",

szParent,

ParentId,

szProcess,

ProcessId);

}

return;

}

//This function executes when processes load new DLLs

VOID LoadImageNotifyRoutine (

IN PUNICODE_STRING FullImageName,

IN HANDLE ProcessId,

IN PIMAGE_INFO ImageInfo)

{

WCHAR * ImageName = NULL;

ULONG Length = 0;

CHAR szProcess[MAX_PROCESS];

GetProcessName((PCHAR)PsGetCurrentProcess(), szProcess);

Length = (FullImageName->Length + 1) * sizeof(WCHAR);

ImageName = ExAllocatePoolWithTag(NonPagedPool, Length, 'data');

if (ImageName != NULL) {

memset(ImageName, 0, Length);

wcsncpy(ImageName,

FullImageName->Buffer,

FullImageName->Length);

DbgPrint("[IMAGE LOAD] %s (PID %d) loaded %ws\n",

szProcess,

ProcessId,

ImageName);

ExFreePoolWithTag(ImageName, 'data');

}

return;

}

//This function executes when processes start new threads

VOID ThreadNotifyRoutine (

IN HANDLE ProcessId,

IN HANDLE ThreadId,

IN BOOLEAN Create)

{

CHAR szProcess[MAX_PROCESS];

GetProcessName((PCHAR)PsGetCurrentProcess(), szProcess);

if (Create) {

DbgPrint("[THREAD START] %s (PID %d) thread started TID %d\n",

szProcess,

ProcessId,

ThreadId);

}

return;

}

Once you load the driver, you can execute the desired malware sample and observe its activity on the system. The code shown in this recipe prints debug messages, which you can capture with DebugView.14 The next few recipes, however, show how you can combine notification routines with other dynamic analysis tricks and log the results to a file instead. The image inFigure 9-15 shows how the debug messages appear after running a component of a trojan named Koobface.

Figure 9-15: The notification routines triggered by Koobface

f0915.tif

The left-hand column in the DebugView application shows the number for each debug message. Use those numbers to follow along with the descriptions of the events that follow:

· #14: Shows when v2capcha.exe started. Its parent process is explorer.exe because we launched v2capcha.exe by double-clicking it from Windows Explorer.

· #16–25: Shows the executable images mapped into memory as a result of v2capcha.exe starting. Although it is truncated a bit, the first image (#16), contains the path on disk to the v2capcha.exe application. The rest of the entries are DLLs loaded by the application.

· #26–27: Shows when v2capcha.exe launches cmd.exe. It doesn’t matter which API (CreateProcess, ShellExecute, WinExec, and so on) was used to start cmd.exe because you’re not hooking user mode functions to monitor events. Also notice that the process-creation callback function uses PsLookupProcessByProcessId to get a pointer to the new process’s EPROCESS block. Therefore, you can easily extend the output of the sample driver to include information such as the new process’s command-line parameters.

· #28: Shows when v2capcha.exe terminates.

· #29–39: Shows when cmd.exe begins. Its main executable and DLLs are mapped into memory.

· #40–41: Shows when the first cmd.exe process launches rundll32.exe.

· #42–65: Shows when rundll32.exe begins. Its main executable and DLLs are mapped into memory.

· #66–67: Shows when cmd.exe attempts to delete the main executable file for v2captcha.exe and an apparent batch script named captcha.bat. The notification routines discussed in this recipe are not responsible for monitoring file deletions. That information is available in Recipe 9-11.

As you can see, notification routines can be extremely useful for dynamic analysis. In case you were wondering, the process and thread events logged by Process Monitor, shown in Recipe 9-1, are the result of using notification routines. However, because Process Monitor isn’t open source, you can’t take custom actions when the notifications are triggered. With just a few modifications to the code in this recipe, you can program the driver to take action on events rather than passively logging the activity.

Note Recipe 17-9 describes how you can use Volatility to detect registered callback functions in memory dumps because they are so often used by rootkits.

10 www.f-secure.com/weblog/archives/vb2008_kasslin_florio.pdf

11 http://www.secureworks.com/research/threats/blackenergy2/

12 http://www.reconstructer.org/papers/Rustock.C%20-%20When%20a%20myth%20comes%20true.pdf

13 http://rootkit.com/newsread.php?newsid=979

14 http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx

Data Preservation

One of the most troublesome aspects of dynamic malware analysis is that things happen so quickly; sometimes you don’t get a chance to react. As previously mentioned, change detection tools can miss files or registry keys that are deleted before the second snapshot. Similarly, if processes terminate shortly after they start, a lot of potentially valuable information is lost, such as the contents of the process’s memory. This section shows how you can build a driver that uses SSDT hooks to preserve data (for more details on SSDT hooks, see Recipe 17-6). It’s the same technique that rootkits have used for years to hide processes, files, registry keys, and other data, but you can also use it to build analysis tools. The DVD that accompanies this book contains the full source code to the snippets shown in the next few recipes. Here is a description of what the recipes contain:

· Recipe 9-11: Shows how to prevent processes from terminating by hooking ZwTerminateProcess

· Recipe 9-12: Shows how to prevent files from being deleted by hooking ZwSetInformationFile and ZwDeleteFile

· Recipe 9-13: Shows how to prevent drivers from loading by hooking ZwLoadDriver and ZwSetSystemInformation

· Recipe 9-14: Shows how to install and operate the data preservation module described in Recipes 9-11 through 9-13.

Hooking the SSDT is relatively simple and will not work against some malware samples. Consider the image in Figure 9-16, which shows the relationship of API calls that are typically used to delete files. The driver that we present in this section will only be effective against the calls that pass through the SSDT—in other words, calls made from a user mode program. If malware loads its own driver and calls ZwDeleteFile or ZwSetInformationFile directly, then the data preservation driver will not be able to intercept or prevent those attempts. Of course, you can use the data preservation module to prevent malware from loading its own driver also (Recipe 9-13), but that could cause a significant difference in the malware’s behavior.

The upcoming discussions contain a lot of code and key words related to APIs. If you need a source of knowledge to accommodate your reading, please see http://undocumented.ntinternals.net. Also, here are a few tools similar to the data preservation module presented in this section:

· Capture-BAT (http://dfrws.org/2007/proceedings/p23-seifert.pdf) is a dynamic analysis tool built with a focus on portability to versions of Windows other than XP. It outputs activity logs and copies deleted files to a specified directory. It is also open source, so you can build new capabilities into the program as you see fit.

· Flypaper (https://www.hbgary.com/products-services/flypaper/) is a closed source, but free (for non-commercial use) tool by HBGary. It prevents processes from exiting, prevents memory from being freed, and can block incoming and outgoing network traffic.

Figure 9-16: The relationship of common APIs used to delete files

f0916.eps

Recipe 9-11: Preventing Processes from Terminating

This recipe describes how to prevent processes from terminating with your data preservation driver. Processes can terminate themselves by calling ExitProcess, or they can terminate other processes by calling TerminateProcess. You might want to handle these cases differently, so it’s important to understand how you can distinguish the two in your kernel driver. As you can see by the function definitions that follow, ExitProcess only takes one parameter—an integer that specifies the exit status. TerminateProcess takes one additional parameter—an open handle to the process to be terminated, which must have at least PROCESS_TERMINATE access rights.

VOID

WINAPI ExitProcess(

IN UINT ExitStatus

);

BOOL

WINAPI TerminateProcess(

IN HANDLE hProcess,

IN UINT ExitStatus

);

Both of these functions are exported by kernel32.dll and they both internally call ntdll!NtTerminateProcess, which then leads to the kernel version—ZwTerminateProcess. Because all calls ultimately lead to the same place, how can you tell if the calling process got there via ExitProcess or via TerminateProcess? The answer is based on the handle value. ExitProcess is hard-coded to pass a value of 0xFFFFFFFF to ntdll!NtTerminateProcess. Therefore, if ZwTerminateProcess receives a handle value of 0xFFFFFFFF, it knows the calling process itself is about to shut down. Otherwise, the calling process is attempting to shut down another process.

The source code that follows shows the function that executes in place of the real ZwTerminateProcess once the SSDT hooks are installed.

NTSTATUS NewZwTerminateProcess(

HANDLE ProcessHandle,

NTSTATUS ExitStatus)

{

CHAR szProcess[MAX_PROCESS+4];

CHAR szProcessToTerminate[MAX_PROCESS+4];

NTSTATUS ntStatus;

PEPROCESS eProcess = NULL;

CHAR szLog[MAX_LOG_SIZE];

DWORD ProcessId = 0;

if (ProcessHandle != 0) {

ntStatus = ObReferenceObjectByHandle(

ProcessHandle,

PROCESS_ALL_ACCESS,

NULL,

KernelMode,

&eProcess,

NULL

);

memset(szProcessToTerminate, 0, sizeof(szProcessToTerminate));

if (ntStatus == STATUS_SUCCESS && eProcess != NULL) {

GetProcessName((PCHAR)eProcess, szProcessToTerminate);

ProcessId = PsGetProcessId(eProcess);

ObDereferenceObject(eProcess);

}

sprintf(szLog,

"terminating %s (PID %d)",

szProcessToTerminate,

ProcessId);

LogMessage("PROCESS TERMINATE", szLog);

if ((DWORD)ProcessHandle == 0xFFFFFFFF) {

ZwSuspendProcess(ProcessHandle);

}

}

return ((ZWTERMINATEPROCESS)(RealZwTerminateProcess)) (

ProcessHandle, ExitStatus);

}

As you can see, if the calling process is about to terminate, the driver suspends it instead. This keeps the process around long enough for you to dump its memory or analyze it using any other dynamic analysis tools at your disposal. In some cases, you’ll find that malware won’t execute certain behaviors because it can’t terminate one of its components. For example, a trojan might drop a batch script that waits until its dropper terminates and then installs a service. If you prevent process termination, the batch script will loop infinitely and you’ll never see the second- and third-stage behaviors. Fortunately, you can manually resume a process after it’s been trapped by the data preservation driver. Using a tool such as Process Hacker, right-click the suspended process and choose Resume Process, as shown in Figure 9-17.

Figure 9-17: Resuming a suspended process with Process Hacker

f0917.tif

Recipe 9-12: Preventing Malware from Deleting Files

This recipe describes how to prevent files from being deleted. By hooking ZwDeleteFile and ZwSetInformationFile, you can preserve files that malware (or a user) tries to delete in the following manners:

· From Explorer (right-clicking a file and choosing Delete)

· Using the del command in cmd.exe

· Calling the native ntdll!NtDeleteFile

· As a result of a move operation such as kernel32!MoveFile

The following function executes in place of the real ZwDeleteFile once the SSDT hooks are installed. It gets the file’s name from the OBJECT_ATTRIBUTES structure and logs the activity (you can see the full code for the generic LogMessage function on the DVD).

NTSTATUS NewZwDeleteFile(

POBJECT_ATTRIBUTES ObjectAttributes)

{

WCHAR szFileName[MAX_PATH*2];

ULONG MaxLength = MAX_PATH*2;

CHAR szLog[MAX_LOG_SIZE];

memset(szFileName, 0, sizeof(szFileName));

if (ObjectAttributes->ObjectName != NULL &&

ObjectAttributes->ObjectName->Buffer != NULL &&

ObjectAttributes->ObjectName->Length < MaxLength)

{

wcsncpy(szFileName,

ObjectAttributes->ObjectName->Buffer,

ObjectAttributes->ObjectName->Length);

szFileName[ObjectAttributes->ObjectName->Length] = L'\0';

sprintf(szLog, "deleting file %ws", szFileName);

LogMessage("FILE DELETE", szLog);

}

return STATUS_SUCCESS;

}

The following function executes in place of the real ZwSetInformationFile once the SSDT hooks are installed. Because there are many reasons, besides deletion, that a program might call ZwSetInformationFile, you have to create a filter based on the FILE_INFORMATION_CLASS value. In this case, you’re interested in any calls where that value is FileDispositionInformation or FileRenameInformation.

NTSTATUS NewZwSetInformationFile(

IN HANDLE FileHandle,

OUT PIO_STATUS_BLOCK IoStatusBlock,

IN PVOID FileInformation,

IN ULONG Length,

IN FILE_INFORMATION_CLASS FileInformationClass)

{

PFILE_DISPOSITION_INFORMATION pFDI = NULL;

WCHAR szFileName[MAX_PATH*2];

CHAR szLog[MAX_LOG_SIZE];

pFDI = (PFILE_DISPOSITION_INFORMATION) FileInformation;

if (

((FileInformationClass == FileDispositionInformation) \

&& pFDI->DeleteFile) \

|| \

(FileInformationClass == FileRenameInformation) \

)

{

memset(szFileName, 0, sizeof(szFileName));

GetFileName(FileHandle, szFileName);

sprintf(szLog, "deleting file %ws", szFileName);

LogMessage("FILE DELETE", szLog);

return STATUS_SUCCESS;

}

return ((ZWSETINFORMATIONFILE)(RealZwSetInformationFile))(

FileHandle,

IoStatusBlock,

FileInformation,

Length,

FileInformationClass);

}

Recipe 9-13: Preventing Drivers from Loading

As mentioned in the beginning of this section, malware can load a driver and perform actions beyond the control of the data preservation module. Therefore, we built in the ability to prevent additional drivers from loading. Keep in mind that this can have adverse effects on your analysis, so it is not a good idea to always enable this feature. The point is to give you a configurable tool that lets you control which operations are permitted and which ones are denied on a case-by-case basis.

The following code snippets show the replacement functions for ZwLoadDriver and ZwSetSystemInformation. When the driver is loaded, these hooks cover the documented methods of loading drivers. If there are undocumented methods of loading a driver, or if there is a vulnerability in your kernel that allows DKOM attacks, then malware can still delete files and terminate processes.

NTSTATUS NewZwLoadDriver(PUNICODE_STRING DriverName)

{

CHAR szLog[MAX_LOG_SIZE];

WCHAR * szDriver = NULL;

ULONG Length = 0;

if (DriverName != NULL && DriverName->Length > 0)

{

Length = (DriverName->Length + 1) * sizeof(WCHAR);

szDriver = (WCHAR *) ExAllocatePoolWithTag(

PagedPool, Length, 'data');

if (szDriver != NULL) {

wcsncpy(szDriver,

DriverName->Buffer,

DriverName->Length);

sprintf(szLog, "loading driver %ws", szDriver);

LogMessageA("DRIVER LOAD", szLog);

ExFreePoolWithTag(szDriver, 'data');

}

}

return STATUS_SUCCESS;

}

NTSTATUS NTAPI NewZwSetSystemInformation(

IN SYSTEM_INFORMATION_CLASS SystemInformationClass,

IN PVOID SystemInformation,

IN ULONG SystemInformationLength)

{

CHAR szLog[MAX_LOG_SIZE];

if (SystemInformationClass == SystemLoadAndCallImage)

{

sprintf(szLog, "loading driver %s", "UNKNOWN");

LogMessageA("DRIVER LOAD", szLog);

return STATUS_SUCCESS;

}

return ((ZWSETSYSTEMINFORMATION)(RealZwSetSystemInformation))(

SystemInformationClass,

SystemInformation,

SystemInformationLength);

}

Recipe 9-14: Using the Data Preservation Module

dvd1.eps

You can find supporting material for this recipe on the companion DVD.

On this book’s DVD, you can find an archive named preservation.zip, which contains a pre-compiled driver (for XP only) and a command-line loader. The following code is the syntax for using the driver:

C:\preservation>preservation.exe

Usage: preservation.exe [OPTIONS]

OPTIONS:

l load driver and log actions

f prevent file deletions

d prevent driver loading

p prevent process termination

n install notify routines

u unload the driver

EXAMPLE:

preservation.exe lfdpn (prevent and log all)

preservation.exe l (allow and log all)

As shown in the example usage, you can enable all of the data preservation techniques by combining the flags on the command line, such as lfdpn. If you only want to log activity (similar to an API monitor) instead of prevent it, then just specify the l flag when you load the driver.

To use the data preservation driver, load it with your desired options from the command line, as shown in Figure 9-18. We chose to enable all the available hooks and also monitor events with the notification routines described in Recipe 9-10.

Figure 9-18: Loading the preservation driver before malware analysis

f0918.tif

Execute the malware that you are interested in, wait however long you think is necessary, and then look in the C:\Preservation directory for logs. You’ll find a text file that contains entries similar to the ones that you saw via DebugView in Figure 9-15. However, in this case, you’ll also see alerts regarding process termination, file deletion, and DLL and driver loading. Here is an example:

[PROCESS START] fetch_10d8c4282 (PID:2776)

started rundll32.exe (PID 2956)

[THREAD START] fetch_10d8c4282 (PID:2776)

started thread (TID 2972)

[IMAGE LOAD] rundll32.exe (PID:2956)

loaded \Device\HarddiskVolume1\WINDOWS\system32\rundll32.exe

[IMAGE LOAD] rundll32.exe (PID:2956)

loaded \SystemRoot\System32\ntdll.dll

[IMAGE LOAD] rundll32.exe (PID:2956)

loaded \WINDOWS\system32\kernel32.dll

[...truncated for brevity...]

[IMAGE LOAD] rundll32.exe (PID:2956)

loaded \WINDOWS\system32\comctl32.dll

[IMAGE LOAD] rundll32.exe (PID:2956)

loaded \WINDOWS\tete458015t.dll

[IMAGE LOAD] rundll32.exe (PID:2956)

loaded \WINDOWS\system32\sfc.dll

[IMAGE LOAD] rundll32.exe (PID:2956)

loaded \WINDOWS\system32\sfc_os.dll

[IMAGE LOAD] rundll32.exe (PID:2956)

loaded \WINDOWS\system32\wintrust.dll

[IMAGE LOAD] rundll32.exe (PID:2956)

loaded \WINDOWS\system32\crypt32.dll

[IMAGE LOAD] rundll32.exe (PID:2956)

loaded \WINDOWS\system32\msasn1.dll

[FILE DELETE] rundll32.exe (PID:2956)

deleting file \WINDOWS\system32\drivers\asyncmac.sys

[DRIVER LOAD] services.exe (PID:736)

loading driver \Registry\Machine\

System\CurrentControlSet\Services\AsyncMac

We’ve only shown a snippet of the output in the previous code. Based on these lines, you can make the following conclusions:

· The malware (named fetch_10d8c4282.exe) started a new rundll32.exe process.

· The new process starts normally, by having its main executable (rundll32.exe) mapped into memory first, followed by ntdll.dll and kernel32.dll.

· The rundll32.exe process then loads tete458015t.dll, which has a suspicious name (at least, we don’t recognize it). As you’ll see in Chapter 13, the purpose of rundll32.exe is to execute a given DLL.

· Right after loading tete458015t.dll, the process loads several legitimate DLLs such as sfc.dll and sfc_os.dll (contains functions for disabling Windows File Protection), wintrust.dll, crypt32.dll, and msasn1.dll (contains functions related to cryptography, hashing, and encoding). All DLLs loaded after tete458015t.dll were probably loaded as dependencies of tete458015t.dll because rundll32.exe does not need access to those libraries in legitimate cases.

· The process tries to delete a legitimate driver (WINDOWS\system32\drivers\asyncmac.sys, which is the RAS Asynchronous Media Driver). Windows File Protection normally prevents this from being successful, but because the malware loaded sfc.dll and sfc_os.dll, you can surmise that it disabled WFP on asyncmac.sys before trying to delete it.

· Next, you can see services.exe initiating a driver load event. The parameter you see is the path in the registry where the driver’s configuration exists. Did tete458015t.dll inject code into services.exe to make it load the driver? Probably not—services.exe is the Service Control Manager. You’ll see services.exe taking action when other processes use API functions such as StartService to load drivers.

Figure 9-19 shows how you can analyze the preserved evidence using tools such as Process Hacker. The executed malware resulted in the creation of nine other processes, all of which still exist in the process listing because they weren’t allowed to terminate. You can click them and see their command-line parameters or go to another tab to view threads, memory, handles, and so on. The process we clicked in Figure 9-19 is the rundll32.exe process. Now you know why the output showed traces of tete458015t.dll!

Figure 9-19: Examining process details with Process Hacker

f0919.eps

Recipe 9-15: Creating a Custom Command Shell with ReactOS

dvd1.eps

You can find supporting material for this recipe on the companion DVD.

The Windows command shell (cmd.exe) doesn’t have a good mechanism for maintaining command history. You can investigate the commands previously typed into a given shell by typing DOSKEY /history, but that is not possible if the shell has been closed or if the system has been rebooted. This recipe explains how to build a custom command shell that you can use to log command history to a file. The benefit to logging commands is you’ll preserve the contents of batch files dropped by malware (because each line in a batch file is essentially run through the command shell) and you can see any commands that attackers type into a shell even if the traffic is encrypted over the network (useful for capturing backdoor activity).

Note In their paper Extracting Windows command line details from physical memory,15 Richard M. Stevens and Eoghan Casey describe how you can extract command history from the memory of csrss.exe with a plug-in for the Volatility memory forensics platform.

Building ReactOS

To get started with ReactOS, follow these steps:

1. Download and install the ReactOS build environment16 for Windows/NT compatible systems. You can try the build environment for Linux-compatible systems, but the ReactOS developers warn that it may be out-of-date.

2. During the installation, you’ll see a components selector like the one shown in Figure 9-20. For the purposes of this recipe, you only need the Subversion Tools—all others are optional.

Figure 9-20: Installing the ReactOS build environment

f0920.tif

3. To access the build environment, click Start⇒ All Programs⇒ ReactOS Build Environment ⇒ ReactOS Build Environment. The first time this program runs, it will ask you to download the most recent ReactOS source code from SVN. You can comply by typing ssvn create into the prompt. By default, the source files will be installed to C:\Documents and Settings\USERNAME\reactos, which we refer to as %ROSPATH% in the remainder of this recipe.

4. Once the download is complete, you can type make to build all files for the operating system. The first time you do this, it can take up to an hour, depending on the speed of your system. In the future, you can modify source files and then rebuild modules individually, which takes only a few seconds each.

Creating a Custom Shell

Complete the following steps to build a custom command shell. On the DVD that accompanies this book, you’ll find an archive named cmd_files.zip. If you’re using version 0.3.11 of the ReactOS source code, you can just extract the files in that archive into your %ROSPATH%\build\shell\cmd directory and skip to Step 7.

1. Create a new header file named %ROSPATH%\base\shell\cmd\proxy.h with the following contents:

void StripCRLF(LPTSTR);

void LogCommand(LPTSTR);

void LogStart(void);

void LogCommandWithArgs(LPTSTR, LPTSTR);

2. Modify %ROSPATH%\base\shell\cmd\precomp.h to include your new header file, like this:

#include "proxy.h"

3. Create a new source file named %ROSPATH%\base\shell\cmd\proxy.c. This is the file that contains your custom functions defined in proxy.h. By default, the code that follows creates a file named C:\commands.log that contains any commands that a user, an attacker, or a malware sample executed through your command shell.

void StripCRLF(LPTSTR first)

{

int in=0;

int out=0;

for(in=0; in < _tcslen(first); in++)

{

TCHAR c = first[in];

if (c != _T('\n') && c != _T('\r'))

first[out++] = c;

}

first[out] = _T('\x00');

}

void LogCommand(LPTSTR first)

{

TCHAR * dup = NULL;

FILE * LOG = NULL;

dup = _tcsdup(first);

if (dup == NULL) {

error_out_of_memory();

return;

}

LOG = _tfopen(_T("C:\\commands.log"), _T("a"));

if (LOG != NULL) {

StripCRLF(dup);

_ftprintf(LOG, _T("> %s\n"), dup);

fclose(LOG);

}

free(dup);

}

void LogStart(void)

{

TCHAR buf[256];

_stprintf(buf, _T("** New Command Shell [PID:%d]"),

GetCurrentProcessId());

LogCommand(buf);

}

void LogCommandWithArgs(LPTSTR cmd, LPTSTR args)

{

TCHAR * com = NULL;

u_int len = (_tcslen(cmd) + _tcslen(args) + 2) * sizeof(TCHAR);

com = cmd_alloc(len);

if (com == NULL)

{

error_out_of_memory();

return;

}

_tcscpy(com, cmd);

_tcscat(com, args);

LogCommand(com);

cmd_free(com);

}

4. Add the following line to %ROSPATH%\build\shell\cmd\cmd.rbuild. This makes the build environment compile your proxy.c file.

<file>proxy.c</file>

5. Modify %ROSPATH%\base\shell\cmd\cmd.c to insert calls to your custom functions. In particular, you want to add a call to LogStart at the very beginning of the Initialize function. Optionally, you can change the welcome banner from “ReactOS Operating System[...]” to “Microsoft Windows[...].” Otherwise, attackers may notice that they’re working with a modified command shell. Then add the following lines in bold to the appropriate places in the DoCommand function.

ret = cmdptr->func(param);

LogCommand(com);

cmd_free(com);

LogCommandWithArgs(first, rest);

ret = Execute(com, first, rest, Cmd);

cmd_free(com);

6. Modify %ROSPATH%\base\shell\cmd\parser.c and insert a call to your custom function from the ParseCommand routine, as shown in the following code.

if (!ReadLine(ParseLine, FALSE))

return NULL;

bLineContinuations = TRUE;

LogCommand(ParseLine);

7. Now recompile the cmd.exe module, by typing remake cmd into the ReactOS build environment, as shown in Figure 9-21.

Figure 9-21: Compiling the custom command shell

f0921.tif

Installation and Usage

You should now have a customized command shell in %ROSPATH%\output-i386\base\shell\cmd\cmd.exe. The last step is to install the new cmd.exe into your honeypot or malware analysis system. You can’t just overwrite the original cmd.exe because it is protected by WFP (Windows File Protection). The InstallCmdProxy.exe program on the DVD is an installer that temporarily disables WFP, makes a backup of your original cmd.exe, and then replaces the original copy with your custom shell. Be aware—the installer only works on Windows XP. You can use the custom command shell on Vista and 7, but you must disable WFP manually in order to overwrite cmd.exe. Figure 9-22 shows an image of the installer application.

Figure 9-22: Installing the command shell with InstallCmdProxy.exe

f0922.tif

At this point, your custom command shell is ready to use. You can expect to log all sorts of interesting activity. Each time a new instance of cmd.exe starts up, the LogStart function prints the process ID of the new cmd.exe process. Each time the malware (or attacker if you’re using it on a honeypot) types a command into cmd.exe, the LogCommand function logs the activity. The following output is from a malware sample known to antivirus vendors as Pakes or Dogrobot. You can see evidence of the malware disabling security services, killing processes, setting access controls on the system directory, and deleting itself.

> ** New Command Shell [PID:1280]

> sc config ekrn start= disabled

> ** New Command Shell [PID:2752]

> taskkill.exe /im ekrn.exe /f

> ** New Command Shell [PID:2812]

> taskkill.exe /im egui.exe /f

> ** New Command Shell [PID:176]

> net stop wscsvc

> ** New Command Shell [PID:2888]

> net stop SharedAccess

> ** New Command Shell [PID:2924]

> sc config sharedaccess start= disabled

> ** New Command Shell [PID:1272]

> cacls "C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\" /e /p everyone:f

> ** New Command Shell [PID:376]

> cacls C:\WINDOWS\system32 /e /p everyone:f

> ** New Command Shell [PID:2956]

> afc90a.bat

> @echo off

> @echo ad32rwhlk>>321.aqq

> @del 321.aqq

> @del "C:\kdhxyy.exe"

> @del afc90a.bat

> @exit

The next output was captured from a malware sample known to antivirus vendors as an Rbot variant. You can see it installs several other executables on the system and then launches batch files through cmd.exe to delete the evidence.

> ** New Command Shell [PID:3060]

> C.tmp_deleteme.bat

> :try

> del "C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\IXP000.TMP\C.tmp"

> if exist "C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\IX.TMP\C.tmp" goto try

> del C.tmp_deleteme.bat

> ** New Command Shell [PID:2952]

> "C:\Program Files\Common Files\Microsoft Shared\MSINFO\Del.bat"

> :try

> del "C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\IXP000.TMP\B.tmp"

> if exist "C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\IX.TMP\B.tmp" goto try

> del "C:\Program Files\Common Files\Microsoft Shared\MSINFO\Del.bat"

> ** New Command Shell [PID:3108]

> C:\WINDOWS\Deleteme.bat

> :try

> del "C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\IXP000.TMP\E.tmp"

> if exist "C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\IX.TMP\E.tmp" goto try

> del C:\WINDOWS\Deleteme.bat

> ** New Command Shell [PID:156]

> WinRAR.exe_deleteme.bat

> ** New Command Shell [PID:3248]

> I.exe_deleteme.bat

> :try

> del "C:\I.exe"

> if exist "C:\I.exe" goto try

> del I.exe_deleteme.bat

> ** New Command Shell [PID:3196]

> C:\WINDOWS\Deleteme.bat

> :try

> del "C:\Love.exe"

> if exist "C:\Love.exe" goto try

> del C:\WINDOWS\Deleteme.bat

15 http://www.dfrws.org/2010/proceedings/2010-307.pdf

16 http://www.reactos.org/wiki/Build_Environment