DLL注入--Suspend Inject Resume注入

  • A+
所属分类:逆向工程


此注入方式主要是使用挂起、设置运行点、继续的方式劫持目标程序的执行,通过GetThreadContext与SetThreadContext函数进行控制。

通过SetThreadContext设置线程即将执行的代码从而使其调用LoadLibraryA函数实现DLL加载。

 

SetThreadContext定义:

BOOL SetThreadContext(   HANDLE hThread,  //线程句柄,该句柄必须拥有THREAD_SET_CONTEXT权限  const CONTEXT *lpContext  //欲设置的上下文, 包含有各种寄存器);

关于CONTEXT 中ContextFlags该如何取值

//

// CONTEXT_CONTROL specifies SegSs, Rsp, SegCs, Rip, and EFlags.

//

// CONTEXT_INTEGER specifies Rax, Rcx, Rdx, Rbx, Rbp, Rsi, Rdi, and R8-R15.

//

// CONTEXT_SEGMENTS specifies SegDs, SegEs, SegFs, and SegGs.

//

// CONTEXT_FLOATING_POINT specifies Xmm0-Xmm15.

//

// CONTEXT_DEBUG_REGISTERS specifies Dr0-Dr3 and Dr6-Dr7.

 

注入流程

1. 打开进程句柄

2. LoadLibrary 的Shellcode写入目标进程中

3. 挑选其中一个通过SuspendThread将线程暂停。

4. 通过GetThreadContext保存当前状态

5. SetThreadContext 设置下一条语句将从Shellcode开始执行

6. 执行完成后通知注入程序,随后通过SetThreadContext 恢复之前的状态与代码位置

7. 通过ResumeThread使得线程继续运行

 

首先通过CreateToolhelp32Snapshot枚举所有线程,找出属于该进程的线程,找到第一个立即调用注入函数进行DLL注入。

 

注入代码向目标进程分配了0x1000个字节的RWX内存,0~0x6ff留给代码,0x700留给DLL路径。

写入DLL全路径至0x700偏移处。

随后将Shellcode写入0x0的位置。

 

Shellcode编写:可能我们第一个想法是通过SetThreadContext直接设置rcx为DLL全路径,rip为LoadLibrary,执行完成后通过注入程序SetThreadContext来恢复成原来的样子。

但是有个情况很难做到,通过syscall进入内核后在内核被挂起,此时通过GetThreadContext是syscall之前的上下文,而实际上syscall回来之后寄存器的值会发生变化。此时恢复后会因为寄存器错误导致崩溃。

所以这里还是不要通过SetThreadContext来恢复线程上下文,而仅仅通过他来设置rip,随后在shellcode中保存上下文,执行LoadLibrary 进行DLL注入,在此文章中DLL全目录设置在了shellcode + 0x700的位置,在执行完成时激活事件,通知注入线程,随后自身进入死循环指令(jmp $) 注入线程将其执行的代码移动到之前的位置继续向下执行。

shellcode:

DLL注入--Suspend Inject Resume注入


 

完整代码:


#include <stdio.h>#include <tchar.h>#include <windows.h>#include <Psapi.h>#include <TlHelp32.h>#define DLL_NAME "X:\xxxx\DllInject\x64\Debug\DllInject.dll" // DLL全路径

#define GLOBAL_EVENT _T("Global\Inject")
typedef BOOLEAN(*LPTHREAD_ROUTINE)(DWORD dwProcessId, DWORD dwThreadId, PVOID Context);static VOID EnumAllThread(DWORD dwProcessId, LPTHREAD_ROUTINE Routine, PVOID Context);static BOOLEAN RunCode(DWORD dwProcessId, DWORD dwThreadId, PVOID lpContext);
#pragma pack(push)#pragma pack(1)typedef struct _CODE_STRUCT{ BYTE Restore[0x18]; BYTE MovRcx[2]; UINT64 Rcx; BYTE SubRsp[3]; UINT32 Sub; BYTE MovRax[2]; UINT64 Rax; BYTE CallRax[2]; BYTE AddRsp[3]; UINT32 Add; BYTE Recovery[0x18]; BYTE Loop[2];}CODE_STRUCT, * LPCODE_STRUCT;#pragma pack(pop)
CODE_STRUCT Codes = { {0x50,0x51,0x52,0x53,0x55,0x41,0x50,0x41,0x51,0x41,0x52,0x41,0x53,0x41,0x54,0x41,0x55,0x41,0x56,0x41,0x57,0x56,0x57,0x9C}, //保存调用环境 {0x48,0xB9}, //修改Rcx {0x0}, //Rcx {0x48,0x81,0xEC}, //减少Rsp {0x308}, //Rsp {0x48,0xB8}, //修改Rax {0x0}, //Rax {0xFF,0xD0}, //Call Rax {0x48,0x81,0xC4}, //增加Rsp {0x308}, //Rsp {0x9D,0x5F,0x5E,0x41,0x5F,0x41,0x5E,0x41,0x5D,0x41,0x5C,0x41,0x5B,0x41,0x5A,0x41,0x59,0x41,0x58,0x5D,0x5B,0x5A,0x59,0x58}, //恢复调用环境 {0xEB, 0xFE} // Jmp $};
int main(){ DWORD dwProcessId; printf("输入进程ID:"); scanf_s("%d", &dwProcessId); EnumAllThread(dwProcessId, RunCode, NULL); return 0;}
BOOLEAN RunCode(DWORD dwProcessId, DWORD dwThreadId, PVOID lpContext){ SIZE_T szWriteBytes; BOOLEAN isSuccess = FALSE; HANDLE hThread = NULL; HANDLE hProcess = NULL; LPBYTE lpRmtCode = NULL; LPBYTE lpDllName = NULL;
CONTEXT Context = { 0 }; CONTEXT NewContext = { 0 };
do { hProcess = OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, dwProcessId); if (!hProcess) break;
lpRmtCode = (LPBYTE)VirtualAllocEx(hProcess, NULL, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (!lpRmtCode) break; lpDllName = lpRmtCode + 0x700; printf("Alloc %pn", lpRmtCode); if (!WriteProcessMemory(hProcess, lpDllName, DLL_NAME, sizeof(DLL_NAME), &szWriteBytes)) break;
hThread = OpenThread(THREAD_SUSPEND_RESUME | THREAD_SET_CONTEXT | THREAD_GET_CONTEXT, FALSE, dwThreadId); if (!hThread) break;
HANDLE hKernel = LoadLibrary(_T("kernel32.dll")); if (!hKernel) break;
PVOID lpLoadLibraryA = GetProcAddress(hKernel, "LoadLibraryA"); FreeLibrary(hKernel); if (!lpLoadLibraryA) break;
Codes.Rcx = (UINT64)lpDllName; Codes.Rax = (UINT64)lpLoadLibraryA;
if (!WriteProcessMemory(hProcess, lpRmtCode, (LPBYTE)&Codes, sizeof(Codes), &szWriteBytes)) break;
HANDLE hEvt = CreateEvent(NULL, FALSE, FALSE, GLOBAL_EVENT); if (!hEvt) break;
Context.ContextFlags = CONTEXT_CONTROL; GetThreadContext(hThread, &Context); Context.ContextFlags = CONTEXT_CONTROL; NewContext = Context; NewContext.Rip = (UINT64)lpRmtCode; SetThreadContext(hThread, &NewContext); ResumeThread(hThread); WaitForSingleObject(hEvt, INFINITE); Sleep(1000); SetThreadContext(hThread, &Context); ResumeThread(hThread); CloseHandle(hEvt); isSuccess = TRUE; } while (FALSE); if (hProcess) { if (lpRmtCode) VirtualFreeEx(hProcess, lpRmtCode, 0, MEM_RELEASE); CloseHandle(hProcess); } if (hThread) CloseHandle(hThread); return FALSE;}
//枚举所有线程VOID EnumAllThread(DWORD dwProcessId, LPTHREAD_ROUTINE Routine, PVOID Context){ THREADENTRY32 ThreadInfo; ThreadInfo.dwSize = sizeof(THREADENTRY32); HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwProcessId);
if (!Thread32First(hSnap, &ThreadInfo)) return; do { if (ThreadInfo.th32OwnerProcessID == dwProcessId) if (!Routine(dwProcessId, ThreadInfo.th32ThreadID, Context)) break; } while (Thread32Next(hSnap, &ThreadInfo));
CloseHandle(hSnap);}

本文始发于微信公众号(锋刃科技):DLL注入--Suspend Inject Resume注入

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: