Reverse engineering

Windows Building Environment for Kernel Driver Development

Dejan Lukan
April 9, 2013 by
Dejan Lukan

Details of Driver Development Environment

In the previous article , we saw that upon installing WDK 7.1.0, we got build environments for Windows 7, Windows Server 2003, Windows Vista, Windows Server 2008 and Windows XP. Since we're on Windows XP, we'll be using this build environment.

Inside the Windows XP folder are the checked and free build environments. The checked build environment builds a driver that has debugging enabled and compiler optimizations disabled, which can be of great help when debugging our driver. The free build environment should be used at the end to provide the production driver, which has debugging disabled and optimizations enabled.

We need to keep in mind that the driver built for a specific version of Windows will actually be supported on all versions of Windows up until and including that Windows version. We need to keep this in mind when choosing the build target.

If we open the relnote.htm file that was installed in the C:WinDDK7600.16385.1 directory, we'll get to the WDK documentation. There's a great table that describes what each of the subdirectories in the C:WinDDK7600.16385.1 directory contains. The table is shown on the picture below:

The src directory should contain the WDK driver samples, but it only contains a few pictures on how to sign the binary. This is because we didn't check the Samples check box during the installation of the DDK. Nevertheless, the samples can also be obtained online at http://code.msdn.microsoft.com/windowshardware. Let's download the first hello-world sample as shown below:

We can see that the WpdHelloWorld driver supports four objects: device object, storage object, folder object and a file object. Once we click on the driver, we'll be redirected to the page presented below, where we can download the sample driver. We can also see that we need to have Visual Studio 2012 installed. I had to switch to Windows 7 to install Visual Studio 2012 to be able to use this project. If we're using Visual Studio 2012, we should download WDK 8.0, since it's fully integrated with Visual Studio 2012.

I downloaded the hello-world project archive, which contains the following:

The description.html file contains the same information as the online version of the hello-world project. The C++ folder actually contains the code that we're after. We need to extract the C++ folder somewhere on the disk and then open the WpdHelloWorldDriver.vcxproj file with Visual Studio 2012 as shown below:

Once that is done, Visual Studio will load the project file and present us with the project code that we can browse.

Building a Simple Program with Build.exe

When we only have access to the 7.1.0 version of the WDK, which is true for Windows XP operating systems, we have to build programs and kernel drivers with the build.exe program. To do so, we first need to go to the build environment by opening the "x86 Checked Build Environment" as such:

The reason why we're doing this is because the build environment automatically sets the required environment variables as seen below (note that only a few environmental variables are presented for brevity):

We need to create a new folder C:driver and place the file main.c in it. The main.c file is a simple hello world program with this C++ code:

[cpp]

#include <stdio.h>

int __cdecl main(void) {

printf("Hello World!n");

return 0;

}

[/cpp]

In our build environment console, we have to move to the newly created directory C:driver, which can be seen below. We first moved to the wanted directory and then listed the files in it, and only the file main.c is present as it should be.

In order to continue, we must take a look at the TARGETTYPE macro, which specifies the type of program being built. TARGETTYPE is used by the build.exe program instructions to note what kind of input files to expect and what we would want to build. The build.exe program reads the macros from the filename sources that are located in the code directory. Each sources file must contain the following macros:

  • TARGETNAME: specifies the name of the binary to be built without the extensions.
  • TARGETTYPE: take a look below.
  • SOURCES: specifies the files with extensions to be compiled and separated by spaces.
  • TARGETLIBS: specify other libraries that we want to link to, separated by spaces.

The sources file can also contain other variables like the following [11]:

  • UMTYPE: specifies the target type
  • UMENTRY: name of the default entry point function
  • USE_MSVCRT: use this library
  • USE_STL: enabled C++ STL library
  • USER_C_FLAGS: specify compiler flags
  • MSC_OPTIMIZATION: specify which optimization flags are enabled

The TARGETTYPE macro can hold the following values:

  • PROGRAM: user-mode .exe program that does not export anything
  • PROGLIB: an executable program that also exports functions for other programs
  • DYNLINK: a DLL library that exports functions that other programs can use
  • LIBRARY: an import library that will be linked with other code in user-mode
  • DRIVER_LIBRARY: an import library that will be lined with other code in kernel-mode
  • DRIVER: a kernel mode driver
  • EXPORT_DRIVER: a kernel mode driver that also exports functions for other drivers
  • MINIPORT: a kernel mode driver that does not link with ntoskrnl.lib or hal.lib
  • GDI_DRIVER: a kernel mode graphics driver that links with win32k.sys
  • BOOTPGM: a kernel mode driver
  • HAL: the hardware abstraction layer
  • NOTARGET: no target should be actually created, only some processing

In our hello world program, we therefore need to use the following values for the required variables:

  • TARGETNAME: main
  • TARGETTYPE: PROGRAM
  • SOURCES: main.c
  • UMTYPE: console
  • UMENTRY: main
  • USE_MSVCRT: 1
  • MSC_OPTIMIZATION: /Od

The actual sources file will look like this:

After that, we should go back into the console window and execute the build command, which should read the sources file to get the values of specified variables to guide the build process. Then the build program should create an executable named main.exe as specified by the TARGETNAME variable. The whole compilation looks like this:

Now the C:driver directory contains the following files:

The file main.c and the sources file is there, but there are also two other files present in the directory: a log file that contains the detailed log of the build process, which can be very useful for analysis if something goes wrong, and a folder named objchk_wcp_x86 that actually holds the executable, symbol file and some other files as well. This can be seen on the picture below:

