'Trouble passing a C# string from userland to kernelmode C and using it to find specific LDR_DATA_TABLE_ENTRY
I am having difficulty comparing a string passed from usermode type LPWSTR to a LDR table entry type UNICODE_STRING
Kernel C:
struct {
int pid;
int user_pid;
int size;
int protection_mode;
int allocation_type;
void* address;
void* write_buffer;
LPWSTR module_name;
}
userland_operation;
This struct is passed to the kernel via deviceiocontrol. The counterpart userland struct is as follows:
public struct MemOperation
{
public int Pid;
public int UserPid;
public int Size;
public int protection_mode;
public int allocation_type;
public IntPtr Addr;
public IntPtr WriteBuffer;
[MarshalAs(UnmanagedType.LPWStr)] public String ModuleName;
}
Where the String ModuleName
is Marshaled as LPWStr.
ModuleName
is the desired search term for the loaded module in a process. Now, here's where things get tricky. The string I have access to via the _LDR_DATA_TABLE_ENTRY
is a UNICODE_STRING
. I want to compare this UNICODE_STRING with my LPWSTR.
I have tried the following and it did not work:
{
UNICODE_STRING str;
RtlInitUnicodeString(&str, module_name) // module name is the userland passed string LPWSTR
if (RtlCompareUnicodeString(&str, &module_ldr->BaseDllName, TRUE) {
}
}
I've also tried wcscmp, and a few other things. I'm not sure how I can compare these two properly. I've added some minor pseudocode to the function to provide additional context on what I'm looking to do.
NTSTATUS GetModuleList(HANDLE PID, PVOID UserBuffer, LPWSTR module_name) {
KAPC_STATE APC;
__try {
PEPROCESS TargetProcess;
PsLookupProcessByProcessId(PID, &TargetProcess);
PPEB Peb = PsGetProcessPeb(TargetProcess);
if (!Peb)
return STATUS_INVALID_PARAMETER;
KeStackAttachProcess(TargetProcess, &APC);
UINT64 Ldr = (UINT64)Peb + PEBLDR_OFFSET;
ProbeForRead((CONST PVOID)Ldr, 8, 8);
PLIST_ENTRY ModListHead = (PLIST_ENTRY)(*(PULONG64)Ldr + PEBLDR_MEMORYLOADED_OFFSET);
ProbeForRead((CONST PVOID)ModListHead, 8, 8);
PLIST_ENTRY Module = ModListHead->Flink;
while (ModListHead != Module) {
LDR_DATA_TABLE_ENTRY* Module_Ldr = (LDR_DATA_TABLE_ENTRY*)(Module);
//psuedo if (module_name is in Module_Ldr->BaseDllName) // the comparison, where BaseDllName is type UNICODE_STRING
Module = Module->Flink;
}
KeUnstackDetachProcess(&APC);
ObDereferenceObject(TargetProcess);
return STATUS_SUCCESS;
Solution 1:[1]
Figured I'd answer this since I asked it a few years ago and no longer have this issue.
The string I create from userland prior to calling KeStackAttachProcess
is not valid once that is called. I'm not quite sure if that's just how stack attaching to a process works, but regardless the fix was something very trivial such as (this is pseudo):
RtlInitUnicodeString(&str, module_name)
UNICODE_STRING example;
KeStachAttachProcess()
example = &module_ldr->BaseDllName;
KeStackDetachProcess()
RtlCompareUnicodeString(&example, &module_name)
Where the comparison is done outside of the attach.
Solution 2:[2]
In this call below:
if (RtlCompareUnicodeString(&str, &module_ldr->BaseDllName) {
This function takes an additional argument which you are not passing. Please refer to https://docs.microsoft.com/en-us/windows/win32/devnotes/rtlcompareunicodestring
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Ben |
Solution 2 | Catch22 |