Notebook
Search…
Injection Method

R3 Dll 注入方式总结

1. SetWindowsHookEx 方法

HHOOK WINAPI SetWindowsHookEx(
_In_ int idHook,
_In_ HOOKPROC lpfn,
_In_ HINSTANCE hMod,
_In_ DWORD dwThreadId
);
该方法主要通过注册 指定线程(dwThreadId) 的 Windows 消息事件(idHook)的回调函数(lpfn)来进行注入 当 dwThreadId 设为 0 或者不是由当前进程创建的线程时,该 hook 函数(lpfn)必须存在于 指定的 dll (hMod)中 eg. SetWindowsHookEx(WH_CBT, MyHookProc, MyDll, 0) ,当系统中有线程触发 WH_CBT事件后,系统自动将 MyDll载入该线程中,并执行 MyHookProc,以此实现 Dll 注入

2. CreateRemoteThread 方法

HANDLE CreateRemoteThread(
HANDLE hProcess,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
该方法在CreateRemoteThread 在目标进程中创建一个线程,再通过 新创建的线程进行dll注入 获取函数 LoadLibrary 在内存中的地址
loadLibraryAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
在目标进程空间给将要注入的dll路径字符串(形如“c:\my.dll”)分配空间
remoteDllAddr = VirtualAllcoEx (targetProcess, NULL, str(myDllPath)+1, MEM_COMMIT | MEM_READWRITE);
将dll路径 写入目标进程空间
WriteProcesMemory(targetProcess, remoteDllAddr, (LPVOID)myDllPath, strlen(myDllPath)+1, NULL);
创建远程线程
CreateRemoteThread(
targetProcess, // 目标进程
NULL,
0,
// 线程的执行函数(此处是LoadLibrary)
(LPTHREAD_START_ROUTINE)loadLibraryAddr,
// 执行函数的参数(此处是 需要载入的dll路径)
remoteDllPath,
NULL,NULL );
通过该方法创建的线程,其创建进程和父进程均为 目标进程 !
另:还可以通过RtlCreateUserThread 和 NtCreateThread 函数进行注入,但这两个函数没有从ntdll.dll导出,因此需要手动寻找这两个函数的地址。注入原理和 CreateRemoteThread非常相似,只是替换了CreateRemoteThread 来在目标进程中创建线程,因此将它们归为一类

2.5 Reflective Dll Injection

该方法依然使用线程创建函数在目标进程中创建线程, 但不是通过使线程执行 LoadLibrary 来载入线程
具体过程比较复杂,简述如下:
  1. 1.
    将需要载入的dll的文件内容读入内存中(读入其二进制代码)
  2. 2.
    搜索其PE头,查找该dll 的入口地址(EntryPoint:DllMain) (关键一步)
  3. 3.
    将其入口地址(DllMain)直接作为 线程创建函数的线程起始地址(lpStartAddress)
  4. 4.
    创建远程线程

3. QueueUserApc 方法

DWORD QueueUserAPC(
PAPCFUNC pfnAPC,
HANDLE hThread,
ULONG_PTR dwData
);
该方法和 CreateRemoteThread 类似,将实现注入功能的函数(pfnAPC)加入目标线程的 APC 队列中,当目标线程(hThread)通过 SleepEx、WaitForSingleObject 、WaitForMultipleObject 等过程 变为 Alertable 状态 时,该线程将依次调用其APC队列中的函数,并以dwData作为传入参数,以此实现dll注入
和 CreateRemoteThread相同,pfnAPC 指向 LoadLibrary 的内存地址,dwData则为需要注入的dll的 路径
(目标线程的状态必须为 Alertable,否则不会执行 APC )

4. SetThreadContext 方法

该方法原理是 通过 SetThreadContext 函数更改 目标进程上下文中的指令指针(EIP for x86),跳转到目标进程中我们写入的内存区域,并开始执行,以此实现dll注入或任意代码执行。
由于该方法无法传递参数,因此需要在目标进程中写入一段 汇编代码 用于处理参数并调用 LoadLibrary,然后将EIP指向该段代码起始地址,汇编码如下所示:
(写入内容实际是汇编代码对应的机器码)
// 0xAAAAAAAA 为占位符,以后会被替换
unsigned char shellcode[] = {
0x68, 0xef, 0xbe, 0xad, 0xde, // push 0xAAAAAAAA, 将原EIP值压栈
0x9c, // pushfd,通用寄存器压栈
0x60, // pushad,标志寄存器压栈
0x68, 0xef, 0xbe, 0xad, 0xde, // push 0xAAAAAAAA, 将dll路径压栈(传参)
0xb8, 0xef, 0xbe, 0xad, 0xde, // mov eax, 0xAAAAAAAA
0xff, 0xd0, // call eax, 调用 LoadLibrary
0x61, // popad
0x9d, //popfd
0xc3 //ret
};
注入流程简述如下:
获取 LoadLibrary 内存地址 - loadLibraryAddr
在目标进程中申请内存,将dll路径写入 - remoteDllAddr
在目标进程中申请内存,用于保存shellcode - remoteShellcodeAddr
使用 SuspendThread(htargetThread) 挂起目标线程,并使用GetThreadContext(targetThread, &context) 获取线程上下文 context
存储 context中的原EIP并更新 EIP
oldEip = context.Eip;
context = (DWORD)remoteShellCode;
context.ContextFlags = CONTEXT_CONTROL;
替换 shellcode中的相关地址参数
// 修改内存页为可写可执行属性
VirtualProtect(shellcode, sizeof(shellcode), PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy((void*)(shellcode + 1), &oldEip);
memcpy((void*)(shellcode + 8), &remoteDllAddr);
memcpy((void*)(shellcode + 13, &loadLibraryAddr));
将shellcode写入目标进程并开始执行
// 写入shellcode
WriteProcessMemory(targetProcess, remoteShellcodeAddr, shellcode, NULL);
// 更改线程上下文
SetThreadContext(targetThread, &context);
// 恢复执行
ResumeThread(targetThread);

总结

当前 Windows 平台用户层的注入方法主要为以上4种,其共同点为均需要在目标进程的内存中申请空间来保存相关参数,再通过创建远程线程或注入shellcode的方式直接或间接调用 LoadLibrary 来载入dll (Reflective Injection 除外)
因此涉及到的函数主要有VirtualAlloc(Ex)、WriteProcessMemory、VirtualProtect(Ex), 为保证这些函数成功执行,其传入的目标进程句柄(hProcess)必须拥有相应的权限,通常进程句柄通过 OpenProcess 获取
HANDLE OpenProcess(
DWORD dwDesiredAccess, // 句柄权限, PROCESS_ALL_PROCESS表示请求所有权限
BOOL bInheritHandle,
DWORD dwProcessId // 进程id
);
PROCESS_ALL_ACCESS包括以下 特定权限:
  • PROCESS_TERMINATE
    PROCESS_CREATE_THREAD PROCESS_DUP_HANDLE PROCESS_SET_INFORMATION PROCESS_VM_OPERATION PROCESS_VM_READ PROCESS_VM_WRITE
而以上函数要求进程句柄至少具有 PROCESS_CREATE_THREAD, PROCESS_VM_OPERATION, PROCESS_VM_WRITE 权限

防御

在我们的驱动中,对打开进程或线程句柄的操作注册了回调函数,当某一个进程句柄被请求时,回调函数首先查看DesiredAccess参数,如果相应权限位被置1,则将该位置为0(即拒绝该权限的请求行为),因此通过OpenProcess函数返回的句柄将不具有 读写内存或进行内存操作的权限,以此来防御 dll 注入
目前为止,驱动可以防御住所有的用户层 针对特定进程的 dll 注入行为
Copy link
On this page
R3 Dll 注入方式总结
1. SetWindowsHookEx 方法
2. CreateRemoteThread 方法
2.5 Reflective Dll Injection
3. QueueUserApc 方法
4. SetThreadContext 方法
总结