libcurl crashes in a Windows service - c++

My service receives data from some processes, parses it, and sends an HTTP post to my PHP server.
When I started writing the code, it was an ordinary 64-bit program. After I finished, I converted it to a service, but some crashes happen when the service tries to send the data.
The reason isn't clear, as I use libcurl in other places in the service without problems.
My receiver is something like this:
while (true)
{
memset(pipe_buffer, 0, 10000);
cres = ReadFile(pipe, pipe_buffer, 10000, &read, 0);
ofile << "[*] got a packet with length : " << read << endl;
if (read > 0 && cres) {
ofile << "[*] " << pipe_buffer << endl;
// send the request
string payload;
payload += "data=";
payload += pipe_buffer;
ofile << "[*] sending post : " << url << "?" << payload<< endl;
CURL *curl;
curl = curl_easy_init();
if (!curl) {
ofile << "[!] curl failed to init" << endl;
return 0;
}
curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // crashes start here
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload.c_str());
curl_easy_perform(curl);
curl_easy_cleanup(curl);
}
else {
// the client may dissconnect , wait for it to connect again
DisconnectNamedPipe(pipe); ConnectNamedPipe(pipe, 0);
}
}
I'm getting very different and strange errors every time.
Most of them come from RtlFreeHeap() that libcurl calls, and integer divided by zero from some WSA functions that curl_easy_perform() uses.
The crash may occur in any of the libcurl functions, starting from curl_easy_setopt().
The same code works without problems in an ordinary program.
EDIT : after digging this is the function causes corruption
the reason why the crash didn't happen for previous curl usage is that I don't use this function except after this , then I create a receiver thread that works in parallel with a thread that uses this function , also the ordinary program didn't crash as this function was only for the service (a program not running in local system can use EnumWindows)
I think Poco net didn't crash as it's based on c++ not c and uses new/delete to allocate and free the memory but the crash coming from curl and other functions start from _malloc_base and similar c allocation functions
the function code :
wstring GetCommandLineRemote(DWORD id) {
PROCESS_BASIC_INFORMATION pbInfo = { 0 };
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, 0, id);
if (!hProc || hProc == INVALID_HANDLE_VALUE) return wstring(L"");
auto status = fNtQueryInformationProcess(hProc, ProcessBasicInformation, &pbInfo, sizeof(pbInfo), NULL);
if (!NT_SUCCESS(status)) { CloseHandle(hProc); return wstring(L""); }
BPEB bbeb = { 0 };
BOOL result;
result = ReadProcessMemory(hProc, (void*)pbInfo.PebBaseAddress, &bbeb, sizeof(BPEB), 0);
if (!result) { CloseHandle(hProc); return wstring(L""); }
BRTL_USER_PROCESS_PARAMETERS parameters = { 0 };
result = ReadProcessMemory(hProc, (void*)((uintptr_t)bbeb.ProcessParameters), &parameters, sizeof(BRTL_USER_PROCESS_PARAMETERS), 0);
if (!result) { CloseHandle(hProc); return wstring(L""); }
UNICODE_STRING CommandLine = { 0 };
CommandLine.Length = parameters.CommandLine.Length;
CommandLine.MaximumLength = parameters.CommandLine.MaximumLength;
CommandLine.Buffer = new WCHAR[CommandLine.MaximumLength];
result = ReadProcessMemory(hProc, (void*)parameters.CommandLine.Buffer, CommandLine.Buffer, parameters.MaximumLength, 0);
if (!result) { CloseHandle(hProc); return wstring(L""); }
CloseHandle(hProc);
wstring wCommandLine = CommandLine.Buffer;
delete CommandLine.Buffer;
return wCommandLine;
}
I'm using this function to distinguish the instances of my helper process by the command line that the process started with
so the mechanism to find the instance looks like this :
vector<DWORD> enum_ids(wstring proc_name) {
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
vector<DWORD> ids;
PROCESSENTRY32W entry = { 0 };
entry.dwSize = sizeof(entry);
if (!Process32FirstW(snap, &entry)) return ids;
do {
wstring p_name = entry.szExeFile;
auto check_pos = p_name.find(proc_name);
if (check_pos != wstring::npos) {
ofile << "[*] found process instance with id : " << entry.th32ProcessID << endl;
ids.push_back(entry.th32ProcessID);
}
} while (Process32NextW(snap, &entry));
return ids;
}
DWORD find_process(wstring proc_name,wstring unique) {
DWORD process_id = 0;
auto ids = enum_ids(proc_name);
for (auto id : ids) {
wstring wCommandLine = GetCommandLineRemote(id);
auto check_pos = wCommandLine.find(unique);
if (check_pos != wstring::npos) {
process_id = id;
break;
}
//if (id == 83004) { process_id = id; break; } 83004 is example , if I used this instead of the above comparison code no errors occur so I assumed the errors come from GetCommandLineRemote
}
return process_id;
}
then in the service main thread :
CreateThread(0, 0, recieve, 0, 0, 0);
CreateThread(0, 0, FindParticularInstance, (char*)"ch", 0, 0);
now after finding the errors source , how this function do all these errors and how to prevent it from this ?
the definitions of the structures (from NiroSoft and process hacker) :
typedef struct _CURDIR
{
UNICODE_STRING DosPath;
PVOID Handle;
} CURDIR, *PCURDIR;
typedef struct _RTL_DRIVE_LETTER_CURDIR
{
WORD Flags;
WORD Length;
ULONG TimeStamp;
STRING DosPath;
} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR;
typedef struct _BRTL_USER_PROCESS_PARAMETERS
{
ULONG MaximumLength;
ULONG Length;
ULONG Flags;
ULONG DebugFlags;
PVOID ConsoleHandle;
ULONG ConsoleFlags;
PVOID StandardInput;
PVOID StandardOutput;
PVOID StandardError;
CURDIR CurrentDirectory;
UNICODE_STRING DllPath;
UNICODE_STRING ImagePathName;
UNICODE_STRING CommandLine;
PVOID Environment;
ULONG StartingX;
ULONG StartingY;
ULONG CountX;
ULONG CountY;
ULONG CountCharsX;
ULONG CountCharsY;
ULONG FillAttribute;
ULONG WindowFlags;
ULONG ShowWindowFlags;
UNICODE_STRING WindowTitle;
UNICODE_STRING DesktopInfo;
UNICODE_STRING ShellInfo;
UNICODE_STRING RuntimeData;
RTL_DRIVE_LETTER_CURDIR CurrentDirectores[32];
ULONG EnvironmentSize;
} BRTL_USER_PROCESS_PARAMETERS, *PBRTL_USER_PROCESS_PARAMETERS;
typedef struct _BPEB
{
UCHAR InheritedAddressSpace;
UCHAR ReadImageFileExecOptions;
UCHAR BeingDebugged;
UCHAR BitField;
ULONG ImageUsesLargePages : 1;
ULONG IsProtectedProcess : 1;
ULONG IsLegacyProcess : 1;
ULONG IsImageDynamicallyRelocated : 1;
ULONG SpareBits : 4;
PVOID Mutant;
PVOID ImageBaseAddress;
PPEB_LDR_DATA Ldr;
PBRTL_USER_PROCESS_PARAMETERS ProcessParameters;
PVOID SubSystemData;
PVOID ProcessHeap;
PRTL_CRITICAL_SECTION FastPebLock;
PVOID AtlThunkSListPtr;
PVOID IFEOKey;
ULONG CrossProcessFlags;
ULONG ProcessInJob : 1;
ULONG ProcessInitializing : 1;
ULONG ReservedBits0 : 30;
union
{
PVOID KernelCallbackTable;
PVOID UserSharedInfoPtr;
};
ULONG SystemReserved[1];
ULONG SpareUlong;
} BPEB, *PBPEB;

