Driverlib—The Static Analysis Tool for Drivers

Driverlib is a Python library designed to automate some of the tedious reverse engineering tasks required to discover key pieces of information from a driver. Typically in order to determine which device names and IOCTL codes a driver supports, we would have to load it into IDA Pro or Immunity Debugger and manually track down the information by walking through the disassembly. We will take a look at some of the driverlib code to understand how it automates this process, and then we'll harness this automation to provide the IOCTL codes and device names for our driver fuzzer. Let's dive into the driverlib code first.

Using the powerful built-in Python library from Immunity Debugger, finding the device names inside a driver is quite easy. Take a look at Example 10-2, which is the device-discovery code from driverlib.


This code simply retrieves a list of all referenced strings from the driver and then iterates through the list looking for the "\Device\" string, which is a possible indicator that the driver will use that name for registering a symbolic link so that a user-mode program can obtain a handle to that driver. To test this out, try loading the driver C:\WINDOWS\System32\beep.sys into Immunity Debugger. Once it's loaded, use the debugger's PyShell and enter the following code:

*** Immunity Debugger Python Shell v0.1 ***
Immlib instanciated as 'imm' PyObject
READY.
>>> import driverlib
>>> driver = driverlib.Driver()
>>> driver.getDeviceNames()
['\\Device\\Beep']
>>>

You can see that we discovered a valid device name, \\Device\\Beep, in three lines of code, with no hunting through string tables or having to scroll through lines and lines of disassembly. Now let's move on to discovering the primary IOCTL dispatch function and the IOCTL codes that a driver supports.

Any driver that implements an IOCTL interface must have an IOCTL dispatch routine that handles the processing of the various IOCTL requests. When a driver loads, the first function that gets called is the DriverEntry routine. A skeleton DriverEntry routine for a driver that implements an IOCTL dispatch is shown in Example 10-3:


This is a very basic DriverEntry routine, but it gives you a sense of how most devices initialize themselves. The line we are interested in is

DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IOCTLDispatch

This line is telling the driver that the IOCTLDispatch function handles all IOCTL requests. When a driver is compiled, this line of C code gets translated into the following pseudo-assembly:

mov     dword ptr [REG+70h], CONSTANT

You will see a very specific set of instructions where the MajorFunction structure (REG in the assembly code) will be referenced at offset 0x70, and the function pointer (CONSTANT in the assembly code) will be stored there. Using these instructions, we can then deduce where the IOCTL-handling routine lives (CONSTANT), and that is where we can begin searching for the various IOCTL codes. This dispatch function search is performed by driverlib using the code in Example 10-4.


This code utilizes Immunity Debugger's powerful search API to find all possible matches against our search criteria. Once we have found a match, we send a Function object back that represents the IOCTL dispatch function where our hunt for valid IOCTL codes will begin.

Next let's take a look at the IOCTL dispatch function itself and how to apply some simple heuristics to try to find all of the IOCTL codes a device supports.