Building the Windows Device Driver
Our first example will operate on the Windows XP and 2000 platforms and will be designed as a simple device driver. In reality, this isn't actually a rootkit yetit's just a simple "hello world" device driver.
Wow, that one was easy, wasn't it? You can load this code into the kernel, and the debug statement will be posted.[4][4] See the section Logging the Debug Statements later in this chapter to learn how to capture debug messages.
#include "ntddk.h"
NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject,
IN PUNICODE_STRING theRegistryPath )'
{
DbgPrint("Hello World!");
return STATUS_SUCCESS;
}
Our rootkit will be composed of several items, each of which we describe in the sections that follow.
The Device Driver Development Kit
To build our Windows device driver, we'll need the Driver Development Kit (DDK). DDKs are available from Microsoft for each version of Windows.[5] Chances are you will want the Windows 2003 DDK. You can build drivers for Windows 2000, XP, and 2003 using this version of the DDK.[5] Information on Windows DDKs is available at: www.microsoft.com/ddk/
The Build Environments
The DDK provides two different build environments: the checked and the free build environments. You use the checked-build environment when you're developing a device driver, and you use the free-build environment for release code. The checked build results in debugging checks being compiled into your driver. The resulting driver will be much larger than the free-build version. You should use the checked build for most of your development work, and switch to the free build only when you're testing your final product. While exploring the examples in this book, checked builds are fine.
The Files
You will write your driver source code in C, and you will give the filename a .c extension. To start your first project, make a clean directory (a suggestion is C:\myrootkit), and place a mydriver.c file there. Then copy into that file the "hello world" device-driver code shown earlier.You will also need a SOURCES file and a MAKEFILE file.
The SOURCES File
This file should be named SOURCES in all-capital letters, with no file extension. The SOURCES file should contain the following code:
The TARGETNAME variable controls what your driver will be named. Remember that this name may be embedded in the binary itself, so using a TARGETNAME of MY_EVIL_ROOTKIT_IS_GONNA_GET_YOU is not a good idea. Even if you later rename the file, this string may still existand be discoveredwithin the binary itself.
TARGETNAME=MYDRIVER
TARGETPATH=OBJ
TARGETTYPE=DRIVER
SOURCES=mydriver.c
Many device drivers are already loaded on a computer. Sometimes you can get great ideas by just looking at the existing list and coming up with some variations on their names.The TARGETPATH variable will usually be set to OBJ. This controls where the files go when they are compiled. Usually your driver files will be placed underneath the current directory in the objchk_xxx/i386 subdirectory.The TARGETTYPE variable controls the kind of file you are compiling. To create a driver, we use the type DRIVER.On the SOURCES line, a list of .c files is expected. If you want to use multiple lines, you need to place a backslash ("\") at the end of each line (except the last line). For example:
Better names for the driver are those that look like legitimate device drivers.
Examples include MSDIRECTX, MSVID_H424, IDE_HD41, SOUNDMGR, and H323FON.
Notice that there is no trailing backslash character on the last line.Optionally, you can add the INCLUDES variable and specify multiple directories where include files will be located. For example:
SOURCES= myfile1.c myfile2.c myfile3.c
INCLUDES= c:\my_includes ..\..\inc c:\other_includes
Create Executables with DDKsA little-known bit of trivia about Microsoft Driver Development Kits is that they can be used to compile regular program executables, not just driver files. To do this, you set the TARGETTYPE to PROGRAM. There are other types as well, such as EXPORT_DRIVER, DRIVER_LIBRARY, and DYNLINK. |
or this:
TARGETLIBS=$(BASEDIR)\lib\w2k\i386\ndis.lib
You may need to find the ndis.lib file on your own system and hard-code the path to it when you're building the NDIS driver. For examples, see Chapter 9, Covert Channels.$(BASEDIR) is a variable that specifies the DDK install directory. $(DDK_LIB_PATH) specifies the location where default libraries are installed. The rest of the path may differ depending on your system and the DDK version that you're using.
TARGETLIBS=$(DDK_LIB_PATH)\ndis.lib
The MAKEFILE File
Finally, create a file named MAKEFILE, using all capital letters, and with no extension. MAKEFILE should contain the following text on a line by itself:
!INCLUDE $(NTMAKEENV)\makefile.def
Running the Build Utility
Once you have the MAKEFILE, SOURCES, and .c files, all you need to do is start the checked-build environment in the DDK, which opens a command shell. The checked-build environment can be found as a link under the Windows DDK group from the Start MenuPrograms. Once you have the build environment command shell open, change the active directory to your driver directory and type the command "build." Ideally there won't be any errors, and you will now have your very first driver! One hint: make sure your driver directory is in a location where the full path does not contain any spaces. For example, put your driver into c:\myrootkit.Rootkit.comYou can find an example driver complete with the MAKEFILE and SOURCES files already created for you at: www.rootkit.com/vault/hoglund/basic_1.zip
The Unload Routine
When you created the driver, a theDriverObject argument was passed into the driver's main function. This points to a data structure that contains function pointers. One of these function pointers is called the "unload routine." If we set the unload routine, this means that the driver can be unloaded from memory. If we do not set this pointer, then the driver can be loaded but never unloaded. You will need to reboot to remove the driver from memory.As we continue to develop features for our driver, we will need to load and unload it many times. We should set the unload routine so that we don't need to reboot every time we want to test a new version of the driver.Setting the unload routine is not difficult. We need to create an unload function first, then set the unload pointer:
Now we can safely load and unload the driver without rebooting.
// BASIC DEVICE DRIVER
#include "ntddk.h"
// This is our unload function
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
DbgPrint("OnUnload called\n");
}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT theDriverObject,
IN PUNICODE_STRING theRegistryPath)
{
DbgPrint("I loaded!");
// Initialize the pointer to the unload function
// in the DriverObject
theDriverObject->DriverUnload = OnUnload;
return STATUS_SUCCESS;
}