If we now run the executable main.exe, it should print "Hello World!" to the console window, as can be seen on the picture below:

We've just built a simple hello world program, which is different from kernel driver by quite a bit. When programming a driver, we should probably specify the TARGETTYPE=DRIVER in the sources file.

Building a Driver with WDK 8.0

We can use different methods to build the driver, but the method depends on whether we're using WDK 8.0 or WDK 7.1.0. If we're using WDK 8.0, then we can build a driver for Windows 8, Windows 7 or Windows Vista directly in Visual Studio 2012 or alternatively with MSBuild in command line. If we're using WDK 7.1.0, then we can build a driver for Windows XP using the build.exe program. Build.exe is actually now replaced by MSBuild, which uses the same compiler and build tools as in Visual Studio.

Let's take a look at how to build a driver in Visual Studio 2012. To begin, we need to download and install WDK 8.0 if we haven't done so already. It's advisable that you use Windows 7 with this setup. Because Visual Studio 2012 uses the MSBuild script underneath the GUI, we're going to describe how to use that to build the driver we've previously downloaded.

First we have to open "Developer Command Prompt for VS2012" as we can see on the picture below:

This is the development environment that Visual Studio 2012 uses to build its projects. If we execute the msbuild command, we can see that the command prints some error about us not specifying the project file; this means that the msbuild command is found and we can use it to build projects.

We can print the help information of that command by executing the "msbuild /?" command, which will print all the arguments that we can pass to msbuild. We won't display all the options here, because there are just too many of them. Rather than that, we'll present the exact command that we can use to build the Visual Studio project. The actual command that we can use is the following (note that we have to extract the hello-world project from above into the C:cpp directory):

[plain]

> msbuild /t:clean /t:build C:cppWpdHelloWorldDriver.vcxproj

[/plain]

When running that command, we'll receive errors like those below:

The errors are presented because we installed Visual Studio 2012 first and then installed WDK. If we want to make VS2012 recognize the WDK, we need to go to the "Add/Remove Programs" in Control Panel, right-click on WDK and repair the WDK installation. The picture below shows the initial repair window:

Once the WDK has been repaired, we need to restart the "Developer Command Prompt for VS2012" and issue the same command again. After that, the driver compiles without a problem. From there on, we can load the driver into the kernel and start using its services. In future articles we'll describe how to do this on our own example, so we won't do so here.

Conclusion

In this article, we've seen how we can compile a simple program with Visual Studio, MSBuild and build.exe. The process is the same when building kernel drivers, except that some variables are different. If we run newer versions of Windows, like Windows Vista or Windows 7, then we can install WDK 8.0, which can integrate into Visual Studio very easily so we can use it to develop a kernel driver.

However, in older versions of Windows operating systems, like Windows XP, we don't have that luxury, because we can only install WDK version 7.1.0, which cannot be seamlessly integrated with Visual Studio. Because of this, we have to build the kernel driver manually by hand with the build.exe program. We've seen the number of variables and their values that affect the compile process; those variables need to be put into the sources file, where the build.exe program can find them.
Basically, Visual Studio does the same as build.exe, but automates the building process so we don't have to do it manually. Underneath, it still uses the new MSBuild program, which is still command-line based, but we don't have to deal with that if we don't want to. I guess at some point of Windows development, we have to take a look at how to do it manually with the build.exe tool, because we can learn the internals of the Windows compilation scheme which can be useful in many areas of Windows architecture.

References:

[1] Kernel Mode Driver Tutorial: Part I: The Skeleton KMD, accessible at http://www.reverse-engineering.info/SystemCoding/SkeletonKMD_Tutorial.htm.

[2] Creating a New Software Driver, accessible at http://msdn.microsoft.com/en-us/library/windows/hardware/hh454833(v=vs.85).aspx.

[3] Driver Development Part 1: Introduction to Drivers, accessible at http://www.codeproject.com/Articles/9504/Driver-Development-Part-1-Introduction-to-Drivers.

[4] What is IRQL?, accessible at http://blogs.msdn.com/b/doronh/archive/2010/02/02/what-is-irql.aspx.

[5] I/O request packet, accessible at http://en.wikipedia.org/wiki/I/O_request_packet.

[6] IRP, accessible at http://msdn.microsoft.com/en-us/library/windows/hardware/ff550694(v=vs.85).aspx.

[7] DriverEntry routine, accessible at http://msdn.microsoft.com/en-us/library/windows/hardware/ff544113(v=vs.85).aspx.

[8] DRIVER_OBJECT, accessible at http://msdn.microsoft.com/en-us/library/windows/hardware/ff544174(v=vs.85).aspx.

[9] Don Burn, Getting Started with the Windows Driver Development Environment, Microsoft MVP, Windows Driver Kit, Windows Filesystem and Driver Consulting – windrvr.com.

[10] TARGETTYPE, accessible at http://msdn.microsoft.com/en-us/library/ff552920.aspx.

Become a certified reverse engineer!

Become a certified reverse engineer!

Get live, hands-on malware analysis training from anywhere, and become a Certified Reverse Engineering Analyst.

[11] using WDK/DDK build environment for drivers and non-drivers, accessible at http://randomlearningnotes.wordpress.com/2009/04/20/using-wdkddk-build-environment-for-drivers-and-non-drivers/.

Dejan Lukan
Dejan Lukan

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