How to use ZwQueryInformationProcess to get ProcessImageFileName in a kernel driver?

fardjad picture fardjad · Sep 14, 2010 · Viewed 14k times · Source

I'm writing a simple kernel driver for my application (think of a very simple anti-malware application.)

I've hooked ZwOpenFile() and used PsGetCurrentProcess() to get a handle to the caller process.

It returns a PEPROCESS structure:

PEPROCESS proc = PsGetCurrentProcess();

I'm using ZwQueryInformationProcess() to get the PID and ImageFileName:

DbgPrint("ZwOpenFile Called...\n");
DbgPrint("PID: %d\n", PsGetProcessId(proc));
DbgPrint("ImageFileName: %.16s\n", PsGetProcessImageFileName(proc));

and trying to get the process FullPath this way (but I get BSOD):

WCHAR strBuffer[260];
UNICODE_STRING str;

//initialize
str.Buffer = strBuffer;
str.Length = 0x0;
str.MaximumLength = sizeof(strBuffer);

//note that the seconds arg (27) is ProcessImageFileName
ZwQueryInformationProcess(proc, 27, &str, sizeof(str), NULL);

DbgPrint("FullPath: %wZ\n", str.Buffer);

DbgView Output

As you see str.Buffer is empty or filled with garbage. Perhaps a buffer overflow while filling the str via ZwQueryInformationProcess() triggers the BSOD.

alt text

Any help would be appreciated.

Answer

Steve Townsend picture Steve Townsend · Sep 14, 2010

The MSDN docs for this API indicate that

When the ProcessInformationClass parameter is ProcessImageFileName, the buffer pointed to by the ProcessInformation parameter should be large enough to hold a UNICODE_STRING structure as well as the string itself. The string stored in the Buffer member is the name of the image file.file.

With this in mind, I suggest you try modifying your buffer structure like this:

WCHAR strBuffer[(sizeof(UNICODE_STRING) / sizeof(WCHAR)) + 260];
UNICODE_STRING str;
str = (UNICODE_STRING*)&strBuffer;

//initialize
str.Buffer = &strBuffer[sizeof(UNICODE_STRING) / sizeof(WCHAR)];
str.Length = 0x0;
str.MaximumLength = 260 * sizeof(WCHAR);

//note that the seconds arg (27) is ProcessImageFileName
ZwQueryInformationProcess(proc, 27, &strBuffer, sizeof(strBuffer), NULL);

Additionally, your code needs to check and handle the error case described in the docs here. This may be why you missed the BSOD trigger case.

If the buffer is too small, the function fails with the STATUS_INFO_LENGTH_MISMATCH error code and the ReturnLength parameter is set to the required buffer size.