Windows Driver IOCTL Code Bluescreens / Crashes The Computer - c++

I have a device driver which I utilize to read other process virtual memory from kernel space so I do not have to use functions like ReadProcessMemory or WriteProcessMemory.
This works fine when I use a structure as a medium to pass the arguments to the kernel via DeviceIoControl, but the driver crashes my computer when I use plain variables like an unsigned long.
Here is an example of perfectly working code
(Driver):
#define IO_KERNEL_READ_REQUEST CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0701, METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
typedef struct _KERNEL_READ_REQUEST
{
ULONG ProcessId;
ULONG Address;
ULONG Response;
ULONG Size;
} KERNEL_READ_REQUEST, *PKERNEL_READ_REQUEST;
if (ControlCode == IO_KERNEL_READ_REQUEST)
{
PKERNEL_READ_REQUEST ReadInput = (PKERNEL_READ_REQUEST)Irp->AssociatedIrp.SystemBuffer;
PKERNEL_READ_REQUEST ReadOutput = (PKERNEL_READ_REQUEST)Irp->AssociatedIrp.SystemBuffer;
PEPROCESS Process;
PsLookupProcessByProcessId(ReadInput->ProcessId, &Process);
KeReadVirtualMemory(Process, ReadInput->Address, &ReadOutput->Response, ReadInput->Size);
DbgPrintEx(0, 0, "Read Params: %lu, %#010x \n", ReadInput->ProcessId, ReadInput->Address);
DbgPrintEx(0, 0, "Value: %lu \n", ReadOutput->Response);
status = STATUS_SUCCESS;
bytesIO = sizeof(KERNEL_READ_REQUEST);
}
(Program):
template <typename type>
type KernelRead(HANDLE hDriver, ULONG ProcessId, ULONG ReadAddress, SIZE_T ReadSize)
{
if (hDriver == INVALID_HANDLE_VALUE)
return (type)false;
DWORD Return;
DWORD Bytes;
KERNEL_READ_REQUEST ReadRequest;
ReadRequest.ProcessId = ProcessId;
ReadRequest.Address = ReadAddress;
ReadRequest.Size = ReadSize;
if (DeviceIoControl(hDriver, IO_KERNEL_READ_REQUEST, &ReadRequest, sizeof(ReadRequest),
&ReadRequest, sizeof(ReadRequest), &Bytes, NULL)) {
return (type)ReadRequest.Response;
}
else
return (type)false;
}
This is what causes the problem
#define IO_KERNEL_GET_ID CTL_CODE(FILE_DEVICE_UNKNOWN, 0x0703, METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
else if (ControlCode == IO_KERNEL_GET_ID)
{
// ProcessId is an ULONG initialized at the driver entry
PULONG OutPut = (PULONG)Irp->AssociatedIrp.SystemBuffer;
OutPut = &ProcessId;
DbgPrintEx(0, 0, "Kernel Get Id: %d \n", *OutPut);
status = STATUS_SUCCESS;
bytesIO = sizeof(OutPut);
}
DWORD KernelGetProcessId(HANDLE hDriver)
{
if (hDriver == INVALID_HANDLE_VALUE)
return false;
ULONG Id;
if (DeviceIoControl(hDriver, IO_KERNEL_GET_ID, &, sizeof(Id),
&Id, sizeof(Id), 0, NULL))
return Id;
else
return false;
}
Calling KernelGetProcessId crashes my driver and the whole computer, how can this be fixed? What am I doing wrong here?

Check DeviceIoControl. If lpOverlapped is NULL, lpBytesReturned cannot be NULL. Even when an operation returns no output data and lpOutBuffer is NULL, DeviceIoControl makes use of lpBytesReturned. After such an operation, the value of lpBytesReturned is meaningless.
Maybe it can be one of the reason.
Other issue which I an see is passing only &, you should pass ULONG variable in it.
Check some thing like this
DWORD KernelGetProcessId(HANDLE hDriver)
{
if (hDriver == INVALID_HANDLE_VALUE)
return false;
ULONG Id;
DWORD Bytes;
if (DeviceIoControl(hDriver, IO_KERNEL_GET_ID, &Id, sizeof(Id),
&Id, sizeof(Id), &Bytes, NULL))
return Id;
else
return false;
}

