Archive for September, 2006

Legacy Drivers, WDM, WDF, KMDF, UMDF… WTF?!

Monday, September 25th, 2006

These days someone asked me what is the relationship between the operating system and the alphabet soup that has been accumulated over the builds. In this post, I’ll try to summarize what kind of each of these models brings new and what is their relationship with the operating system.

In the beginning, God created the Earth, soon after Windows NT came and, introduced what we used to call “Legacy Drivers”. This model was used in all Windows NT drivers and there are still a lot of its concepts in the new models. Here, the classical concepts of IRP and I/O Manager have been adopted.

With Plug-and-Play, Windows 98 and Windows 2000 advent, it was the first family to implement WDM (Windows Driver Model). This model has been intended to unify development concepts for drivers on both platforms (9x and NT). Anyone who has had contact with VXDs knows how different Windows NT drivers concepts are. VXDs programmers had to learn a completely different technology. With WDM, besides using the same sources to generate drivers for both platforms, the same binary can be used on both platforms. That is, I can generate a single binary that will run on both Windows 98 and Windows 2000. Moreover, WDM is able to identify and classify devices by Plug-and-Play class and subclass, and thus, associate drivers and filters to them. The Legacy drivers are still supported in Windows 2000 as well as VXDs in Windows 98. In practice, for NT platform, WDM drivers are Legacy Drivers that use new rules to classify and assign drivers to devices. As to the 9x platform, the change was drastic. WDM calls are routed to a VXD that makes the translation of IRP to IOP.

As time passed by, the 9x platform ended, and the big need was to make driver development a painless thing to do. That was the priority in WDF development (Windows Driver Foundation). One comment I will not forget was the one about KMDF video (Kernel Mode Driver Framework) on Channel9 that says: “It’s hard to write kernel mode driver. Really hard. In fact, it’s hard to believe how hard it is. Well, Windows Driver staff has worked hard to become a little less difficult (not easy) to write kernel mode drivers that do not knock your system down. You know, blue screens and stuff”. WDF brings a lot of things already done, details that were repetitive, even in WDM, now there is a default behavior in WDF but that may change as the need. Applying a bigger abstraction allows, for example, registering callback routines to handle only interest events and lets the framework take care of the power management, say by the way. Impressive, don’t you think? Greater abstraction does not necessarily mean less control. You can still have access to all IRP members you think that is easy for you and want to do this with your own hands.

I’ve read in one of these articles from NT Insider that a developer still has to know too much to do too little in Kernel Mode development and Microsoft wants to change that. Developers could know less about so many things involved in driver development to do something simple. WDF is composed of KMDF and UMDF (User Mode Driver Framework). That’s right! User Mode. Ensuring that drivers are less harmful to the system and therefore prevent a flaw in a driver that is not critical to the system (such as your MP3 player) causes a blue screen and drops your whole system. It’s really interesting to see how this works. Your driver runs as a COM server, using a system account and uses a communication framework that is based on COM to have interaction with the system as if in Kernel Mode. There is one .ppt very interesting that was used in the WinHEC 2006 that shows in a simplified way how it happens. It isn’t possible develop .net drivers because of impact of system performance, but it is desirable, no doubt. It is clear that only certain types of devices can work in User Mode, but for the growing line of USB devices it’s been already real. WDF will come along with Windows Vista DDK, many (not to mention all) of drivers of the new system were migrated to WDF, but this is not a luxury only for Windows Vista. KMDF can even be installed on Windows 2000 SP4 and UMDF beta can be installed on Windows XP SP2.

Getting Started…

Monday, September 11th, 2006

Let’s just stop the idle talk and get our hands dirty. Answering some requests, in this post I will take the steps to develop a minimum driver. At the end of this post, we will have a module that will be installed and loaded into the kernel without any functional purpose. I am not gonna talk yet about process context, Devices, Symbolic Links, IOCTL, IRP, DPC, ISR, FGTS, IR, or even about IPTU. I’ll talk a little about everything over time. Today we’ll just build from scratch an empty module that could eventually serve as a starting point for future experiments.

To compile a driver for Windows, we need minimally the Windows Device Driver Kit (DDK). The DDK can be obtained from the Microsoft website. There are some alternatives to get the DDK, but the most economical of them is to opt  for downloading the ISO file. When this post was created, the version available on the site was the “Windows Server 2003 SP1 DDK” with 230 MB.

I do not think it’s necessary to provide each detail about the DDK installation, something like “Take the mouse to the button which the text on it written NEXT and press the left mouse button”. The default options are more than sufficient for our experiments. If you don’t have much on disk space, change the options of “Build Environments” to be installed only Windows 2000 environments, as it appears below. This will make the necessary space drop from 628 MB to 246 MB. If you still have trouble installing the DDK, you might consider professional help.

After installing the DDK, we have everything we need to create drivers. Obviously, there are many other tools that make our work more comfortable, but in this post we will stay on bread and water.

For our test, create a folder where we put the driver source (e.g. C:\Projects\Useless). Inside this folder, create a text file named “Useless.c” and use your text editor from the bottom of your heart to enter the source below. Not worth using “Microsoft Word”, but if you thought it, use the “Notepad” and seek for professional help.

#include 
 
VOID OnDriverUnload(IN PDRIVER_OBJECT   pDriverObject);
 
/****
***     DriverEntry 
**
**      That's the entry point of our driver, everything starts here,
**      and later it'll get confused and confused...
*/
 
