X64–线程劫持-Suspend-Inject-Resume
- X64与X86 的原理是一样的。这里不做多解释
#include <stdio.h>
#include <Windows.h>
#include <string.h>
#include <Psapi.h>
#include <TlHelp32.h>
#include <tchar.h>
EXTERN_C VOID asm_code();
#define RIP_PARAM 0x1234567812345679
#define RIP_TARGET 0x1234567812345670
#define RIP_PRE 0x1234567812345678
#define RIP_END 0x78563412
static DWORD asm_code_size = 0;
PBYTE GetStealthStubPtr()
{
PBYTE Ptr = (PBYTE)asm_code;
if (*Ptr == 0xE9)
{
Ptr += *((PINT)(Ptr + 1)) + 5;
}
return Ptr;
}
ULONG32 GetStealthStubSize()
{
PUCHAR Ptr = NULL;
PUCHAR BasePtr = NULL;
ULONG32 Index = 0;
ULONG32 Signature = 0;
if (asm_code_size != 0)
{
return asm_code_size;
}
BasePtr = Ptr = GetStealthStubPtr();
for (Index = 0; Index < 2000; Index++)
{
Signature = *((PULONG)Ptr);
if (Signature == RIP_END)
{
asm_code_size = (ULONG)(Ptr - BasePtr);
return asm_code_size;
}
Ptr++;
}
printf("Error:没有找到:%p\n", RIP_END);
return 0;
}
VOID suspendInjectResume(HANDLE hHandle, LPVOID loadLibAddr, LPVOID dllPathAddr) {
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
HANDLE thread = NULL;
THREADENTRY32 te;
CONTEXT ctx;
DWORD firstThread = 0;
LPVOID scAddr;
int i;
unsigned char sc[0x100];
memcpy(sc, GetStealthStubPtr(), GetStealthStubSize());
te.dwSize = sizeof(THREADENTRY32);
ctx.ContextFlags = CONTEXT_FULL;
// Suspend Threads
if (Thread32First(hSnapshot, &te)) {
do {
if (te.th32OwnerProcessID == GetProcessId(hHandle)) {
thread = OpenThread(THREAD_ALL_ACCESS | THREAD_GET_CONTEXT, FALSE, te.th32ThreadID);
if (thread != NULL) {
printf("\t[+] Suspending Thread 0x%08x\n", te.th32ThreadID);
SuspendThread(thread);
break;
}
else {
printf("\t[+] Could not open thread!\n");
}
}
} while (Thread32Next(hSnapshot, &te));
}
else {
printf("\t[+] Could not Thread32First! [%d]\n", GetLastError());
CloseHandle(hSnapshot);
exit(-1);
}
CloseHandle(hSnapshot);
if (GetThreadContext(thread, &ctx) == 0)
printf("[!] GetThreadContext Failed!\n");
ULONG_PTR ptr = (ULONG_PTR)sc;
for (int Index = 0; Index < GetStealthStubSize(); Index++)
{
#pragma warning(disable:4311) // 关闭截断警告
if (*(PULONG_PTR)ptr == RIP_PARAM)
{
*(PULONG_PTR)ptr = (ULONG_PTR)dllPathAddr;
}
else if (*(PULONG_PTR)ptr == RIP_PRE) // NETEntry
{
*(PULONG_PTR)ptr = (ULONG_PTR)ctx.Rip;
}
else if (*(PULONG_PTR)ptr == RIP_TARGET) // NETEntry
{
*(PULONG_PTR)ptr = (ULONG_PTR)loadLibAddr;
}
ptr++;
}
scAddr = VirtualAllocEx(hHandle, NULL, GetStealthStubSize(), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hHandle, scAddr, (LPCVOID)sc, GetStealthStubSize(), NULL);
ctx.Rip = (DWORD64)scAddr;
printf("new RIP:%p\n", scAddr);
if (SetThreadContext(thread, &ctx) == 0)
printf("[!] SetThreadContext Failed!\n");
getchar();
getchar();
ResumeThread(thread);
getchar();
getchar();
}
#define SE_DEBUG_PRIVILEGE 20
typedef long(__fastcall *pfnRtlAdjustPrivilege64)(ULONG, ULONG, ULONG, PVOID);
pfnRtlAdjustPrivilege64 RtlAdjustPrivilege;
BOOL EnableDebugPrivilegeX64()
{
DWORD dwRetVal;
RtlAdjustPrivilege = (pfnRtlAdjustPrivilege64)GetProcAddress((HMODULE)(GetModuleHandle(_T("ntdll.dll"))), "RtlAdjustPrivilege");
if (RtlAdjustPrivilege == NULL)
{
return FALSE;
}
/**
.常量 SE_BACKUP_PRIVILEGE, "17", 公开
.常量 SE_RESTORE_PRIVILEGE, "18", 公开
.常量 SE_SHUTDOWN_PRIVILEGE, "19", 公开
.常量 SE_DEBUG_PRIVILEGE, "20", 公开
*/
return RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, 1, 0, &dwRetVal);
}
BOOL Inject(DWORD ProcessId, LPCSTR szPar)
{
DWORD dwOldProtect;
CONST PVOID StartPoint = (PVOID)GetStealthStubPtr();
EnableDebugPrivilegeX64();
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
if (hProcess == NULL)
{
printf("Error :OPenProcess\n");
return FALSE;
}
PVOID funRemote = 0;
if (!(funRemote = (PVOID)GetProcAddress(LoadLibrary(_T("kernel32.dll")), "LoadLibraryA")))//"User32.dll")),"MessageBoxA")))
{
printf("Error :GetProcAddress\n");
return FALSE;
}
PVOID strRemote = NULL;
if (!(strRemote = VirtualAllocEx(hProcess, NULL, (size_t)(strlen(szPar) + 1) * sizeof(char), MEM_COMMIT, PAGE_READWRITE)))
{
printf("Error :VirtualAllocEx\n");
return FALSE;
}
if (!WriteProcessMemory(hProcess, strRemote, szPar, (strlen(szPar) + 1) * sizeof(char), NULL))
{
printf("Error :WriteProcessMemory\n");
return FALSE;
}
suspendInjectResume(hProcess, funRemote, strRemote);
return TRUE;
}
int main()
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe = { 0 };
pe.dwSize = sizeof(pe);
if (Process32First(hSnapshot, &pe)) {
while (Process32Next(hSnapshot, &pe))
{
if (!_stricmp(pe.szExeFile, "showfour.exe"))
{
CHAR* szDll = "\\SimpleDll.dll";
CHAR szFullPath[MAX_PATH] = { 0 };
GetCurrentDirectory(MAX_PATH, szFullPath);
strcat_s(szFullPath, szDll);
if (Inject(pe.th32ProcessID, szFullPath))
{
//ShowMessage(_T("注入成功"));
}
else
{
printf("注入失败\n");
}
break;
}
}
}
else {
printf("\t[+] Could not ProcessFirst! [%d]\n", GetLastError());
CloseHandle(hSnapshot);
exit(-1);
}
CloseHandle(hSnapshot);
return 0;
}
- 核心的汇编代码
.CODE
public asm_code
asm_code PROC
push rax
push r12
push rcx
pushfq
mov r12,rsp
and r12,15
test r12 , r12
jne ok
sub rsp , 8
ok:
sub rsp , 4 * 8 + 8
mov rcx, 1234567812345679h
mov rax, 1234567812345670h
call rax
test r12 , r12
jne next
add rsp , 8
next:
add rsp , 4 * 8 + 8
popfq ; POPFQ pops 64 bits from the stack, loads the lower 32 bits into RFLAGS, and zero extends the upper bits of RFLAGS.
pop rcx
pop r12
sub rsp,8
pop rax
pop rax
mov qword ptr [rsp-16],rax ;因为要用rax ,将它放到栈外面的位置了
mov rax,1234567812345678h ;设置返回值
push rax
mov qword ptr rax,[rsp - 8] ;恢复一下rax
ret
; 硬编码 获得ASM-CODE 长度
db 12h
db 34h
db 56h
db 78h
asm_code ENDP
END
这里需要注意的是,在调用ResumeThread 之后,线程的部分寄存器与GetThreadContext 获得到的值不一样,但是运行是没有出错的,不知道是什么原因,如果为了稳定的话,应该在外面保存整个Context 结构,在申请代码空间的时候同样的为需要存储的寄存器存储空间并将所得到的地址传递给汇编代码。这样才可以保证在执行我们的代码前后目标线程的执行环境不变。这里不给出实现了,后面介绍esayhook 的代码的时候会有这样的实现。