在网上找了很长时间也没找到驱动向应用层创建线程的方法,于是自己研究了一份,现在把研究的结果共享给大家。 1. 获取 NtCreateThreadEx接口的地址 NtCreateThreadEx是未导出函数,需要我们手动获取地址。对于各位大手子来说在驱动里获取一个未导出接口的地址肯定不是难事,这里不多赘述。 2.构造 NtCreateThreadEx的参数
NtCreateThreadEx函数的定义
NTSTATUS NtCreateThreadEx(
OUT PHANDLE ThreadHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes,
IN HANDLE ProcessHandle,
IN PVOID StartRoutine,
IN PVOID Argument,
IN ULONG CreateFlags,
IN SIZE_T ZeroBits,
IN SIZE_T StackSize,
IN SIZE_T MaximumStackSize,
OUT PPS_ATTRIBUTE_LIST AttributeList); 构造ObjectAttributes参数
OBJECT_ATTRIBUTES objAttr;
CLIENT_ID clientId;
InitializeObjectAttributes(&objAttr, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
构造 shellcode,也就是线程的启动地址。这里我用的是 FreeLibrary 函数作为启动地址,当然具体的函数参数(RCX)和函数地址都是动态的,需要执行的时候进行赋值。
UCHAR opcodes[] = {
0x48, 0x83, 0xEC, 0x28, // sub rsp, 0x28
0x48, 0x31, 0xC9, // xor rcx, rcx
0x48, 0x31, 0xc0,// xor rax, rax
0x48, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00,0x00,//mov rcx, ModuleBase
0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00,0x00,//mov rax, func_FreeLibrary
0xff, 0xd0,//call rax
0x48, 0x83, 0xC4, 0x28, // add rsp, 0x28
0xc3,//ret};
3.在应用程序中分配内存并创建线程这里采用附加读写的方式进行内存分配
KAPC_STATE ApcState = { 0 };
KeStackAttachProcess(TargetProcess, &ApcState);
//设置当前线程运行在用户模式
_KTHREAD->PreviousMode = UserMode
//给shellcode分配内存
PVOID codeBuffer = 0;
ZwAllocateVirtualMemory(ZwCurrentProcess(), &codeBuffer, 0, &Size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
RtlZeroMemory(codeBuffer, Size);
memcpy(codeBuffer, opcodes, sizeof(opcodes));
//设置当前线程运行在内核模式并创建线程
_KTHREAD->PreviousMode = KernelMode
NTSTATUS status;
HANDLE hThread;
status = NtCreateThreadEx(&hThread,
THREAD_ALL_ACCESS,
&objAttr,
NtCurrentProcess(),
(PVOID)codeBuffer,
(PVOID)NULL,
0,
0,
0x1000,
0x1000,
NULL);
//重新设置为用户模式,并等待线程执行完毕
_KTHREAD->PreviousMode = UserMode
if (NT_SUCCESS(status)) {
// 延迟 60s
LARGE_INTEGER Timeout = { 0 };
Timeout.QuadPart = -(60ll * 10 * 1000 * 1000);
status = ZwWaitForSingleObject(hThread, TRUE, &Timeout);
NtClose(hThread);
}
//释放分配的内存
status = ZwFreeVirtualMemory(NtCurrentProcess(), &codeBuffer, &Size, MEM_RELEASE);
//恢复原来的运行模式
_KTHREAD->PreviousMode = oldMode;
KeUnstackDetachProcess(&ApcState);
以上就是内核向用户层创建线程的全部思路,如果有问题可以在评论区留言。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)