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

15,267

Solution 1

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.

Solution 2

//Declare this piece of code in header file if available otherwise before the Function definition..

typedef NTSTATUS (*QUERY_INFO_PROCESS) (
__in HANDLE ProcessHandle,
__in PROCESSINFOCLASS ProcessInformationClass,
__out_bcount(ProcessInformationLength) PVOID ProcessInformation,
__in ULONG ProcessInformationLength,
__out_opt PULONG ReturnLength
);

QUERY_INFO_PROCESS ZwQueryInformationProcess;

//Function Definition

NTSTATUS GetProcessImageName(HANDLE processId, PUNICODE_STRING ProcessImageName)
{
NTSTATUS status;
ULONG returnedLength;
ULONG bufferLength;
HANDLE hProcess;
PVOID buffer;
PEPROCESS eProcess;
PUNICODE_STRING imageName;

PAGED_CODE(); // this eliminates the possibility of the IDLE Thread/Process

status = PsLookupProcessByProcessId(processId, &eProcess);

if(NT_SUCCESS(status))
{
    status = ObOpenObjectByPointer(eProcess,0, NULL, 0,0,KernelMode,&hProcess);
    if(NT_SUCCESS(status))
    {
    } else {
        DbgPrint("ObOpenObjectByPointer Failed: %08x\n", status);
    }
    ObDereferenceObject(eProcess);
} else {
    DbgPrint("PsLookupProcessByProcessId Failed: %08x\n", status);
}


if (NULL == ZwQueryInformationProcess) {

    UNICODE_STRING routineName;

    RtlInitUnicodeString(&routineName, L"ZwQueryInformationProcess");

    ZwQueryInformationProcess =
           (QUERY_INFO_PROCESS) MmGetSystemRoutineAddress(&routineName);

    if (NULL == ZwQueryInformationProcess) {
        DbgPrint("Cannot resolve ZwQueryInformationProcess\n");
    }
}

/* Query the actual size of the process path */
status = ZwQueryInformationProcess( hProcess,
                                    ProcessImageFileName,
                                    NULL, // buffer
                                    0, // buffer size
                                    &returnedLength);

if (STATUS_INFO_LENGTH_MISMATCH != status) {
    return status;
}

/* Check there is enough space to store the actual process
   path when it is found. If not return an error with the
   required size */
bufferLength = returnedLength - sizeof(UNICODE_STRING);
if (ProcessImageName->MaximumLength < bufferLength)
{
    ProcessImageName->MaximumLength = (USHORT) bufferLength;
    return STATUS_BUFFER_OVERFLOW;   
}

/* Allocate a temporary buffer to store the path name */
buffer = ExAllocatePoolWithTag(NonPagedPool, returnedLength, 'uLT1');

if (NULL == buffer) 
{
    return STATUS_INSUFFICIENT_RESOURCES;   
}

/* Retrieve the process path from the handle to the process */
status = ZwQueryInformationProcess( hProcess,
                                    ProcessImageFileName,
                                    buffer,
                                    returnedLength,
                                    &returnedLength);

if (NT_SUCCESS(status)) 
{
    /* Copy the path name */
    imageName = (PUNICODE_STRING) buffer;
    RtlCopyUnicodeString(ProcessImageName, imageName);
}

/* Free the temp buffer which stored the path */
ExFreePoolWithTag(buffer, 'uLT1');

return status;
}

//Function Call.. Write this piece of code in PreOperation Call back and IRQ should be PASSIVE_LEVEL

PEPROCESS objCurProcess=NULL;
HANDLE hProcess;
UNICODE_STRING fullPath;

objCurProcess=IoThreadToProcess(Data->Thread);//Note: Date is type of FLT_CALLBACK_DATA which is in PreOperation Callback as argument

hProcess=PsGetProcessID(objCurProcess);

fullPath.Length=0;
fullPath.MaximumLength=520;
fullPath.Buffer=(PWSTR)ExAllocatePoolWithTag(NonPagedPool,520,'uUT1');

GetProcessImageName(hProcess,&fullPath);

in fullPath variable there is Full Path of Process .. Like if the process is explorer.exe then path will be look like this:-

\Device\HarddiskVolume3\Windows\explorer.exe

Note:- \Device\HarddiskVolume3 Path might be changed due to Machine and different volume in hard disk this is an example in my case.

Share:
15,267
fardjad
Author by

fardjad

Updated on July 20, 2022

Comments

  • fardjad
    fardjad almost 2 years

    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.

  • Konrad
    Konrad over 4 years
    where is UNICODE_STRING str; used here?
  • Steve Townsend
    Steve Townsend over 4 years
    @Konrad it's used implicitly thru strBuffer