Kayaker
November 13th, 2008, 19:51
I've been noticing a peculiar behaviour in that the value of the PE->OptionalHeader.ImageBase in some loaded system drivers has been modified from the default 0x10000 value as found in the raw file.
Now we know that all driver files generally have the same PE image base (0x10000) and are relocated into memory to whatever address the OS chooses.
That PE file image base value can probably be changed when compiling it with the /BASE directive, i.e. /BASE:0X20000, but it doesn't really matter since its going to be dynamically relocated to high memory anyway.
The loaded Image Base value can be obtained from the DriverObject structure DRIVER_OBJECT->DriverStart, but we don't really care about that either.
What is peculiar though is if you parse through the PE header in memory of some loaded drivers, the ImageBase value will have changed to some non-intuitive address.
You can see this with WinDbg/LiveKd or using the simple code:
Let's check this in LiveKD for a driver which doesn't have a modified PE ImageBase in memory - Beep.sys. Other examples are cdrom.sys, flpydisk.sys, i8042prt.sys, etc.
And now for a driver in which the PE ImageBase has been modified. There are several system drivers to choose from - acpi, agp440, atapi, IntelIde, KSecDD, PCI, VolSnap, Livekd, ...
There doesn't seem to be any rhyme or reason to the modified PE image base address. The addresses don't point to anything in particular, just random bits of memory from what I can see.
In some cases the imagebase will be missing entirely from the Driver Object structure and LiveKd may (win32k.sys), or may not (PnPManager.sys) get any information from the LM and !DH commands.
There's probably nothing more mysterious than the operation of the PE loader. Is there some common factor in these drivers that cause the loader (presumably) to modify the PE header ImageBase value, while in other cases not? A compiler option? Something related to the drivers purpose or operation?
I certainly don't have an explanation for it, so I guess that ends my post
Kayaker
Now we know that all driver files generally have the same PE image base (0x10000) and are relocated into memory to whatever address the OS chooses.
That PE file image base value can probably be changed when compiling it with the /BASE directive, i.e. /BASE:0X20000, but it doesn't really matter since its going to be dynamically relocated to high memory anyway.
The loaded Image Base value can be obtained from the DriverObject structure DRIVER_OBJECT->DriverStart, but we don't really care about that either.
What is peculiar though is if you parse through the PE header in memory of some loaded drivers, the ImageBase value will have changed to some non-intuitive address.
You can see this with WinDbg/LiveKd or using the simple code:
Code:
// RtlImageNtHeader returns offset of "PE" in PE header
EXTERN_C
NTKERNELAPI
PIMAGE_NT_HEADERS
RtlImageNtHeader (
IN PVOID Base
);
PIMAGE_NT_HEADERS NtHeaders; // "PE"
///////////////////////////////////////////////////
// Get value of ImageBase from PE header in memory
///////////////////////////////////////////////////
// Base Address in DRIVER_OBJECT may be missing
// in some drivers, so don't pass a null pointer!
if(TargetpDriverObject->DriverStart)
{
// Get offset of "PE" in PE header
NtHeaders = (PIMAGE_NT_HEADERS)RtlImageNtHeader(
(PVOID)TargetpDriverObject->DriverStart);
if(NtHeaders)
{
PE_ImageBase = NtHeaders->OptionalHeader.ImageBase;
}
}
Let's check this in LiveKD for a driver which doesn't have a modified PE ImageBase in memory - Beep.sys. Other examples are cdrom.sys, flpydisk.sys, i8042prt.sys, etc.
Code:
Beep.sys
Loaded ImageBase = F8C2B000
ImageBase in loaded PE header = 0x10000
(List Loaded Modules, m = pattern parameter)
0: kd> lm mBeep
start end module name
f8c2b000 f8c2c080 Beep
(!Display Headers)
0: kd> !dh f8c2b000
File Type: EXECUTABLE IMAGE
OPTIONAL HEADER VALUES
----- new -----
00010000 image base
And now for a driver in which the PE ImageBase has been modified. There are several system drivers to choose from - acpi, agp440, atapi, IntelIde, KSecDD, PCI, VolSnap, Livekd, ...
Code:
acpi.sys
Loaded ImageBase = F86A8000
ImageBase in loaded PE header = 0x80124000
0: kd> lm mACPI
start end module name
f86a8000 f86d5d80 ACPI
0: kd> !dh f86a8000
File Type: EXECUTABLE IMAGE
OPTIONAL HEADER VALUES
----- new -----
80124000 image base
Code:
ksecdd.sys
Loaded ImageBase = F8569000
ImageBase in loaded PE header = 0x802AC000
0: kd> lm mksecdd
start end module name
f8569000 f857f780 KSecDD
0: kd> !dh f8569000
File Type: DLL
OPTIONAL HEADER VALUES
----- new -----
802ac000 image base
Code:
agp440.sys
Loaded ImageBase = F8757000
ImageBase in loaded PE header = 0x803DA000
0: kd> lm magp440
start end module name
f8757000 f8761580 agp440
0: kd> !dh f8757000
File Type: EXECUTABLE IMAGE
OPTIONAL HEADER VALUES
----- new -----
803da000 image base
There doesn't seem to be any rhyme or reason to the modified PE image base address. The addresses don't point to anything in particular, just random bits of memory from what I can see.
In some cases the imagebase will be missing entirely from the Driver Object structure and LiveKd may (win32k.sys), or may not (PnPManager.sys) get any information from the LM and !DH commands.
There's probably nothing more mysterious than the operation of the PE loader. Is there some common factor in these drivers that cause the loader (presumably) to modify the PE header ImageBase value, while in other cases not? A compiler option? Something related to the drivers purpose or operation?
I certainly don't have an explanation for it, so I guess that ends my post

Kayaker