Drivers, pointers, and x86-64

I'm working on a small driver that does a little magic on kernel space. I originally wrote it for XP x86, but now I'm testing on W7 x86-64.
Anyway, I communicate with the driver by opening "\\.\driver" and passing a pointer to a structure through WriteFile(). Basically WriteFile(file,&pointer,sizeof(&pointer),/*...*/). The driver verifies that the buffer passed is at least as big as sizeof(structure *).
Here's the thing: the driver is obviously build for x86-64, but the client application is built for x86. When I call WriteFile() like above, the function fails because sizeof(&pointer) is determined at compile time for x86, which differs from 64-bit pointers, but if I multiply the pointer size by 2, the function succeeds and the structure is modified correctly! The structure looks something like this:
1
2
3
4
5
struct structure{
    char operation;
    ulong parameter[4];
    char string[16];
};
structure::string is what's supposed to be modified by the call. How exactly is this working when the driver and the client are using different offsets to access the structure, and why does the driver not crash when it tries to read four bytes past the end of the pointer? Does Windows perform some kind of code modification on user space code?

PS: Don't bother pointing out the problems with reading memory from user pointers. The IO mode I'm using allows the driver read user memory.

From what you said, it's just the pointer size that is different and a 64bit pointer can address 32bits.

As long as sizeof(char), sizeof(ulong) and sizeof(structure) are the same in x86 and x86-64 I wouldn't anticipate a problem, since the offset calculations and padding would be the same.

Hope that's food for thought.
Last edited on
...
I don't know why I didn't think of that.

Still, that doesn't explain why the driver can read four bytes past the end of the pointer. The client should only allocate four bytes for the temporary pointer, the rest would most likely be garbage.
You mean is it thunking...it's possible since Windows knows it's a 32bit app. You'd have to look at the assembly.

This might be a way to figure out if you are getting lucky with the pointer.

Intel is small endian, so I guess the trailing four bytes are 0 after the pointer itself. Maybe you can print this out or deliberately manipulate it to see what happens.

Or maybe you should just allocate storage following the pointer and ensure it is zero'd out.
Last edited on
As matter of fact, I did try copying the pointer to a char[8] (while clearing the upper half). It BSODed.
Interesting that it BSODed...I seem to remember Intel byte order is not strictly lowest bytes first. The bytes themselves are swapped within each word. Probably straightforward to write a quick program to verify this.

Having said that, I'd probably change the driver to dump out the address it got passed.

Then you could play around a bit to figure out what is happening.

Probably do some struct like this:

struct {
32bit_ptr_type; // assign the 32bit ptr here
32_bit_ptr_type; // assign 0xdeadbeef here for easy reference
};

Guess, you can figure out what I am getting at here.
Here's another thought. I'm not that familar with Windows drivers, so I'm guessing here that WriteFile is a standard function call. Since Windows knows it's 32bit binary, maybe it is dynamically linking with WriteFile32 which is doing the address translation and calling WriteFile64 on your behalf. Though I'm bemused as to why it needs sizeof(ptr) = 8 instead of 4, since it could easily allocate this on the stack.
As far as I know, the CPU is strictly little endian.

Well, it doesn't matter, anyway. The main procedure crashes no matter what. The driver relies on hardcoded offsets to access _EPROCESS instances. Highly dangerous, I know, but that's the whole point of the driver. I keep trying to read or write (not sure which) to nowhere. My best guess is that something's going wrong with pointer arithmetic (which shouldn't be because I'm casting all pointers to 32-bit integers); the offsets are wrong, although every source cites the same offsets; or something entirely different involving a strange combination of improper casts, truncated values, and reading past the end of integers.

EDIT: Oh, you mean something like this?
1
2
3
BOOL WriteFile32(/*...*/,Prt32 buffer,DWORD size,/*...*/){
	WriteFile64(/*...*/,Ptr64(buffer),size,/*...*/);
}
That makes sense. It also explains why I have to multiply the size by two.
Last edited on
Topic archived. No new replies allowed.