< Day Day Up > |
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 yet it's just a simple "hello world" device driver. #include "ntddk.h" NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath )' { DbgPrint("Hello World!"); return STATUS_SUCCESS; } Wow, that one was easy, wasn't it? You can load this code into the kernel, and the debug statement will be posted.[4]
Our rootkit will be composed of several items, each of which we describe in the sections that follow. The Device Driver Development KitTo 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.
The Build EnvironmentsThe 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 FilesYou 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 FileThis file should be named SOURCES in all-capital letters, with no file extension. The SOURCES file should contain the following code: TARGETNAME=MYDRIVER TARGETPATH=OBJ TARGETTYPE=DRIVER SOURCES=mydriver.c 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 exist and be discovered within the binary itself. Better names for the driver are those that look like legitimate device drivers. Examples include MSDIRECTX, MSVID_H424, IDE_HD41, SOUNDMGR, and H323FON. 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: SOURCES= myfile1.c \ myfile2.c \ myfile3.c 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: INCLUDES= c:\my_includes \ ..\..\inc \ c:\other_includes
If libraries need to be linked in, then you will have a TARGETLIBS variable. We use the NDIS library for some of our rootkit drivers, so the line might look like this: TARGETLIBS=$(BASEDIR)\lib\w2k\i386\ndis.lib or this: TARGETLIBS=$(DDK_LIB_PATH)\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. The MAKEFILE FileFinally, 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 UtilityOnce 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 Menu Programs. 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.com You 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 RoutineWhen 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: // 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; } Now we can safely load and unload the driver without rebooting. |
< Day Day Up > |