Windows 内核驱动程序:ZwAllocateVirtualMemory 导致线程终止

Windows Kernel Driver: ZwAllocateVirtualMemory causing thread to terminate

我正在尝试编写一个 APC dll 注入驱动程序,我找到了 this 示例并考虑根据我的需要对其进行修改。

我用的是PcreateProcessNotifyRoutineEx to obtain the ProcessId for specific applications I targeting, in this case, "iexplore.exe" and then use PloadImageNotifyRoutine to check if ntdll.dll is already loaded and initialized (from ),如果加载了ntdll.dll,我就调用"my"InjectDLL函数

这是调用 InjectDll 的 PloadImageNotifyRoutine 函数:

VOID PloadImageNotifyRoutine(
    _In_ PUNICODE_STRING FullImageName,
    _In_ HANDLE ProcessId,
    _In_ PIMAGE_INFO ImageInfo
)
{
    PEPROCESS Process = NULL;
    PETHREAD Thread = NULL;
    PCHAR pTeb = nullptr;
    DWORD ArbitraryUserPointer = 0;
    PCHAR pszProcessNameA = nullptr;

    pTeb = (PCHAR)__readfsdword(0x18);
    ArbitraryUserPointer = *(DWORD*)(pTeb + 0x014);

    // If ArbitraryUserPointer points to kernel32.dll it means ntdll.dll is done loading.
    if (FALSE == IsStringEndWith((wchar_t*)ArbitraryUserPointer, L"\kernel32.dll"))
    {
        return;
    }

    if (!NT_SUCCESS(PsLookupProcessByProcessId(ProcessId, &Process)))
    {
        return;
    }

    pszProcessNameA = (PCHAR)PsGetProcessImageFileName(Process);
    if (FALSE == StringNCompare(pszProcessNameA, "iexplore.exe", GetStringLength("iexplore.exe")))
    {
        return;
    }

    Thread = KeGetCurrentThread();
    InjectDll(MODULE_PATH, Process, Thread);
    ObDereferenceObject(Process);
}

这是 InjectDll 函数:

BOOLEAN InjectDll(PWCHAR pModulePath, PEPROCESS Process, PETHREAD Thread)
{
    PKINJECT mem;
    ULONG size;

    mem = NULL;
    size = 4096;

    if (!NT_SUCCESS(ZwAllocateVirtualMemory(NtCurrentProcess(), (PVOID*)&mem, 0, &size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)))
    {
        return FALSE;
    }

    //more code ...
}

我在这里删除了一些检查,这样会更清楚。

我试着调试了一下,但似乎 ZwAllocateVirtualMemory 正在尝试分配内存,但在某个时候失败并终止了线程。

调用 ExAllocatePoolWithTag 后,它开始释放内存、取消映射部分并终止线程(<= 我在调试时在堆栈中看到的调用 - 并没有真正跟踪每个调用,但在一般视图中查看了它)。

线程终止前的堆栈:

nt!ExAllocatePoolWithTag+0x195
nt!NtAllocateVirtualMemory+0x1066
nt!KiSystemServicePostCall
nt!ZwAllocateVirtualMemory+0x11
kernel_apc_dll_injector!InjectDll+0x54
kernel_apc_dll_injector!PloadImageNotifyRoutine+0x2b0
nt!PsCallImageNotifyRoutines+0x62

该进程仍然可以从任务管理器中看到,但它的内存是 92k 并且没有 CPU 使用,可能是因为它没有正确 "cleaned up"。

不知道我的分析对不对,也许对于这个问题也没有必要。

首先附注 - 不从图像通知例程中调用 PsLookupProcessByProcessId。这根本就不需要。检查 ProcessId == PsGetCurrentProcessId()。如果是 - 使用当前进程指针(就像你在调用 ZwAllocateVirtualMemory - NtCurrentProcess() 中使用的那样)否则就存在。

现在关于主要 - "ZwAllocateVirtualMemory causing thread to terminate" - 当然没有。线程 未终止 。它 挂起 。首先,如果线程终止——因为这是在这个阶段进程中的单线程——所有进程都终止。但是您自己说 进程仍然可以从任务管理器中看到 。您还如何查看已终止线程的调用堆栈?这也说明线程没有终止但在 ExAllocatePoolWithTag

中等待

在某些关键区域执行回调的问题,您可以在此例程中执行的操作受到限制(操作系统调用驱动程序的进程通知例程 PASSIVE_LEVEL 在正常内核 APC 禁用的临界区内)。限制之一是调用 ZwAllocateVirtualMemory - 它挂在回调中,您可以看到。

所以你不能调用 ZwAllocateVirtualMemory 并直接从回调中进行注入。但解决方案存在。将普通内核 apc 插入当前线程。它将不会就地执行,因为 - 在回调中禁用了正常的内核 APC。但是就在您退出回调并且 apc 将被启用之后 - 您的 apc 被执行。在这里(在正常程序中)你已经可以调用 ZwAllocateVirtualMemory 并进行 dll 注入