Tuesday, April 17, 2012

Kernel Hacking Is Hard (KHIH)

Besides totally slacking on game hacking, I have taken the time to read four documents from Microsoft which I felt were important for gaining at least a bit of understanding of this driver craziness.
1. Architecture of the Kernel-Mode Driver Framework
2. Architecture of the Windows Driver Foundation
3. I/O Flow and Dispatching in WDF Drivers
4. Handling IRPs: What Every Driver Writer Needs to Know

Since I have a little (and I mean very little) understanding of drivers, I do have a bit of a suspicion that the PnkBstrK.sys driver is simply reading in custom IOCTLs and acting upon them. However, I wanted to confirm this. I did a bit of reverse engineering of the PnkBstrK.sys DriverEntry function. You'll notice in my IDA output I renamed a lot of things (and added comments) to make understanding what was going on a bit easier. Remember ';' for adding comments and right click -> rename for renaming labels/functions whatever.
PnkBstrK.sys DriverEntry function.
It's a pretty standard setup, loop over and set all IRP handlers to the same function. Except one, you'll notice at .text:10033FB (I renamed DeviceControlDispatchHandler) a function is moved into an offset into the DriverObject structure.  Initially when analyzing this, I missed that line and during debugging sessions always ended up going to the same stub dispatch function which did nothing. So what is this special handler? This is actually the dispatch handler that will handle the IRP_MJ_DEVICE_CONTROL "event" or whatever the hell they're called in kernel land. All of the other handlers don't do anything but accept the IRP and pass it on/finish it.

Later on, you'll notice a call to IoCreateDevice with the "\\device\pnkbstrk" as the device name. Further down in the code a symbolic name "\\DosDevices\\pnkbstrk_link" is also created which the user-mode applications will most likely call. However, I have yet to verify this.

Which is actually my biggest problem, trying to figure out how user-mode code calls into this driver. After searching around I found a really good forum post on how to gain a bit of additional information when debugging drivers. From that post I learned if you are actively debugging PnkBstrK.sys there's a way you can determine what IRP handlers are mapped to. In WinDBG you run "!drvobj PnkBstrK 7" which gives the following output:
PnkBstrK.sys IRP dispatch handlers
You'll notice they're all set to the same address, except one: [0e] IRP_MJ_DEVICE_CONTROL. This is what I believe the user-mode code calls to interact with this driver. So my next step(s) are to do a bit of analysis of this device control handler function. For that I turn back to IDA.

Here's what the device control handler function looks like by itself:
device control handler function with no comments/names.
So that's not really helpful. Again, taken from that above post he suggests including the IRP and IO_STACK_LOCATION structures into the IDA session. This ends up helping a lot. How do you add structures? It's pretty easy, click on the 'structures' tab of your IDA view and hit the 'Insert' key. Next click 'Add standard structure'. Then find the IRP structure (or _IRP).
Adding the IRP structure to IDA
Do the same for the IO_STACK_LOCATION structure and go back to the device control dispatch function at .text:10002fC0. Now we can change Irp+60h to [eax+IRP.Tail.Overlay.anonymous_1.anonymous_0] by selecting the 60h part and hitting 'T' and selecting the proper value. Not sure why it's that structure value, but whatever, that's what the dude from the forum said. Three lines below that you'll see a mov eax, [edx+0Ch]. This can be transformed to: IO_STACK_LOCATION.Parameters.DeviceIoControl.IoControlCode which looks a bit more reasonable to me. Here's what I've come up with for this segment of code after doing some initial analysis of it:
DeviceControlDispatchHandler with comments.
Of course, I've just started doing this reverse engineering so there are two things to keep in mind; I'm not done yet and I could totally be wrong. Trying to figure out what the Irp+60h value pointed to turned out to be challenging for me. I looked through the 'wdm.h' header file and found the _IRP definition but without looking up all it's members, I have no idea what the 0x60h offset points to. Again, the forum post I mention earlier helps clear that up, but I hadn't read it at that point. If you haven't already, I seriously suggest going back to read it.

*After* reading that post, I tried running the commands he mentioned to see what the structures look like from WinDBG, but it went horribly wrong. Mainly because I wasn't referencing the value correctly.
Attempting to display the IRP struct values using the wrong value.
Next I waited until ECX got set to the value of the IRP structure and tried again:
_IRP.Tail (0x40) + CurrentStackLocation (0x20) => 0x60 = IO_STACK_LOCATION (I think?)
So this looks 'better' but the 'memory read errors' throughout the structure references makes me think I might be wrong. OH WELL. I'm sure it'll all become clear the more I work with this stuff.

Of course, next up is reversing what the different IOCTLs actually do. That might take me some time, but I'll keep at it, don't you worry.

1 comment: