I'm trying to write an APC dll injection driver, I've found this example and thought to modify it to my needs.
I use 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 this recommendation), and if ntdll.dll is loaded, I call "my" InjectDLL function.
This is the PloadImageNotifyRoutine function that is calling InjectDll:
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);
}
And this is the InjectDll function:
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 ...
}
I removed here some checks so it would be more clear.
I tried to debug it a little, but it seems like ZwAllocateVirtualMemory is trying to allocate memory, failing at some point and terminate the thread.
After it calls ExAllocatePoolWithTag it starting to free memory, unmapping sections and terminate the thread (<= calls I saw at the stack while debugging - didn't really trace each call, but looked at it in general view).
The stack before the thread get terminated:
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
The process is still visible from task manager, but its Memory is 92k and has no CPU usage, probably because it didn't "cleaned up" properly.
I don't know if my analysis is right, and maybe it even unnecessary for this problem.
at first side note - not call PsLookupProcessByProcessId
from image notify routine. this is simply not need. check that ProcessId == PsGetCurrentProcessId()
. and if yes - use current process pointer (as you and use in call ZwAllocateVirtualMemory
- NtCurrentProcess()
) otherwise just exist.
now about main - "ZwAllocateVirtualMemory causing thread to terminate" - of course no. thread not terminate. it hung. at first if thread is terminate - because this is single thread in process at this stage - all process is terminate. but you yourself say that The process is still visible from task manager. also how you view call stack of terminated thread ? this also say that thread not terminated but wait inside ExAllocatePoolWithTag
problem that callback is execute in some critical region and The actions that you can perform in this routine are restricted (The operating system calls the driver's process-notify routine at PASSIVE_LEVEL inside a critical region with normal kernel APCs disabled). one of the restrictions - is call ZwAllocateVirtualMemory
- it hung in callback, which you and can see.
so you can not call ZwAllocateVirtualMemory
and do injection direct from callback. but solution exist. insert normal kernel apc to current thread. it will be not executed in-place because - normal kernel APCs disabled in callback. but just after you exit from callback and apc will be enabled - your apc executed. and here (in normal routine) you already can call ZwAllocateVirtualMemory
and do dll injection