10

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;
Alexander Malakhov
  • 3,383
  • 2
  • 33
  • 58
Ben
  • 749
  • 1
  • 7
  • 18
  • 1
    `UNICODE_STRING` is structure. LPWSTR is not. See: https://learn.microsoft.com/en-us/windows/win32/api/subauth/ns-subauth-unicode_string – jwdonahue Jun 05 '20 at 05:23
  • What do you exactly mean with _"it did not work"_? What error(s) do you get? Did you check the created string, for example using `DbgPrint()` (with the `%wZ` specifier)? – Mr.C64 Apr 11 '22 at 17:43

2 Answers2

1

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://learn.microsoft.com/en-us/windows/win32/devnotes/rtlcompareunicodestring

Catch22
  • 391
  • 2
  • 5
  • Sorry, the code did infact have that third parameter. I had rewritten a portion of it prior to posting the question, so I retyped some of the code in the comment here manually and forgot to include the third parameter. – Ben Jun 06 '20 at 03:17
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.

Ben
  • 749
  • 1
  • 7
  • 18