In GetCommandLineRemote(), when you allocate CommandLine.Buffer, you are over-allocating. MaximumLength is expressed in bytes, and WCHAR is 2 bytes in size. Your use of new[] is allocating 2x more memory than you really need, but that is OK since you are not reading more bytes than you allocate. You should divide MaximumLength by sizeof(WCHAR) to avoid over-allocating when using new WCHAR[]:
CommandLine.Buffer = new WCHAR[(CommandLine.MaximumLength / sizeof(WCHAR)) + 1];
However, the UNICODE_STRING data you read is not guaranteed to be null terminated, but you are treating it as if it were when constructing the final wstring. You should take the Length into account (which is also expressed in bytes) instead of relying on a null terminator:
wstring wCommandLine(CommandLine.Buffer, CommandLine.Length / sizeof(WCHAR));
More importantly, you are freeing CommandLine.Buffer with delete instead of delete[], so your code has undefined behavior from that point onward. Memory allocated with new is freed with delete. Memory allocated with new[] is freed with delete[]. You cannot interchange them.
Also, on a side note, in enum_ids(), you are leaking the HANDLE returned by CreateToolhelp32Snapshot(). You need to close it with CloseHandle() when you are done using it.

the exactly lines that caused the heap corruption are these :
CommandLine.Buffer = new WCHAR[CommandLine.MaximumLength];
result = ReadProcessMemory(hProc, (void*)parameters.CommandLine.Buffer, CommandLine.Buffer, parameters.MaximumLength, 0);
the ReadProcessMemory succeeds but causes a heap corruption that appears on using curl functions
changing this line :
CommandLine.Buffer = new WCHAR[CommandLine.MaximumLength];
to this :
CommandLine.Buffer = (wchar_t*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, CommandLine.MaximumLength);
then freeing the buffer before returning with HeapFree instead of delete solves the problem
I didn't face any problem like this where ReadProcessMemory caused such strange errors if it read into a buffer allocated with new
the documentation of the function hasn't anything about this
did anyone face such problem ?

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;

