我正在尝试编写一个APC dll注入驱动程序,我找到了这个例子,并想根据我的需要对其进行修改。
我使用 PcreateProcessNotifyRoutineEx 获取我针对的特定应用程序的 ProcessId,在本例中为 "iexplore.exe",然后使用 PloadImageNotifyRoutine 检查 ntdll.dll 是否已加载和初始化(来自此建议(,如果加载了 ntdll.dll,我调用"我的"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 使用率,可能是因为它没有正确"清理"。
我不知道我的分析是否正确,也许这个问题甚至没有必要。
首先注意 - 不要从图像通知例程调用PsLookupProcessByProcessId
。 这根本不需要。 检查ProcessId == PsGetCurrentProcessId()
. 如果是 - 使用当前进程指针(如您并在调用中使用ZwAllocateVirtualMemory
-NtCurrentProcess()
(,否则就存在。
现在关于主要 - "ZwAllocateVirtualMemory 导致线程终止" - 当然没有。 线程未终止。 它挂了。 首先,如果线程终止 - 因为在此阶段这是进程中的单个线程 - 所有进程都将终止。 但你自己说该过程在任务管理器中仍然可见。 还有你如何查看终止线程的调用堆栈? 这也表示线程未终止,但在ExAllocatePoolWithTag
内等待
在某些关键区域中执行回调的问题,以及您可以在此例程中执行的操作受到限制(操作系统在禁用正常内核 APC 的关键区域内PASSIVE_LEVEL调用驱动程序的进程通知例程(。 其中一个限制 - 是调用ZwAllocateVirtualMemory
- 它挂在回调中,你可以看到。
所以你不能调用ZwAllocateVirtualMemory
直接从回调进行注入。 但解决方案是存在的。 将普通内核 APC 插入当前线程。 它不会就地执行,因为 - 在回调中禁用了正常的内核 APC。 但是就在您退出回调并且 APC 将被启用之后 - 您的 APC 已执行。 在这里(在正常例程中(,您已经可以调用ZwAllocateVirtualMemory
并进行 DLL 注入