The Issue:
Apparently the IOCTLs work by using the stack of both the
driver & the user program. To my understanding the stack of the
function calling DeviceIoControl() is copied to kernel space
and then dissected using the arguments of DeviceIoControl()
to know which stack variables we are working with & the buffer is
finally set to Irp->AssociatedIrp.SystemBuffer.
After the IOCTL operation is finished on the kernel side,
IoCompleteRequest() is made which copies the stack of the
kernel module to userspace which then is again dissected to
the form we want it in.
(please correct me if I am wrong)
The Solution:
The crash is caused by this code in the kernel module:
PULONG OutPut = (PULONG)Irp->AssociatedIrp.SystemBuffer;
OutPut = &ProcessId;
in which the global variable's address is set as the value of the output data.
Now when the stack is copied the address obviously does not point anywhere since
the "ProcessId" variable resides in 64 bit kernel space. This is my understanding of the problem.
This fixes the problem:
PULONG OutPut = (PULONG)Irp->AssociatedIrp.SystemBuffer;
*OutPut = ProcessId;
DbgPrintEx(0, 0, "Kernel Get Id: %d \n", *OutPut);
status = STATUS_SUCCESS;
bytesIO = sizeof(OutPut);

Related

Irp information return PVOID

I'm writing Windows kernel driver in C++ and I have to return PVOID which has information about address in memory. Unfortunately, Irp->IoStatus.Information is only able to handle ULONG which results in shortened address for example: 0x2e341990000 is shortened to 0x41990000. It is very important to keep the address full otherwise user mode client would not be able to find address in memory. Is there any way to return full PVOID to client?
Driver code:
NTSTATUS status = STATUS_SUCCESS;
ULONG bytesIO = 0;
auto stack = IoGetCurrentIrpStackLocation(Irp);
switch (stack->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_SHELL:
{
auto len = stack->Parameters.DeviceIoControl.InputBufferLength;
if (len < sizeof(Data))
{
DbgPrint("[-] Received too small buffer\n");
status = STATUS_BUFFER_TOO_SMALL;
break;
}
auto data = (Data*)stack->Parameters.DeviceIoControl.Type3InputBuffer;
if (data == nullptr)
{
DbgPrint("[-] Received empty buffer\n");
status = STATUS_INVALID_PARAMETER;
break;
}
PVOID buf = SetMemoryAddress(data);
bytesIO = (ULONG)buf; // Buffer is shortened here
DbgBreakPoint();
break;
}
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = bytesIO;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
Client code:
HANDLE hDevice;
BOOL success;
hDevice = CreateFile(L"\\\\.\\Driver", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hDevice == INVALID_HANDLE_VALUE)
return FALSE;
Data data;
// Fill data structure here
PVOID retn;
PVOID buffer = { 0 };
success = DeviceIoControl(hDevice, IOCTL_SHELL, &data, sizeof(data), NULL, 0, (LPDWORD)&retn, NULL);
printf("0x%x\n", retn); // Shortened address
return success;
I tried using buffered IOCTL methods.
Your retn variable is not a memory address it is the number of bytes returned from the DeviceIoControl call. It is also of double word size (32bits) not equivalent with a void pointer on a modern 64bit machine.
The output data is written into the fifth argument which is optional and you seem to have provided NULL to.
You might want to initialize the value at retn so you can see if it is changed by your DeviceIoControl call, even if you provide nowhere to write output to.
DWORD bufffer_size_ouf = 0;
PVOID retn = (PVOID)&buff_size_out;

Writing to read only address with kernel driver c++

Im trying to write memory to a user mode process with kernel driver,
the current address im trying to write memory for is read only, I want to write 4 bytes to the current address,
the thing is if i change protection ( page ) of the process with VirtualProtectEx , it works and it writes the memory but this is only on user mode level, my intention is to change the protection of the process from kernel mode, i want to make it READWRITE, then change it back to READ from kernel space,
Now what I tried to do is giving me a BSOD ( blue screen of death ) with error : Kmode_exception_not_handld
I cant understand what in my code is triggering this BSOD my PC have very limited specs and i cant debug in VM to know..
I will write the code that works but in user mode , and what the code is not working for me in kernel space:
here the code that works:
void dispatch::handler(void* info_struct)
{
PINFO_STRUCT info = (PINFO_STRUCT)info_struct;
if (info->code == CODE_READ_MEMORY)
{
PEPROCESS target_process = NULL;
if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)info->process_id, &target_process)))
{
memory::read_memory(target_process, (void*)info->address, &info->buffer, info->size);
}
DbgPrintEx(0, 0, "[TEST]: Read Memory\n");
}
else if (info->code == CODE_WRITE_MEMORY)
{
PEPROCESS target_process = NULL;
if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)info->process_id, &target_process)))
{
memory::write_memory(target_process, &info->buffer, (void*)info->address, info->size);
}
DbgPrintEx(0, 0, "[TEST]: Write Memory\n");
}
}
NTSTATUS memory::write_memory(PEPROCESS target_process, void* source, void* target, size_t size)
{
if (!target_process) { return STATUS_INVALID_PARAMETER; }
size_t bytes = 0;
NTSTATUS status = MmCopyVirtualMemory(IoGetCurrentProcess(), source, target_process, target, size, KernelMode, &bytes);
if (!NT_SUCCESS(status) || !bytes)
{
return STATUS_INVALID_ADDRESS;
}
return status;
}
int main()
{
DWORD oldprt
ULONG writeTest1 = 3204497152;
VirtualProtectEx(ProcManager::hProcess, (PVOID)(testAddr), 4, PAGE_READWRITE, &oldprt);
driver_control::write_memory(process_id, testAddr, writeTest1);
VirtualProtectEx(ProcManager::hProcess, (PVOID)(testAddr), 4, PAGE_READONLY, &oldprt);
return 0;
}
Now what I want to do is stop using the VirtualProtectEx, and change the PAGE protection to READWRITE from kernel space, so what i did is add this in the dispatch::handler function:
else if (info->code == CODE_WRITE_MEMORY)
{
PEPROCESS target_process = NULL;
if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)info->process_id, &target_process)))
{
PMDL Mdl = IoAllocateMdl((void*)info->address, info->size, FALSE, FALSE, NULL);
if (!Mdl)
return false;
// Locking and mapping memory with RW-rights:
MmProbeAndLockPages(Mdl, KernelMode, IoReadAccess);
PVOID Mapping = MmMapLockedPagesSpecifyCache(Mdl, KernelMode, MmNonCached, NULL, FALSE, NormalPagePriority);
MmProtectMdlSystemAddress(Mdl, PAGE_READWRITE);
memory::write_memory(target_process, &info->buffer, (void*)info->address, info->size);
// Resources freeing:
MmUnmapLockedPages(Mapping, Mdl);
MmUnlockPages(Mdl);
IoFreeMdl(Mdl);
}
DbgPrintEx(0, 0, "[TEST]: Write Read Only Memory\n");
}
So this what I've added caused the BSOD, but why I cannot understand, what am i doing wrong here?
here is the info struct if needed to understand more, :
#define CODE_READ_MEMORY 0x1
#define CODE_WRITE_MEMORY 0x2
typedef struct _INFO_STRUCT
{
ULONG code;
ULONG process_id;
ULONG address;
ULONG buffer;
ULONG size;
}INFO_STRUCT, * PINFO_STRUCT;
any suggestions on solving this problem?

