Archive for August, 2006

ExAllocatePool(WithoutTag)

Wednesday, August 30th, 2006

Like most driver developers, I also have to build drivers for various Windows versions. Although I will not comment about VXDs on this post, I can comment about having a single .SYS file that can run either in Windows Server 2003 and Windows NT. I know that this OS is no longer sold and has no support from Microsoft, but it is impressive to see how we still find Windows 95 and Windows NT both in corporate environments and in end user’s homes. So, we always have to stay in dodging each operating system limitations when we are planning to develop a system.

A function that received a new version was ExFreePool(), its new version is the ExFreePoolWithTag(), which is implemented since Windows 2000. To get a single binary that runs in both versions, we have to use the oldest one that still is supported by recent systems.

Let’s consider the source below for a driver that performs the amazing task of allocating memory dynamically.

#include <ntddk.h>
 
NTSTATUS DriverEntry(PDRIVER_OBJECT  pDriverObject,
                     PUNICODE_STRING pusRegistryPath)
{
   PVOID pTemp;
 
   //-f--> I'm thinking it's easy allocating memory.
    if (!(pTemp = ExAllocatePool(NonPagedPool, 10))
       return STATUS_NO_MEMORY;
 
   //-f--> Memory leak is vulgarity.
    ExFreePool(pTemp);
   return STATUS_SUCCESS;
}

Using the build environment for Windows XP to compile this driver, we can see the same binary runs on Windows Server 2003, Windows XP, Windows 2000 and Windows NT. Or at least, it should. When we try to start our wonderful driver on a Windows NT, we get the following message:

But how? If we take a closer look at our driver, we find that it is a miserable traitor. As a DLL, drivers are executable modules that use the classical structure PE to declare their dependencies. Thus, we can open our driver in the “Dependency Walker” and see what it expects from life to be loaded. You may not believe, but it really statically depends on the new allocating memory function. A bastard traitor!

This is because, within the file ntddk.h, there is a set of definitions that ends up changing the calls from ExFreePool() to  ExFreePoolWithTag(). So, we can get those tons of code written for NT and use them in builds for newer systems without changing a single line, or we can share some source between drivers for Windows NT and newer systems.

#define POOL_TAGGING 1
 
...
 
#ifdef POOL_TAGGING
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,' kdD')
#define ExAllocatePoolWithQuota(a,b) ExAllocatePoolWithQuotaTag(a,b,' kdD')
#endif

Then you ask me the following questions: But what is the real difference between old and new version? Can my driver experience problems if you try to use the old version running on a newer system? Is there intelligent life outside earth?

The new version came to help detecting memory leaks. Each memory allocation is associated with a tag. These tags can be viewed using debugging tools such as WinDbg. For example: If your entire library setup uses a tag, and its communication library uses another, it will be easy to know which ones are leaving allocated memory. This makes it easy to know which programmer on your team you will be fired. The macros in ntddk.h use only one tag to the default memory allocations which has no associated tags.

But, what about drivers compiled with the DDK for Windows NT, running on newer systems? These drivers actually use the old functions. If we use Depends on them we could see that. As a matter of compatibility, the old functions are still exported in newer systems to support older drivers. Let’s take a look at the implementation of ExAllocatePool() Windows 2000.

Implementation of the old function simply forwards the memory allocation for the new function and uses the tag ‘None’. This assures us that we will not die of cancer if we continue using old functions in a new system.

Well, what were we intending to do? Oh yes, build a new driver that can run on both Windows NT and later systems. We could simply use the Windows NT DDK and have backward compatibility to run on newer systems. This is possible, but some of the functions we see in the DDK are actually defined as macros. The IoSetCompletionRoutine() function is a classic example of this. Using an old DDK means not having some of these functions defined and we’d have to patch DDK definitions if we wanted to enjoy them. There are also those cases of missing people on desert islands with only the latest DDK.

#include <ntddk.h>
 
#undef ExAllocatePool
#undef ExFreePool
 
