-1

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

0xC0000022L
  • 20,597
  • 9
  • 86
  • 152
Denis Rozimovschii
  • 428
  • 1
  • 6
  • 19

1 Answers1

2

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

RbMm
  • 31,280
  • 3
  • 35
  • 56
  • I have tried inserting an APC, but it never got called after the routine finished. Besides that, doesn't the APC require some amount of executable virtual memory to copy actual APC function inside it? – Denis Rozimovschii Aug 26 '18 at 20:43
  • @DenisRozimovschii - in this case you do something wrong. wrong initialize apc for example. this must be kernel mode normal apc. of course apc not require any executable memory. but driver must not be unloaded until apc not finish processing – RbMm Aug 26 '18 at 20:46
  • Do you have any resources with examples of creating a kernel mode normal apc? All I've been stumbling upon are just theoretical tutorials or some game hacking forums – Denis Rozimovschii Aug 26 '18 at 21:42
  • @DenisRozimovschii - nothing hard - first you need allocate it from nonpaged pool. than call `KeInitializeApc` with `KeGetCurrentThread()`, `OriginalApcEnvironment` `KernelMode` and not 0 `NormalRoutine`. finally you call `KeInsertQueueApc`. if your driver can be unloaded at any time - you also need reference it (driverobject) before insert apc, and have all 3 apc routines enries in asm code, which call c++ and finally exit with jmp to `ObfDereferenceObject`. but until you can do this latter – RbMm Aug 26 '18 at 21:49
  • Yeah, but the three routines have to be inexecutable buffers of some sort. Don't they? Or can I just reference them like (PKNORMAL_ROUTINE)(some_func)? – Denis Rozimovschii Aug 26 '18 at 21:52
  • @DenisRozimovschii - this routines usually inside your driver code. for what you use type cast ? – RbMm Aug 26 '18 at 21:55
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/178815/discussion-between-denis-rozimovschii-and-rbmm). – Denis Rozimovschii Aug 26 '18 at 21:56