在网上找了很长时间也没找到驱动向应用层创建线程的方法,于是自己研究了一份,现在把研究的结果共享给大家。
1.获取NtCreateThreadEx接口的地址
NtCreateThreadEx是未导出函数,需要我们手动获取地址。对于各位大手子来说在驱动里获取一个未导出接口的地址肯定不是难事,这里不多赘述。
2.构造NtCreateThreadEx的参数
NtCreateThreadEx函数的定义:
[C++] 纯文本查看 复制代码 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参数
[C++] 纯文本查看 复制代码 OBJECT_ATTRIBUTES objAttr;
CLIENT_ID clientId;
InitializeObjectAttributes(&objAttr, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);
构造 shellcode,也就是线程的启动地址。这里我用的是FreeLibrary 函数作为启动地址,当然具体的函数参数(RCX)和函数地址都是动态的,需要执行的时候进行赋值。
[C++] 纯文本查看 复制代码 UCHAR opcodes[] = {[/size][/font][/backcolor][/color][/size][/color]
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.在应用程序中分配内存并创建线程这里采用附加读写的方式进行内存分配[C++] 纯文本查看 复制代码 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);
以上就是内核向用户层创建线程的全部思路,如果有问题可以在评论区留言。
|