NTSTATUS DriverEntry(PDRIVER_OBJECT  pDriverObject,
                     PUNICODE_STRING pusRegistryPath)
{
 
...

To resolve this, just after including ntddk.h, let’s take a #undef in macros that will redirect older calls to new version of ExAllocatePool() and that’s it, your problems have gone away. Rebuilding, we will have a new driver that uses the old allocation functions. Thus, it can be used in systems since Windows NT.

This method disadvantage is that your drivers can not use the tags when running on a system that supports them. Actually, you can have a single binary that, when running on Windows NT can use ExFreePool(), whereas when running on a newer system it uses ExFreePoolWithTag(), but that’s another post.

Debug or Not Debug

Friday, August 25th, 2006

Here is a very simple thing to do. Generating conditional compilation using the pre-processor along with the DBG symbol has to be a simple task. Just as the _DEBUG symbol is defined by the projects written in C/C++ in Visual Studio, DBG symbol is defined by DDK to inform you that the current build is Checked or Free. Thus, we can illustrate with a simple code snippet as it follows below:

//-f--> I'm thinking it's easy...
#ifdef DBG
   DbgPrintf("Checked Version\n");
#else
   DbgPrintf("Free Version\n");
#endif

The code above will display the message “Checked Version” when the build is done in Checked and “Free Version”, when the build is done in Free, right?

WRONG!!! That’s it, wrong! The DBG symbol is defined as 1 in checked builds and set to 0 in free builds. Thus, we have DBG = 1 for Checked builds and DBG = 0 for Free builds. The way it is, the code above still displays the message “Checked Version”, even when compiled as Free. This is because the DBG symbol is still set, it is set to 0, but it is defined in either way. So, to adequately test this condition, we must do it with #if in place of #ifdef, as it follows below:

//-f--> OK, now it will display the correct build configuration
#if DBG
   DbgPrintf("Checked Version\n");
#else
   DbgPrintf("Free Version\n");
#endif
 
//-f--> Testing only for Free builds
#if !DBG
   DbgPrintf("Free Version\n");
#endif

To facilitate debugging, it is not rare to see some drivers with the code that, when compiled in Checked, interrupts its execution using the following code:

//-f--> Interrupt the execution only in checked version
#if DBG
   __debugbreak();
#endif

If the build test was incorrect here, we would have a beautiful blue screen whether the code above went to the production. As you may know, calling breakpoints in code throw exceptions that would be handled by the debugger, but if the debugger is not attached to the system, HORRIBLE THINGS WILL HAPPEN.

In the file there ntddk.h, there is a shown macro below that helps to test the build conditions in Checked.

#if DBG
    #define IF_DEBUG if (TRUE)
#else
    #define IF_DEBUG if (FALSE)
#endif

In this way the syntax below is quite clear:

IF_DEBUG
{
   __debugbreak();
}

Since we are talking about conditional builds, we may also have to build specialization in SOURCES file. DDKBUILDENV environment variable is set to “che” when the build is done in Checked, or it is set to “fre” when in Free builds. So we can, for example, using different paths for libs, in accordance with the current build configuration.

!if "$(DDKBUILDENV)" == "fre"
BUILD_CONFIG=Release
!else
BUILD_CONFIG=Debug
!endif
 
TARGETLIBS=.\Lib\$(BUILD_CONFIG)\HtsCpp.lib

KdPrint((“Hello World!!!”));

Wednesday, August 16th, 2006

Meu primeiro computador

In this first post, I’ll assume that no one will read it to talk about me. After all, people who are interested in reading this blog may want to know my profile with a little more detail. Born in 1976, I could not imagine what which job I would have when I was 13 years old and saw a computer for the first time. My cousin had a CP200 and I had no idea what kind of video game it was. When my cousin did an upgrade to a powerful MSX, I bought his CP200. I just did not know anything about it. I that time had the games and applications were recorded on K7 tape and I had no recorder to enjoy all this technology. As a result, we were only me and the BASIC manual and that’s how I learned how to program.

I didn’t take so long to decide that I wanted to be a programmer, but I had to program using a language that once I heard about, the C language. I wanted to do what others found difficult, and perhaps for this reason, instead of doing the traditional course of Data Processing, I decided to study Industrial Informatics at ETE Jorge Street School in Sao Caetano do Sul.

Although I knew how to program in Visual Basic, I still knew nothing about C for Windows when I was doing my internship at Provectus. I’d say I could not fell into a better place. They worked with a proprietary hardware that was equipped with a V40 processor. Using a library of run-time produced by them, we could make a program for DOS run inside a panel data collection. As time passed by, the first DLL, the first service, the first application, the first driver and long-awaited first blue screen came. The network adapter that received the messages from the network hardware was also a self-produced, and I started just maintaining the code that was already done. We had to redo all the collectors’ network protocol that was all done for us. So while my friends were doing forms, queries and reporting, the fact that debugging a network protocol, part in assembly and part in C both the driver and firmware using an oscilloscope, gave me certainty that was what I wanted to do.

After four years working with data collectors, my wish about programming exclusively for Windows made me work on a financial site. Building MTS and COM + components was good to get another business view. But as expected, tasks using C/C++ for these purposes were little and soon ended, so they were putting me in projects of ASP with SQL, and thanks to that, I moved to work in an enterprise about information security. I just could not bear to arriving at work and having to open the Query Analyzer.

SCUA really introduced me to the low-level programming for Windows. Window Hooks, Authentication DLL (GINA), logon in Windows with smart-card and biometric filters, file systems, file and partition encryption in real time, access control, SoftICE and WinDBG. I’ve currently worked at Open Communications Security and I am responsible for developing low-level software against hacking, detecting, combating and foiling root-kits, trojans and key loggers.

In this blog, I intend to give some tips about developing C/C++ for Windows, including some things in User-Mode and others in Kernel-Mode. I hope I can help by offering some tips and facilities for this small group of people who develops drivers for Windows in Brazil.