NTSTATUS DriverEntry(IN PDRIVER_OBJECT  pDriverObject,
                     IN PUNICODE_STRING pusRegistryPath)
{
    //-f--> If there is a debugger attached to the system,
    //      we could see the message below.
    DbgPrint("Wow, it's really working!\n");
 
    //-f--> Here we inform the system that our driver can be
    //      unloaded dynamically and the callback routine that
    //      will handle the completion of all is the OnDriverUnload.
    pDriverObject->DriverUnload = OnDriverUnload;
 
    //-f--> Phew! We were able to get here, that deserves
    //      a success return code to the system.
    return STATUS_SUCCESS;
}
 
 
/****
***     OnDriverUnload
**
**      Callback routine that deals with the necessary
**      terminations to get this driver unloaded.
*/
 
VOID OnDriverUnload(IN PDRIVER_OBJECT   pDriverObject)
{
    //-f--> If there is a debugger attached to the system,
    //      we could see the message below.
    DbgPrint("But now? I did nothing so far...\n");
}

After doing “Copy” and “Paste” from the source above, we can notice some points. In this example, remember saving your source file with extension “.c”; otherwise, we will have some problems in exporting our entry point. In a few posts, I intend to demonstrate how to have “.cpp” files and how to use object-orientation in building drivers. Another important point to be noted is that the use of  DriverUnload callback function is optional. If you don’t wanna provide a callback function for the driver unload notification, just don’t do this assignment. However, in these circumstances the driver will not be able to be unloaded. You need this assignment even for an empty function, so that its driver is able to get turned off. Let’s talk about this in more details later (see details in the DDK).

It is still necessary to create files that define the project. In the same directory as the source, create a file named “makefile” and copy the text below to it. This file simply implements the real “Makefile” that is used to build various DDK components.

!INCLUDE $(NTMAKEENV)\makefile.def

The makefile should never be edited. All settings what type of driver will be compiled on, which sources will make up the driver, which libraries will be used and many other settings are specified in the file named “SOURCES” that must exist in the same directory. Its content is the one that appears below. In conjunction with the “makefile.def” from DDK, these files create a series of macros that greatly facilitate the project file creation as a Visual Studio “.vcproj”. Posts about how to use Visual Studio 2005 to compile drivers are also on my list of tasks.

TARGETNAME=Useless
TARGETPATH=obj
TARGETTYPE=DRIVER
 
SOURCES=Useless.c

There are too many details to be discussed on a file “SOURCES” for now, let’s just limit it to know that your final binary name is defined by “TARGETNAME” variable, and the list of source code that makes up your driver should be set in “SOURCES” variables separated by space. (For more details, see it in DDK).

At last, after having the files created, let’s compile them. For this, we must set some environment variables to define where the libraries, headers and binaries are that will compile your sources. Installing DDK provides a shortcut on your Start Menu that points to the file “setenv.bat”. This shortcut opens a console window and sets up the environment to compile the driver. You find the shortcut in “Build Environments” that was installed in your Start Menu. Should there be a “Checked” and a “Free” version of this shortcut. Whereas the “Checked” version sets up the environment to generate drivers with debug information, “Free” version sets up the environment for their final version, which is equivalent to “Release”. Selecting the “Checked Build” from “Windows 2000” menu, we will have a Command Prompt window with the title as shown below. Follow the same image steps below to get the same results.

Launching the command “build”, we should have the following results:

All done now, we just need a victim, or rather, a volunteer. It could be your cousin’s computer, a younger brother’s, an intern’s, or just anyone’s that is not bigger than you or also a person’s who can not make you lose your job. Try not to use your own machine, we may need it later. Usually I use virtual machines; they are perfect victims. Next, we will install our driver by the simplest way I know. We will edit some entries at the registry and restart the system. There are some APIs and tools that register the driver and put it to work right away without restarting the system, but remember, we are just on bread and water.

Firstly we copy the “Useless.sys” file that was generated in subfolder “objchk_w2k_x86\i386” of our project to the folder “\Windows\System32\drivers”. Then, using the registry editor, you need to create the “Useless” key inside the “Services” key of the system and put the values as shown in the figure below.

These lines are the minimum for a driver in the registry. The “Type” value specifies a Kernel driver, the “Start” value indicates that it will be started manually and finally the “ErrorControl” value tells the system how it should behave if this driver fails in its load time (details, details and details…).

At this point, we have to restart the system, so that the system becomes aware of this new module. Our driver won’t start automatically. Once the system has restarted, go to the Console Prompt and start the driver with the command “net start Useless”. Isn’t it impressive as anything happened? This means that the driver is doing exactly what it has been intended to do, “Nothing”. You can stop the driver using the “net stop Useless” command. If we had the debugger attached to the system, we’d have the following outputs.

For those who have never created a driver before and cannot see the output in WinDbg, it becomes very frustrating, because nothing happened to prove that our driver was really loaded and running in Kernel. To give a little more action to this monotony, modify the DriverEntry() function as shown below. Recompile the driver, replace it at the directory “\Windows\System32\drivers” and finally reboot the machine.

NTSTATUS DriverEntry(IN PDRIVER_OBJECT  pDriverObject,
                     IN PUNICODE_STRING pusRegistryPath)
{
    //-f--> Say hello to the BSOD and get used to it...
    *(PVOID*)0x00000000 = 0;
 
    //-f--> We wont survive to see this.
    return STATUS_SUCCESS;
}

In this case, when the driver starts, it will try to write at zero address; an exception is thrown here and we will have our first Blue Screen of Death (BSOD). Have fun!

I’ll try to interim posts for beginners with more advanced ones in order to try to attract the attention of both groups. In this post we have nothing very useful, but we already have the basis for people who have never had contact with this type of development before, so they can do their first step. Every citizen has the right to create a Blue Screen. 😉