Windows Driver IOCTL Code Bluescreens / Crashes The Computer

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);

Enumerating process handles, weird issue

I scan for opened handles to my process and print them in the console.
I start my process
I attach cheat engine
I run the enumeration of opened handles
I see which process has a handle to my process
The weird issue at this point is as follows, check the code:
array<Accessor^>^ AntiCheat::ScanHandles()
{
List<Accessor^>^ accessorList = gcnew List<Accessor^>();
if (!EnableDebugPrivilege(true))
printf("EnableDebugPrivilege failed: %d\n", GetLastError());
tNtQuerySystemInformation oNtQuerySystemInformation = (tNtQuerySystemInformation)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQuerySystemInformation");
PSYSTEM_HANDLE_INFORMATION handleInfo = new SYSTEM_HANDLE_INFORMATION;
SYSTEM_INFORMATION_CLASS infoClass = (SYSTEM_INFORMATION_CLASS)16; // SystemHandleInformation
DWORD size = sizeof(SYSTEM_HANDLE_INFORMATION);
DWORD needed = 0;
NTSTATUS status = oNtQuerySystemInformation(infoClass, handleInfo, size, &needed);
while (!NT_SUCCESS(status))
{
if (needed == 0)
return nullptr;
// The previously supplied buffer wasn't enough.
delete handleInfo;
size = needed + 1024;
handleInfo = (PSYSTEM_HANDLE_INFORMATION)new BYTE[size];
status = oNtQuerySystemInformation(infoClass, handleInfo, size, &needed);
}
HANDLE currentProcess = GetCurrentProcess();
DWORD currentProcessId = GetProcessId(currentProcess);
for (DWORD i = 0; i < handleInfo->dwCount; i++)
{
//printf(".");
SYSTEM_HANDLE handle = handleInfo->Handles[i];
HANDLE procHandle = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, handle.dwProcessId);
if (GetLastError() == ERROR_ACCESS_DENIED)
continue;
HANDLE dupl = 0;
if (!DuplicateHandle(procHandle, (HANDLE)handle.wValue, currentProcess, &dupl, 0, false, DUPLICATE_SAME_ACCESS))
continue;
DWORD procId = GetProcessId(dupl);
if (procId == currentProcessId)
{
printf("accessing us\n");
char processName[MAX_PATH];
GetModuleFileNameEx((HMODULE)procHandle, NULL, processName, MAX_PATH);
accessorList->Add(gcnew Accessor(gcnew String(processName), handle.GrantedAccess));
}
CloseHandle(dupl);
}
return accessorList->ToArray();
}
If I uncomment the line with printf(".");, I see 3 opened handles to my process (cheatengine). If it's commented (runs way faster), there is no opened handle. However I don't know why this affects my code. Im surprised, does anyone know why this happens? Or how to find out how to find the handles without my printf("."); line?
Another issue is: each time I call the function, the number of allocated bytes duplicates. And I don't know why.
I see logic problems with your code.
You are not ignoring array items where handle.dwProcessId equals currentProcessId, so you end up opening handles to your own process. Since you are only interested in looking for other processes, you should be ignoring items where handle.dwProcessId is equal to currentProcessId.
You are not checking if OpenProcess() fails for any reason other than ERROR_ACCESS_DENIED. Do not call GetLastError() unless OpenProcess() actually returns NULL first.
You are not closing an opened handle if DuplicateHandle() fails. And why are you duplicating each source handle just to call GetProcessId() on it? You already have their process IDs from the array, so the whole DuplicateHandle()+GetProcessId() is completely unnecessary.
You are taking the wrong approach anyway. Have a look at this discussion:
Enumerating the processes referencing an object
Use NtQuerySystemInformation with SystemInformationClass set to SystemHandleInformation. This fills in an array of SYSTEM_HANDLE_INFORMATION structures, which are defined as:
typedef struct _SYSTEM_HANDLE_INFORMATION {
ULONG ProcessId;
UCHAR ObjectTypeNumber;
UCHAR Flags;
USHORT Handle;
PVOID Object;
ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE_INFORMATION;
Search for the entry corresponding to the handle you opened with ProcessID equal to GetCurrentProcessId(), then find all entries with the same Object pointer.
Although the discussion shows the wrong declaration for SYSTEM_HANDLE_INFORMATION. The following article shows the correct one:
HOWTO: Enumerate handles
#define SystemHandleInformation 16
typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)(
ULONG SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
/* The following structure is actually called SYSTEM_HANDLE_TABLE_ENTRY_INFO, but SYSTEM_HANDLE is shorter. */
typedef struct _SYSTEM_HANDLE
{
ULONG ProcessId;
BYTE ObjectTypeNumber;
BYTE Flags;
USHORT Handle;
PVOID Object;
ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, *PSYSTEM_HANDLE;
typedef struct _SYSTEM_HANDLE_INFORMATION
{
ULONG HandleCount; /* Or NumberOfHandles if you prefer. */
SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
With that said, try something more like this:
array<Accessor^>^ AntiCheat::ScanHandles()
{
List<Accessor^>^ accessorList = gcnew List<Accessor^>();
if (!EnableDebugPrivilege(true))
printf("EnableDebugPrivilege failed: %d\n", GetLastError());
tNtQuerySystemInformation oNtQuerySystemInformation = (tNtQuerySystemInformation) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQuerySystemInformation");
DWORD currentProcessId = GetCurrentProcessId();
HANDLE currentProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, currentProcessId);
PVOID currentProcessAddr = nullptr;
DWORD size = sizeof(SYSTEM_HANDLE_INFORMATION);
DWORD needed = 0;
PSYSTEM_HANDLE_INFORMATION handleInfo = (PSYSTEM_HANDLE_INFORMATION) new BYTE[size];
SYSTEM_INFORMATION_CLASS infoClass = (SYSTEM_INFORMATION_CLASS) 16; // SystemHandleInformation
NTSTATUS status = oNtQuerySystemInformation(infoClass, handleInfo, size, &needed);
while (status == STATUS_INFO_LENGTH_MISMATCH)
{
// The previously supplied buffer wasn't enough.
delete[] handleInfo;
size += 1024;
handleInfo = (PSYSTEM_HANDLE_INFORMATION) new BYTE[size];
status = oNtQuerySystemInformation(infoClass, handleInfo, size, &needed);
}
if (status != 0)
{
delete[] handleInfo;
return nullptr;
}
for (DWORD i = 0; i < handleInfo->dwCount; i++)
{
SYSTEM_HANDLE &handle = handleInfo->Handles[i];
if ((handle.dwProcessId == currentProcessId) &&
(currentProcess == (HANDLE)handle.wValue))
{
currentProcessAddr = handle.pAddress;
break;
}
}
for (DWORD i = 0; i < handleInfo->dwCount; i++)
{
SYSTEM_HANDLE &handle = handleInfo->Handles[i];
if ((handle.dwProcessId != currentProcessId) &&
(handle.pAddress == currentProcessAddr))
{
printf("accessing us\n");
HANDLE procHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, handle.dwProcessId);
if (procHandle != 0)
{
char processName[MAX_PATH+1];
DWORD len = GetModuleFileNameEx((HMODULE)procHandle, NULL, processName, MAX_PATH);
CloseHandle(procHandle);
processName[len] = '\0';
accessorList->Add(gcnew Accessor(gcnew String(processName), handle.GrantedAccess));
}
else
accessorList->Add(gcnew Accessor(gcnew String("unknown"), handle.GrantedAccess));
}
}
CloseHandle(currentProcess);
delete[] handleInfo;
return accessorList->ToArray();
}

How to get window station for a given process?

Say, if I have a process ID or its handle, can I get the window station that the process runs under?
Not straight forward, but try this:
Call EnumWindowStations() to enumerate available window stations in the same Session as the calling process (if you need to query a process in another Session then this will not work).
For each window station, call EnumDesktops() to enumerate its desktops.
For each desktop, call EnumDesktopWindows() to enumerate its top-level windows.
For each window, call GetWindowThreadProcessId() to get its process ID and compare it to the ID you are looking for.
Another option might be to do the following:
Call OpenProcess() to get a HANDLE from the target process ID.
Call NtQueryInformationProcess() to retrieve the address of the process's PEB structure.
Call ReadProcessMemory() to read the PEB. It's ProcessParams.DesktopName field contains the name of the workstation/desktop currently associated with the process (there are many more fields available in the PEB.ProcessParams then what MSDN shows).
Parse the DesktopName to extract the window station and desktop names.
Enumerate workstations as needed, looking for a matching name from GetUserObjectInformation().
OK, this one is not for the faint-hearted. Here's the code I came up with after #RemyLebeau's advice. It turns out I need to do this differently for 32/64-bit processes. I couldn't find the exact PEB structures for 64-bit processes, so I did some basic reverse-engineering to match it.
Also quite an interesting result:
If I call it for my own process, I get something like this: Winsta0\Default
If I call it on some other GUI process, I get this: Default
Overall, one can get the following types of desktops/winstations:
Default for your regular input desktop (that you're using now)
Winlogon or Winsta0\Winlogon for a secure desktop (for instance, Windows logon screen)
"" or empty string for a Metro (or Modern-UI) Windows 8 app.
So next is the code. It'd be nice if someone could review it:
//'dwProcID' = process ID to look up window station for
HANDLE hProc = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcID);
if(hProc)
{
BOOL (WINAPI *pfnIsWow64Process)(HANDLE, PBOOL);
(FARPROC&)pfnIsWow64Process = ::GetProcAddress(::GetModuleHandle(L"kernel32.dll"), "IsWow64Process");
SYSTEM_INFO si = {0};
::GetNativeSystemInfo(&si);
//See if 32-bit process on 64-bit OS
BOOL bWow64Proc = TRUE;
if(si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
{
if(pfnIsWow64Process)
if(!pfnIsWow64Process(hProc, &bWow64Proc))
{
//Error
_tprintf(L"ERROR in IsWow64Process: %d\n", ::GetLastError());
}
}
NTSTATUS ntStatus;
if(bWow64Proc)
{
//32-bit process
NTSTATUS (WINAPI *pfnNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
(FARPROC&)pfnNtQueryInformationProcess = ::GetProcAddress(::GetModuleHandle(L"Ntdll.dll"), "NtQueryInformationProcess");
if(pfnNtQueryInformationProcess)
{
PROCESS_BASIC_INFORMATION pbi = {0};
DWORD dwsz = 0;
if((ntStatus = pfnNtQueryInformationProcess(hProc, ProcessBasicInformation, &pbi, sizeof(pbi), &dwsz)) == 0 &&
dwsz <= sizeof(pbi) &&
pbi.PebBaseAddress)
{
//Define PEB structs
typedef struct _RTL_DRIVE_LETTER_CURDIR
{
WORD Flags;
WORD Length;
ULONG TimeStamp;
STRING DosPath;
} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR;
struct RTL_USER_PROCESS_PARAMETERS_32
{
ULONG MaximumLength;
ULONG Length;
ULONG Flags;
ULONG DebugFlags;
PVOID ConsoleHandle;
ULONG ConsoleFlags;
HANDLE StdInputHandle;
HANDLE StdOutputHandle;
HANDLE StdErrorHandle;
UNICODE_STRING CurrentDirectoryPath;
HANDLE CurrentDirectoryHandle;
UNICODE_STRING DllPath;
UNICODE_STRING ImagePathName;
UNICODE_STRING CommandLine;
PVOID Environment;
ULONG StartingPositionLeft;
ULONG StartingPositionTop;
ULONG Width;
ULONG Height;
ULONG CharWidth;
ULONG CharHeight;
ULONG ConsoleTextAttributes;
ULONG WindowFlags;
ULONG ShowWindowFlags;
UNICODE_STRING WindowTitle;
UNICODE_STRING DesktopName;
UNICODE_STRING ShellInfo;
UNICODE_STRING RuntimeData;
RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20];
};
struct PEB_32
{
BYTE Reserved1[2];
BYTE BeingDebugged;
BYTE Reserved2[1];
PVOID Reserved3[2];
void* Ldr;
RTL_USER_PROCESS_PARAMETERS_32* ProcessParameters;
BYTE Reserved4[104];
PVOID Reserved5[52];
void* PostProcessInitRoutine;
BYTE Reserved6[128];
PVOID Reserved7[1];
ULONG SessionId;
};
//Read PEB-32
PEB_32 peb32 = {0};
DWORD dwcbSzRead = 0;
if(ReadProcessMemory(hProc, pbi.PebBaseAddress, &peb32, sizeof(peb32), &dwcbSzRead) &&
dwcbSzRead == sizeof(peb32) &&
peb32.ProcessParameters)
{
//Read RTL_USER_PROCESS_PARAMETERS_32
RTL_USER_PROCESS_PARAMETERS_32 rupp32 = {0};
dwcbSzRead = 0;
if(ReadProcessMemory(hProc, peb32.ProcessParameters, &rupp32, sizeof(rupp32), &dwcbSzRead) &&
dwcbSzRead == sizeof(rupp32) &&
rupp32.DesktopName.Buffer)
{
//Get desktop name
int ncbSzLn = rupp32.DesktopName.Length + sizeof(TCHAR);
BYTE* pDesktopName = new (std::nothrow) BYTE[ncbSzLn];
if(pDesktopName)
{
dwcbSzRead = 0;
if(ReadProcessMemory(hProc, rupp32.DesktopName.Buffer, pDesktopName, ncbSzLn, &dwcbSzRead) &&
dwcbSzRead == ncbSzLn)
{
//Set last NULL
*(TCHAR*)(pDesktopName + ncbSzLn - sizeof(TCHAR)) = 0;
//We're done
_tprintf(L"Desktop32: %s\n", (LPCTSTR)pDesktopName);
}
else
_tprintf(L"ERROR in ReadProcessMemory DesktopName: %d\n", ::GetLastError());
delete[] pDesktopName;
}
else
_tprintf(L"ERROR DesktopName ptr\n");
}
else
_tprintf(L"ERROR in ReadProcessMemory RTL_USER_PROCESS_PARAMETERS_32: %d\n", ::GetLastError());
}
else
_tprintf(L"ERROR in ReadProcessMemory PEB-32: %d\n", ::GetLastError());
}
else
_tprintf(L"ERROR in NtQueryInformationProcess: %d\n", ntStatus);
}
else
_tprintf(L"ERROR NtQueryInformationProcess API\n");
}
else
{
//64-bit process
NTSTATUS (WINAPI *pfnNtQueryInformationProcess64)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
NTSTATUS (WINAPI *pfnNtWow64ReadVirtualMemory64)(HANDLE, PVOID64, PVOID, ULONG64, PULONG64);
(FARPROC&)pfnNtQueryInformationProcess64 = ::GetProcAddress(::GetModuleHandle(L"Ntdll.dll"), "NtWow64QueryInformationProcess64");
(FARPROC&)pfnNtWow64ReadVirtualMemory64 = ::GetProcAddress(::GetModuleHandle(L"Ntdll.dll"), "NtWow64ReadVirtualMemory64");
if(pfnNtQueryInformationProcess64 &&
pfnNtWow64ReadVirtualMemory64)
{
//Define PEB structs
struct UNICODE_STRING_64 {
USHORT Length;
USHORT MaximumLength;
PVOID64 Buffer;
};
struct PROCESS_BASIC_INFORMATION64
{
PVOID Reserved1[2];
PVOID64 PebBaseAddress;
PVOID Reserved2[4];
ULONG_PTR UniqueProcessId[2];
PVOID Reserved3[2];
};
PROCESS_BASIC_INFORMATION64 pbi64 = {0};
DWORD dwsz = 0;
if((ntStatus = pfnNtQueryInformationProcess64(hProc, ProcessBasicInformation, &pbi64, sizeof(pbi64), &dwsz)) == 0 &&
dwsz <= sizeof(pbi64))
{
struct PEB_64
{
UCHAR InheritedAddressSpace;
UCHAR ReadImageFileExecOptions;
UCHAR BeingDebugged;
BYTE b003;
ULONG Reserved0;
ULONG64 Mutant;
ULONG64 ImageBaseAddress;
ULONG64 Ldr;
PVOID64 ProcessParameters;
};
//Read PEB-64
PEB_64 peb64 = {0};
ULONG64 uicbSzRead = 0;
if(pfnNtWow64ReadVirtualMemory64(hProc, pbi64.PebBaseAddress, &peb64, sizeof(peb64), &uicbSzRead) == 0 &&
uicbSzRead == sizeof(peb64) &&
peb64.ProcessParameters)
{
//Don't know the structure of RTL_USER_PROCESS_PARAMETERS_64 thus read raw bytes
const int ncbSz_rawRUPP64 = sizeof(DWORD) * (6 * 8) + sizeof(UNICODE_STRING_64);
BYTE rawRUPP64[ncbSz_rawRUPP64] = {0};
uicbSzRead = 0;
if(pfnNtWow64ReadVirtualMemory64(hProc, peb64.ProcessParameters, &rawRUPP64, ncbSz_rawRUPP64, &uicbSzRead) == 0 &&
uicbSzRead == ncbSz_rawRUPP64)
{
//Point to the location in raw byte array
UNICODE_STRING_64* pDesktopName = (UNICODE_STRING_64*)(rawRUPP64 + sizeof(DWORD) * (6 * 8));
//Get desktop name
int ncbSzLn = pDesktopName->Length + sizeof(TCHAR);
BYTE* pBytesDesktopName = new (std::nothrow) BYTE[ncbSzLn];
if(pBytesDesktopName)
{
uicbSzRead = 0;
if(pfnNtWow64ReadVirtualMemory64(hProc, pDesktopName->Buffer, pBytesDesktopName, ncbSzLn, &uicbSzRead) == 0 &&
uicbSzRead == ncbSzLn)
{
//Set last NULL
*(TCHAR*)(pBytesDesktopName + ncbSzLn - sizeof(TCHAR)) = 0;
LPCTSTR pStrDesktopName = (LPCTSTR)pBytesDesktopName;
//We're done
_tprintf(L"Desktop64: %s\n", pStrDesktopName);
}
else
_tprintf(L"ERROR in NtWow64ReadVirtualMemory64 DesktopName: %d\n", ::GetLastError());
delete[] pBytesDesktopName;
}
else
_tprintf(L"ERROR DesktopName64 ptr\n");
}
else
_tprintf(L"ERROR in NtWow64ReadVirtualMemory64 RTL_USER_PROCESS_PARAMETERS_32: %d\n", ::GetLastError());
}
else
_tprintf(L"ERROR in NtWow64ReadVirtualMemory64 PEB-64: %d\n", ::GetLastError());
}
else
_tprintf(L"ERROR in NtQueryInformationProcess64: %d\n", ntStatus);
}
else
_tprintf(L"ERROR NtWow64QueryInformationProcess64 API\n");
}
::CloseHandle(hProc);
}
else
_tprintf(L"ERROR in OpenProcess: %d\n", ::GetLastError());
Following your link the following page might help:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms684859(v=vs.85).aspx
A process does NOT run under a Window Station, rather a window station is "associated" with a process.
There doesn't appear to be any direct method to find the window station associated with a given process.
I see two options:
Use code injection to run GetProcessWindowStation and then GetUserObjectInformation in the target process.
Use EnumWindowStations, EnumDesktops, and EnumDesktopWindows to iterate through all windows in the session; then use GetWindowThreadProcessId and compare the process ID with that of the target process. Of course that won't work if the process doesn't currently have a window.
You could also try using GetThreadDesktop and GetUserObjectInformation to get the desktop name; I'm not sure whether this includes the window station name or not. If this does work, see Traversing the Thread List in MSDN to enumerate the threads belonging to the process.

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.