CreateRemoteThread MessageBoxA causes remote process to crash

I made a simple program that calculates the RVA of the function MessaegBoxA in user32.dll and then adds that offset to the base loading address of the dll in the remote process's memory to get the address of the function MessageBoxA. I made a dummy program that outputs the address of the function in its memory using GetProcAddress and then implement my own function in my program to display the address it calculated for the same function in the remote process. They always match so I'm certain my function for finding the address of MessageBoxA in remote process's is not the problem.
I made a struct that contains all the necessary information and parameters needed for ThreadProc to execute MessageBoxA in the remote process once I load it using WriteProcessMemory.
typedef struct
{
typedef int (WINAPI* _MessageBoxA)(HWND, LPCSTR, LPCSTR, UINT);
_MessageBoxA MessageBoxA;
//These are the four parameters passed to MessageBoxA
HWND hwnd;
LPCSTR msg;
LPCSTR caption;
UINT mb;
}MB_DATA, *PMB_DATA;
When I try this on my own dummy program, the message box shows up but with weird text contrary to the strings I specified in the msg and caption members of MB_DATA. It says the following for the caption asic_string::erase and for the message it says u). And when I try to do this in any other process than my dummy process it crashes the remote process. I made a function to iterate through the modules that have been loaded in the process with tlhelp32 functions to make sure user32.dll is present and it is and my function for finding the address of the function in the process doesn't return NULL like it would if the dll were not present.
Here all relevant functions and my main function:
dependencies.hpp
#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <iostream>
using namespace std;
typedef struct
{
typedef int (WINAPI* _MessageBoxA)(HWND, LPCSTR, LPCSTR, UINT);
_MessageBoxA MessageBoxA;
HWND hwnd;
LPCSTR msg;
LPCSTR caption;
UINT mb;
}MB_DATA, *PMB_DATA;
//Map the dll into memory
void* GetFileImage(char path[])
{
HANDLE hFile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
if(hFile == INVALID_HANDLE_VALUE){printf("Error getting file handle: %d", (int)GetLastError());return NULL;}
HANDLE file_map = CreateFileMapping(hFile, NULL, PAGE_READONLY|SEC_IMAGE, 0, 0, "KernelMap");
if(file_map == INVALID_HANDLE_VALUE){printf("Error mapping file: %d", (int)GetLastError());return NULL;}
LPVOID file_image = MapViewOfFile(file_map, FILE_MAP_READ, 0, 0, 0);
if(file_image == 0){printf("Error getting mapped view: %d", (int)GetLastError());return NULL;}
return file_image;
}
//Get to the function export directory and find the offset for the specified function from the
//address in memory the dll was loaded at
DWORD_PTR RVAddress(char* image, const char* proc_name)
{
PIMAGE_DOS_HEADER pDos_hdr = (PIMAGE_DOS_HEADER)image;
PIMAGE_NT_HEADERS pNt_hdr = (PIMAGE_NT_HEADERS)(image+pDos_hdr->e_lfanew);
IMAGE_OPTIONAL_HEADER opt_hdr = pNt_hdr->OptionalHeader;
IMAGE_DATA_DIRECTORY exp_entry = opt_hdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
PIMAGE_EXPORT_DIRECTORY pExp_dir = (PIMAGE_EXPORT_DIRECTORY)(image+exp_entry.VirtualAddress);
DWORD* func_table = (DWORD*)(image+pExp_dir->AddressOfFunctions);
WORD* ord_table = (WORD*)(image+pExp_dir->AddressOfNameOrdinals);
DWORD* name_table = (DWORD*)(image+pExp_dir->AddressOfNames);
for(u_int i=0;i<pExp_dir->NumberOfNames;i++)
{
char* name = (char*)(image+name_table[i]);
if(strcmp(proc_name, name) == 0)
{
return (DWORD_PTR)func_table[ord_table[i]];
}
}
return (DWORD_PTR)0;
}
//Add the RVA returned from RVAddress to the address of the dll to find the function in the
//process memory
LPVOID GetProcAddressEx(DWORD dwPid, char* mod_path, char* function_name, char* mod_name)
{
HANDLE hSnapshot = INVALID_HANDLE_VALUE;
MODULEENTRY32 me32;
me32.dwSize = sizeof(MODULEENTRY32);
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE|TH32CS_SNAPMODULE32, dwPid);
if(hSnapshot == INVALID_HANDLE_VALUE){printf("Snapshot failed");return 0;}
if(!(Module32First(hSnapshot, &me32)))
{
printf("Mod32First failed");
return 0;
}
BOOL found = FALSE;
while(Module32Next(hSnapshot, &me32))
{
if(stricmp(me32.szModule, mod_name) == 0)
{
CloseHandle(hSnapshot);
found = TRUE;
break;
}
}
if(found == FALSE){return 0;}
DWORD_PTR RVA = (DWORD_PTR)RVAddress((char*)GetFileImage(mod_path), function_name);
LPVOID func_addr = me32.modBaseAddr+RVA;
return func_addr;
}
main.cpp
#include "dependencies.hpp"
#define FUNC_SIZE 1024
typedef int (WINAPI* _MessageBoxA)(HWND, LPCSTR, LPCSTR, UINT);
int main()
{
MB_DATA mb_data;
mb_data.hwnd = NULL;
mb_data.msg = "Hey";
mb_data.caption = "Yo";
mb_data.mb = MB_OK;
SIZE_T nBytes = 0;
char proc_path[MAX_PATH];
char kernel_path[MAX_PATH];
char user32_path[MAX_PATH];
//get full path to the current process and store it in proc_path
GetModuleFileName(GetModuleHandle(NULL), proc_path, MAX_PATH);
//get full path to kernel32.dll and store it in kernel_path
GetModuleFileName(GetModuleHandle("kernel32.dll"), kernel_path, MAX_PATH);
//get full path to user3.dll and store it in user32_path
GetModuleFileName(GetModuleHandle("user32.dll"), user32_path, MAX_PATH);
//show all processes running and their PID's
system("tasklist");
DWORD dwPid = 0;
printf("PID: ");
scanf("%lu", &dwPid);
//if dwPid is 0 assign it the pid of the current process
if(dwPid == 0)
{
dwPid = GetCurrentProcessId();
}
//Get a handle to the process with all access rights
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
//make sure the handle is valid
if(hProc == NULL){printf("Error obtaining handle to process: %lu", GetLastError());return 1;}
//Get the address of the function in the remote process
LPVOID _MessageBoxA1 = GetProcAddressEx(dwPid, user32_path, (char*)"MessageBoxA", (char*)"user32.dll");
//assign the pointer to the address to the member MessageBoxA of the MB_DATA structure
mb_data.MessageBoxA = (_MessageBoxA)_MessageBoxA1;
//allocate 2mb for our the ThreadProc callback function and the MB_DATA structure
LPVOID lpBase = VirtualAllocEx(hProc, NULL, 2048, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
//did the allocation work
if(lpBase == NULL){printf("Error allocating space: %lu", GetLastError());return 1;}
//so I can check what was written with CheatEngine
cout << "Base address of memory allocated in remote process: " << lpBase << endl;
//Write the function into memory
if(WriteProcessMemory(hProc, lpBase, (LPVOID)ThreadProc, FUNC_SIZE, &nBytes) == 0)
{
printf("Error writing function to process");
return 1;
}
//the address the space left after having written ThreadProc into memory
LPVOID lpBuffer = lpBase+FUNC_SIZE;
//Write the MB_DATA structure into the memory of the remote process
if(WriteProcessMemory(hProc, lpBuffer, &mb_data, sizeof(MB_DATA), &nBytes) == 0)
{
printf("Error writing buffer to process");
}
//Run the ThreadProc function passing the MB_DATA structure to it as its lpParam parameter
if(CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)lpBase, lpBuffer, 0, NULL) == NULL)
{
printf("Error creating remote thread: %lu", GetLastError());
return 1;
}
//print a list of all the dll's being used by the process
EnumerateModules(dwPid);
system("pause");
return 0;
}
Any help would be greatly appreciated. Thank you very much! :)
mb_data.msg and mb_data.caption point to what in the another process ?? this is already enough for crash error. what is in ThreadProc not visible, but i not sure that it have no relocs. really ThreadProc must be member function of MB_DATA and access only it members. from you post obviously that you not debug remote process at injection time. also obviously that task is over your current level

