Search code examples
wdkdll-injectionwindows-kernelkernel-mode

Injecting a DLL from LoadImageNotifyRoutine, hangs on ZwMapViewOfSection


So I'm making a crackme and one of the parts is to hook a certain function and wait for a certain combination a params to happen, then the challenge is done.

For that, I'm creating a driver to inject a DLL into processes that have a specific DLL and hook a certain function.

I'm doing it by

  1. Getting a handle for the DLL to inject
ZwCreateFile(
        &DeviceExtension->HookDllHandle, 
        GENERIC_ALL, 
        &Attributes, 
        &StatusBlock, 
        NULL, 
        0, 
        0, 
        FILE_OPEN, 
        FILE_SYNCHRONOUS_IO_NONALERT, 
        NULL, 
        0
)
  1. Then, registering a LoadImageNotifyRoutine inside driver main PsSetLoadImageNotifyRoutine(ImageCBK);

What's supposed to happen:

  1. I check the if the needed DLL (that will export my function) is loaded.
  2. By being inside the context of the process that invoked the callback, I create a section with ZwCreateSection, then map the dll into that section and call the DLL's entry point by creating a new thread.
  3. After that, the hooking should be no problem.

Even though the IRQL for ZwCreateSection and ZwMapViewOfSection allows their use inside a notify routine, still ZwMapViewOfSection hangs every time I try to use it.

I've been using some code from Beholder

status = ObOpenObjectByPointer(PsGetCurrentProcess(), OBJ_KERNEL_HANDLE, NULL, STANDARD_RIGHTS_ALL, NULL, KernelMode, &ProcessHandle);
if (!NT_SUCCESS(status))
{
    DbgPrint("Unable to get process handle\n");
    return STATUS_SEVERITY_ERROR;
}

// Create a new section for DLL mapping
InitializeObjectAttributes(&Attributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
status = ZwCreateSection(&DllSectionHandle, SECTION_MAP_WRITE | SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_QUERY, &Attributes, NULL, PAGE_EXECUTE_READ, SEC_IMAGE, DeviceExtension->HookDllHandle);
if (!NT_SUCCESS(status))
{
    ZwClose(ProcessHandle);
    DbgPrint("Section creation failed %08X\n", status);
    return status;
}
DbgPrint("Section created %08X\n", DllSectionHandle);

// Map DLL on the section
status = ZwMapViewOfSection(DllSectionHandle, ProcessHandle, &DllBaseAddress, 0, 0, NULL, &DllViewSize, ViewUnmap, 0, PAGE_EXECUTE_READ);
if (!NT_SUCCESS(status))
{
    ZwClose(ProcessHandle);
    ZwClose(DllSectionHandle);
    DbgPrint("Unable to map section %08X\n", status);
    return status;
}
DbgPrint("Mapped DLL: %08X\n", DllBaseAddress);

Sadly, it never shows the last DbgPrint with the DllBaseAddress


Solution

  • simply read documentation

    The operating system calls the driver's load-image notify routine at PASSIVE_LEVEL inside a critical region with normal kernel APCs always disabled

    and

    To avoid deadlocks, load-image notify routines must not call system routines that map, allocate, query, free, or perform other operations on user-space virtual memory.

    you ignore this and call routine ZwMapViewOfSection that map. and got deadlock

    solution is simply and elegant - insert normal kernel mode APC to current thread inside ImageCBK. because this APC is disabled here - it executed already after you return from ImageCBK -just system exit from critical region and enable APC. at this point your apc KernelRoutine/NormalRoutine will be called. and exactly inside NormalRoutine you must map