内核线程注入(x64)
生活随笔
收集整理的這篇文章主要介紹了
内核线程注入(x64)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
閱讀BlackBone源碼從里面扣出來的關于內核線程注入方法的使用。
經過自己修改后做成的Demo,功能主要通過Ring0層驅動Attach到目標進程(目標進程可以是32位進程也可以是64位進程,使用不同的ShellCode進行注入操作),然后調用NtCreateThreadEx來執行ShellCode,ShellCode做了一個注入Dll的簡單行為(加載Dll使用的是Ntdll模塊下的LdrLoadDll函數)。
關鍵函數如下:
//切換到目標進程創建內核線程進行注入 NTSTATUS AttachAndInjectProcess(IN HANDLE ProcessID) {PEPROCESS EProcess = NULL;KAPC_STATE ApcState;NTSTATUS Status = STATUS_SUCCESS;if (ProcessID == NULL){Status = STATUS_UNSUCCESSFUL;return Status;}//獲取EProcessStatus = PsLookupProcessByProcessId(ProcessID, &EProcess);if (Status != STATUS_SUCCESS){DbgPrint(("PsLookupProcessByProcessId函數失敗\n"));return Status;}//判斷目標進程x86 or x64BOOLEAN IsWow64 = (PsGetProcessWow64Process(EProcess) != NULL) ? TRUE : FALSE;//KeStackAttachProcess例程 將當前線程連接到目標進程的地址空間。KeStackAttachProcess((PRKPROCESS)EProcess, &ApcState);__try{PVOID NtdllAddress = NULL;PVOID LdrLoadDll = NULL;UNICODE_STRING NtdllUnicodeString = { 0 };UNICODE_STRING DllFullPath = { 0 };//獲取ntdll模塊基地址RtlInitUnicodeString(&NtdllUnicodeString, L"Ntdll.dll");NtdllAddress = GetUserModule(EProcess, &NtdllUnicodeString,IsWow64);if (!NtdllAddress){DbgPrint("%s: Failed to get Ntdll base\n", __FUNCTION__);Status = STATUS_NOT_FOUND;}//獲取LdrLoadDllif (NT_SUCCESS(Status)){LdrLoadDll = GetModuleExport(NtdllAddress, "LdrLoadDll", EProcess, NULL);/* 64位LdrLoadDll kd> u 0x00000000`77c77ac0 00000000`77c77ac0 48895c2410 mov qword ptr [rsp+10h],rbx 00000000`77c77ac5 48896c2418 mov qword ptr [rsp+18h],rbp 00000000`77c77aca 56 push rsi 00000000`77c77acb 57 push rdi 00000000`77c77acc 4154 push r12 00000000`77c77ace 4883ec50 sub rsp,50h 00000000`77c77ad2 f605b72e110009 test byte ptr [00000000`77d8a990],9 00000000`77c77ad9 498bf1 mov rsi,r9*/if (!LdrLoadDll){DbgPrint("%s: Failed to get LdrLoadDll address\n", __FUNCTION__);Status = STATUS_NOT_FOUND;}}PINJECT_BUFFER InjectBuffer = NULL;if (IsWow64){RtlInitUnicodeString(&DllFullPath, L"C:\\Windows\\Dllx86.dll");InjectBuffer = GetWow64Code(LdrLoadDll, &DllFullPath);}else{RtlInitUnicodeString(&DllFullPath, L"C:\\Windows\\Dllx64.dll");InjectBuffer = GetNativeCode(LdrLoadDll, &DllFullPath);}/* kd> u 0x00000000`002b0000 00000000`002b0000 4883ec28 sub rsp,28h 00000000`002b0004 4831c9 xor rcx,rcx 00000000`002b0007 4831d2 xor rdx,rdx 00000000`002b000a 49b800022b0000000000 mov r8,2B0200h 00000000`002b0014 49b9e0052b0000000000 mov r9,2B05E0h 00000000`002b001e 48b8c07ac77700000000 mov rax,77C77AC0h 00000000`002b0028 ffd0 call rax 00000000`002b002a 48bae8052b0000000000 mov rdx,2B05E8h*/ExecuteInNewThread(InjectBuffer, NULL, THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER, TRUE, &Status);if (!NT_SUCCESS(Status)){DbgPrint(("ExecuteInNewThread函數失敗\n"));}}__except (EXCEPTION_EXECUTE_HANDLER){Status = STATUS_UNSUCCESSFUL;}//將當前線程返回原進程的地址空間。KeUnstackDetachProcess(&ApcState);//釋放EProcessObDereferenceObject(EProcess);return Status; }這個代碼與我之前所寫的內核線程注入x86的代碼相差不大,附加的主要是對目標進程作了判斷是否位32位進程,然后根據是32位還是64位選擇構建不同的ShellCode。
構建ShellCode的代碼如下:
//創建注入代碼 64位目標進程 PINJECT_BUFFER GetNativeCode(IN PVOID LdrLoadDll, IN PUNICODE_STRING DllFullPath) {NTSTATUS Status = STATUS_SUCCESS;PINJECT_BUFFER InjectBuffer = NULL;SIZE_T Size = PAGE_SIZE;UCHAR Code[] ={0x48, 0x83, 0xEC, 0x28, // sub rsp, 0x280x48, 0x31, 0xC9, // xor rcx, rcx0x48, 0x31, 0xD2, // xor rdx, rdx0x49, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0, // mov r8, ModuleFileName offset +120x49, 0xB9, 0, 0, 0, 0, 0, 0, 0, 0, // mov r9, ModuleHandle offset +280x48, 0xB8, 0, 0, 0, 0, 0, 0, 0, 0, // mov rax, LdrLoadDll offset +320xFF, 0xD0, // call rax0x48, 0xBA, 0, 0, 0, 0, 0, 0, 0, 0, // mov rdx, COMPLETE_OFFSET offset +440xC7, 0x02, 0x7E, 0x1E, 0x37, 0xC0, // mov [rdx], CALL_COMPLETE 0x48, 0xBA, 0, 0, 0, 0, 0, 0, 0, 0, // mov rdx, STATUS_OFFSET offset +600x89, 0x02, // mov [rdx], eax0x48, 0x83, 0xC4, 0x28, // add rsp, 0x280xC3 // ret};Status = ZwAllocateVirtualMemory(ZwCurrentProcess(), &InjectBuffer, 0, &Size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);if (NT_SUCCESS(Status)){PUNICODE_STRING UserPath = &InjectBuffer->Path;UserPath->Length = 0;UserPath->MaximumLength = sizeof(InjectBuffer->Buffer);UserPath->Buffer = InjectBuffer->Buffer;RtlUnicodeStringCopy(UserPath, DllFullPath);// Copy codememcpy(InjectBuffer, Code, sizeof(Code));// Fill stubs*(ULONGLONG*)((PUCHAR)InjectBuffer + 12) = (ULONGLONG)UserPath;*(ULONGLONG*)((PUCHAR)InjectBuffer + 22) = (ULONGLONG)&InjectBuffer->ModuleHandle;*(ULONGLONG*)((PUCHAR)InjectBuffer + 32) = (ULONGLONG)LdrLoadDll;*(ULONGLONG*)((PUCHAR)InjectBuffer + 44) = (ULONGLONG)&InjectBuffer->Complete;*(ULONGLONG*)((PUCHAR)InjectBuffer + 60) = (ULONGLONG)&InjectBuffer->Status;return InjectBuffer;}UNREFERENCED_PARAMETER(DllFullPath);return NULL; } //創建注入代碼 32位目標進程 PINJECT_BUFFER GetWow64Code(IN PVOID LdrLoadDll, IN PUNICODE_STRING DllFullPath) {NTSTATUS Status = STATUS_SUCCESS;PINJECT_BUFFER InjectBuffer = NULL;SIZE_T Size = PAGE_SIZE;// CodeUCHAR Code[] ={0x68, 0, 0, 0, 0, // push ModuleHandle offset +1 0x68, 0, 0, 0, 0, // push ModuleFileName offset +60x6A, 0, // push Flags 0x6A, 0, // push PathToFile0xE8, 0, 0, 0, 0, // call LdrLoadDll offset +150xBA, 0, 0, 0, 0, // mov edx, COMPLETE_OFFSET offset +200xC7, 0x02, 0x7E, 0x1E, 0x37, 0xC0, // mov [edx], CALL_COMPLETE 0xBA, 0, 0, 0, 0, // mov edx, STATUS_OFFSET offset +310x89, 0x02, // mov [edx], eax0xC2, 0x04, 0x00 // ret 4};Status = ZwAllocateVirtualMemory(ZwCurrentProcess(), &InjectBuffer, 0, &Size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);if (NT_SUCCESS(Status)){// Copy pathPUNICODE_STRING32 pUserPath = &InjectBuffer->Path32;pUserPath->Length = DllFullPath->Length;pUserPath->MaximumLength = DllFullPath->MaximumLength;pUserPath->Buffer = (ULONG)(ULONG_PTR)InjectBuffer->Buffer;// Copy pathmemcpy((PVOID)pUserPath->Buffer, DllFullPath->Buffer, DllFullPath->Length);// Copy codememcpy(InjectBuffer, Code, sizeof(Code));// Fill stubs*(ULONG*)((PUCHAR)InjectBuffer + 1) = (ULONG)(ULONG_PTR)&InjectBuffer->ModuleHandle;*(ULONG*)((PUCHAR)InjectBuffer + 6) = (ULONG)(ULONG_PTR)pUserPath;*(ULONG*)((PUCHAR)InjectBuffer + 15) = (ULONG)((ULONG_PTR)LdrLoadDll - ((ULONG_PTR)InjectBuffer + 15) - 5 + 1);*(ULONG*)((PUCHAR)InjectBuffer + 20) = (ULONG)(ULONG_PTR)&InjectBuffer->Complete;*(ULONG*)((PUCHAR)InjectBuffer + 31) = (ULONG)(ULONG_PTR)&InjectBuffer->Status;return InjectBuffer;}UNREFERENCED_PARAMETER(DllFullPath);return NULL; }構建完shellcode之后,我們便從SSDT中獲取NtCreateThreadEx函數來創建新的線程執行ShellCode。
NTSTATUS ExecuteInNewThread(IN PVOID BaseAddress,IN PVOID Parameter,IN ULONG Flags,IN BOOLEAN Wait,OUT PNTSTATUS ExitStatus ) {HANDLE ThreadHandle = NULL;OBJECT_ATTRIBUTES ObjectAttributes = { 0 };//指定一個對象句柄的屬性 句柄只能在內核模式訪問。InitializeObjectAttributes(&ObjectAttributes, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);//創建線程NTSTATUS Status = SeCreateThreadEx(&ThreadHandle, THREAD_QUERY_LIMITED_INFORMATION, &ObjectAttributes,ZwCurrentProcess(), BaseAddress, Parameter, Flags,0, 0x1000, 0x100000, NULL);// 等待線程完成if (NT_SUCCESS(Status) && Wait != FALSE){//60sLARGE_INTEGER Timeout = { 0 };Timeout.QuadPart = -(60ll * 10 * 1000 * 1000);Status = ZwWaitForSingleObject(ThreadHandle, TRUE, &Timeout);if (NT_SUCCESS(Status)){//查詢線程退出碼THREAD_BASIC_INFORMATION ThreadBasicInfo = { 0 };ULONG ReturnLength = 0;Status = ZwQueryInformationThread(ThreadHandle, ThreadBasicInformation, &ThreadBasicInfo, sizeof(ThreadBasicInfo), &ReturnLength);if (NT_SUCCESS(Status) && ExitStatus){*ExitStatus = ThreadBasicInfo.ExitStatus;}else if (!NT_SUCCESS(Status)){DbgPrint("%s: ZwQueryInformationThread failed with status 0x%X\n", __FUNCTION__, Status);}}elseDbgPrint("%s: ZwWaitForSingleObject failed with status 0x%X\n", __FUNCTION__, Status);}else{DbgPrint("%s: ZwCreateThreadEx failed with status 0x%X\n", __FUNCTION__, Status);}if (ThreadHandle){ZwClose(ThreadHandle);}return Status; } NTSTATUS NTAPI SeCreateThreadEx(OUT PHANDLE ThreadHandle,IN ACCESS_MASK DesiredAccess,IN PVOID ObjectAttributes,IN HANDLE ProcessHandle, //目標進程句柄IN PVOID StartAddress, //線程起始地址IN PVOID Parameter, //線程參數IN ULONG Flags,IN SIZE_T StackZeroBits,IN SIZE_T SizeOfStackCommit,IN SIZE_T SizeOfStackReserve,IN PNT_PROC_THREAD_ATTRIBUTE_LIST AttributeList ) {NTSTATUS Status = STATUS_SUCCESS;//從SSDT中獲取NtCreateThreadEx函數地址//Windows7 64位 NtCreateThdIndex = 0x0A5/*Windows7 32位 NtCreateThdIndex = 0x058 0:001> u ntdll!NtCreateThreadEx ntdll!ZwCreateThreadEx: 77a35768 b858000000 mov eax,58h 77a3576d ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300) 77a35772 ff12 call dword ptr [edx] 77a35774 c22c00 ret 2Ch 77a35777 90 nop*/LPFN_NTCREATETHREADEX NtCreateThreadEx = (LPFN_NTCREATETHREADEX)(ULONG_PTR)GetSSDTEntry(0xA5);/*kd> u 0xfffff800`041c730c l 20nt!NtCreateThreadEx:fffff800`041c730c fff3 push rbxfffff800`041c730e 56 push rsifffff800`041c730f 57 push rdifffff800`041c7310 4154 push r12fffff800`041c7312 4155 push r13fffff800`041c7314 4156 push r14fffff800`041c7316 4157 push r15fffff800`041c7318 4881ec10070000 sub rsp,710hfffff800`041c731f 488b05bab6e8ff mov rax,qword ptr [nt!_security_cookie (fffff800`040529e0)]fffff800`041c7326 4833c4 xor rax,rsp……fffff800`041c735d e82e2ad1ff call nt!memset (fffff800`03ed9d90)……fffff800`041c737a e8112ad1ff call nt!memset (fffff800`03ed9d90) ……fffff800`041c738e 0f859a1b0600 jne nt! ?? ::NNGAKEGL::`string'+0x3c040 (fffff800`04228f2e)fffff800`041c739d 3898f6010000 cmp byte ptr [rax+1F6h],bl*/if (NtCreateThreadEx){//如果之前的模式是用戶模式,地址傳遞到ZwCreateThreadEx必須在用戶模式空間//切換到內核模式允許使用內核模式地址//Windows7 PrevMode = 0x1F6PUCHAR pPrevMode = (PUCHAR)PsGetCurrentThread() + 0x1F6;//64位 pPrevMode = 01UCHAR prevMode = *pPrevMode;*pPrevMode = KernelMode;//內核模式//創建線程Status = NtCreateThreadEx(ThreadHandle, DesiredAccess, ObjectAttributes,ProcessHandle, StartAddress, Parameter,Flags, StackZeroBits, SizeOfStackCommit,SizeOfStackReserve, AttributeList);//恢復之前的線程模式*pPrevMode = prevMode;}elseStatus = STATUS_NOT_FOUND;return Status; }獲取NtCreateThreadEx函數需要知道它在SSDT中的索引號,并且獲取SSDT基地址,64位下SSDT并沒有導出,需要從Kernel模塊中手動獲取,代碼在此就不列出,可以從下面分享的源碼中查看。
源碼下載地址:https://download.csdn.net/download/qq_37957965/10800753
總結
以上是生活随笔為你收集整理的内核线程注入(x64)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 点亮led灯的个数_LED灯的点亮电流一
- 下一篇: P2.5室内LED显示屏的技术参数以及优