Obtain Device Information Set for Monitors: Returned Handle is always INVALID_HANDLE_VALUE

I am attempting to list the device information for all the monitors currently connected to the computer. I have a function that can do this and its 90% done except when I go to call the function SetupDiGetClassDevs() with the 2nd parameter set(not NULL) then the function always fails(returns INVALID_HANDLE_VALUE).
When I call GetLastError() I get the error 13(decimal), ie, "The data is invalid" which I am not sure what that means?
What is going wrong? Can you provide any advice on whats happening and how I can fix it?
Function Information:
HDEVINFO SetupDiGetClassDevs(
_In_opt_ const GUID *ClassGuid,
_In_opt_ PCTSTR Enumerator, // According to MSDN this param MUST be set if I want Device Information for a specific class(Monitors)
_In_opt_ HWND hwndParent,
_In_ DWORD Flags
);
My function that attempts to get a Device Information Set for Monitors only and output each monitors details(the error line is commented):
void printDeviceData(GUID guID)
{
// Device Classes: http://msdn.microsoft.com/en-us/library/windows/hardware/ff553426
// System Device Classes: http://msdn.microsoft.com/en-us/library/windows/hardware/ff553428
// Monitor Class GUI: {4d36e96e-e325-11ce-bfc1-08002be10318}
DWORD dataT = 0;
PCTSTR monitorGuID = _T("");
SP_DEVINFO_DATA deviceInfoData = {0};
deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
deviceInfoData.ClassGuid = guID;
// Step 1: Get Device Information Set for Monitors only
// ERROR OCCURS HERE: SetupDiGetClassDevs() always fails
// Also tried these values for param 2: "Monitor" "PCI" but all cause the function to return INVALID_HANDLE_VALUE
HDEVINFO hDevInfo = SetupDiGetClassDevs(&guID, _T("{4d36e96e-e325-11ce-bfc1-08002be10318}"), NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDevInfo == INVALID_HANDLE_VALUE) {
//outputLastError(_T("Fail 1"));
printf("hDevInfo == INVALID_HANDLE_VALUE\n");
return;
}
else printf("SUCCESS 1\n");
if (SetupDiGetSelectedDevice(hDevInfo, &deviceInfoData) == FALSE) {
//outputLastError(_T("SetupDiGetSelectedDevice(hDevInfo, &deviceInfoData) == FALSE"));
printf("SetupDiGetSelectedDevice(hDevInfo, &deviceInfoData) == FALSE, %d, %x\n", GetLastError(), GetLastError());
return;
}
else printf("SUCCESS 2\n");
// Step 2: For each Monitor: Output Device information
const unsigned int FLAG_NUM = 30;
DWORD flags[] = {SPDRP_FRIENDLYNAME, SPDRP_ENUMERATOR_NAME, SPDRP_PHYSICAL_DEVICE_OBJECT_NAME, SPDRP_DEVICEDESC,
SPDRP_ADDRESS, SPDRP_BUSNUMBER, SPDRP_BUSTYPEGUID, SPDRP_CHARACTERISTICS, SPDRP_CLASS, SPDRP_CLASSGUID,
SPDRP_COMPATIBLEIDS, SPDRP_CONFIGFLAGS, SPDRP_DEVICE_POWER_DATA, SPDRP_DEVTYPE, SPDRP_DRIVER,
SPDRP_ENUMERATOR_NAME, SPDRP_EXCLUSIVE, SPDRP_HARDWAREID, SPDRP_INSTALL_STATE, SPDRP_LEGACYBUSTYPE,
SPDRP_LOCATION_INFORMATION, SPDRP_LOCATION_PATHS, SPDRP_LOWERFILTERS, SPDRP_MFG,
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME, SPDRP_UI_NUMBER, SPDRP_UI_NUMBER_DESC_FORMAT, SPDRP_UPPERFILTERS,
SPDRP_SECURITY_SDS, SPDRP_SECURITY, SPDRP_SERVICE };
for (int i=0; i<=FLAG_NUM; i++) {
DWORD buffersize = 0;
LPTSTR buffer = NULL;
while (!SetupDiGetDeviceRegistryProperty(hDevInfo, &deviceInfoData, flags[i], &dataT,
(PBYTE)buffer, buffersize, &buffersize))
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
// Change the buffer size.
if (buffer)
LocalFree(buffer);
buffer = (LPTSTR)LocalAlloc(LPTR, buffersize);
}
else {
// Insert error handling here.
break;
}
}
printf("Data: %d: %s\n", i, buffer);
if (buffer)
LocalFree(buffer);
}
SetupDiDestroyDeviceInfoList(hDevInfo);
}
According to the documentation, Enumerator must be set to a valid device Instance ID which according to http://msdn.microsoft.com/en-us/library/windows/hardware/ff541327 has to be specified like this
"PCI\VEN_1000&DEV_0001&SUBSYS_00000000&REV_02\1&08"
I haven't tested it, but I'd assume that's where the invalid data come from.

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

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);
As you see str.Buffer is empty or filled with garbage. Perhaps a buffer overflow while filling the str via ZwQueryInformationProcess() triggers the BSOD.
Any help would be appreciated.
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.
//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.
ZwQueryInformationProcess needs a HANDLE, not a PROCESS!
You need to use ObOpenObjectByPointer to get the handle first.