无论是进攻还是防御 注入都是非常重要的基本功
记录一下各种注入方式的学习
0x00 注入的本质
- 先在进程空间里植入代码
- 拿到线程权限 去执行该代码
本质就是控制进程
参考 逆向工程核心原理第 3 部分 常用的注入方式有
- 创建线程
- 修改注册表
- 消息钩取(Hook)
- 直接修改 PE 文件
0x01 注入方式
可分为三类
静态修改
DLL劫持、导入表注入
全局性注入
输入法注入、消息钩子注入、注册表注入
线程注入
APC注入、消息钩子注入(指定)、远程线程注入、挂起进程注入
I. 导入表注入
原理 > 静态添加/修改导入表
先移动所有导入表整体的位置 因为原来的位置空间紧凑 不能添加表
就修改PE头的导入表位置
接着静态手动添加新的导入表
构造INA表 IAT表 以及IMAGE_IMPORT_BY_NAME
结构
就OK
导入表内容可以动
INT表可动 IAT表别动
II. 远程线程注入
动态
1. 注入DLL
写入模块名字 常规远程线程调用载入DLL
内存写入模块 修复重定位表 开远程线程执行模块的Entry
在目标进程中修复IAT表
2. 注入代码
直接将代码写到线程中
通过
WriteMemoryProcess
写入目标进程需要参数的话定义一个结构体 传入到目标进程
并启动远程线程进行调用
II. Windows HOOK 注入DLL
Windows 系统给我们提供了一些挂钩函数,使得被hook的进程可以在自己处理接收到的消息之前,先执行我们的消息处理函数,而这个消息处理函数一般会放在 DLL 中,来让目标进程加载,目标进程会映射整个DLL到进程空间,这实际上已经达到了注入代码的效果。
使用 API 函数 SetWindowsHookEx()
把一个应用程序定义的 Hook 子程安装到 Hook 链表中。 函数原型如下:第一个参数是消息编号,第二个参数为 Hook 函数
HHOOK SetWindowsHookExA(
int idHook,
HOOKPROC lpfn,
HINSTANCE hmod,
DWORD dwThreadId
);
操作如下
extern "C" _declspec(dllexport) void HookStart()
{
// HHOOK hHook = NULL;
g_hHook=SetWindowsHookEx(WH_KEYBOARD, HookProc,
GetModuleHandle("E://Viusal Studio//kanxue//注入技术//SetWindowsHookEx//Inject_dll//Debug//Inject_dll.dll"), 0);
if (NULL == g_hHook)
{
MessageBox(NULL, "安装钩子失败", "提示", MB_OKCANCEL);
}
}
II. 特洛伊注入
静态
DLL劫持
代替原DLL 转发其函数 同时加入自己的代码
DLL 劫持法 (输入表 DLL 替换法),原理是利用搜索 DLL 路径存在先后顺序 (exe 程序目录 > 系统目录 > 当前目录 > Path), 当较高层存在一个同名的 DLL 文件的时候,就会直接加载较高层的 DLL 文件。常常用于病毒的白加黑。需要注意的是黑 DLL 路径优先级一定要高于原来的 dll 文件,第二,一定要具有源 dll 文件所有的导出函数。
III. 注册表注入
静态
新创建的进程在加载 User32.dll 时,都会自动调用 LoadLibrary 去加载注册表中某个表项键值里写入的 Dll 路径
x64下:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows
x86下:HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\Windows
当某个进程加载 User32.dll 时,这里面列出的所有的 DLL 都将 User32.dll 利用 LoadLibrary 函数加载到该进程空间中。
我们可以把自己的代码放在一个 DLL 中,并加入该键值,这样就可以注入到所有使用 User32.dll 的进程中了。
当 DLL 以 LoadLibrary 的方式加载时,DllMain 会被以 DLL_PROCESS_ATTACH 为原因调用,实际上我们就在 DLL_PROCESS_ATTACH
中添加自己的代码就行了
需要关闭UEFI安全启动才能生效
参考链接: https://docs.microsoft.com/zh-cn/windows/win32/dlls/secure-boot-and-appinit-dlls?redirectedfrom=MSDN
IV. APC注入
APC 中文名称为异步过程调用, APC 是一个链状的数据结构,可以让一个线程在其本应该的执行步骤前执行其他代码,每个线程都维护这一个 APC 链。当线程从等待状态苏醒后,会自动检测自己得 APC 队列中是否存在 APC 过程。
所以只需要将目标进程的线程的 APC 队列里面添加 APC 过程,当然为了提高命中率可以向进程的所有线程中添加 APC 过程。然后促使线程从休眠中恢复就可以实现 APC 注入。
首先将 DLL 文件路径写入进程
lpData = VirtualAllocEx(hProcess, lpData, lstrlen(szDllFullPath) + 1, MEM_COMMIT, PAGE_READWRITE);
if (lpData)
{
bStatus = WriteProcessMemory(hProcess, lpData, szDllFullPath, lstrlen(szDllFullPath) + 1, &stSize);
if (FALSE == bStatus)
{
printf("WriteProcessMemory:%d\n", GetLastError());
return NULL;
}
}
然后使用 QueueUserAPC 将 APC 例程添加到 APC 队列中,QueueUserAPC 三个参数分别是 APC 例程,线程句柄,例程参数。所以还需要获取线程句柄
dwRet=QueueUserAPC((PAPCFUNC)LoadLibraryA, hThread, (ULONG_PTR)lpData);
if (NULL == dwRet)
{
printf("QueueUserAPC:%d\n", GetLastError());
return NULL;
}
V. 输入法注入
切换输入法时候,输入法管理器 imm32.dll 就会加载 IME 模块,这样就形成了输入法注入的充要条件。而由于这个 Ime 文件本质上只是个存放在 C:\WINDOWS\system32 目录下的特殊的 DLL 文件,因此我们可以利用这个特性,在 Ime 文件中使用 LoadLibrary () 函数待注入的 DLL 文件。
首先就是编写 ime 文件,至少需要两个导出函数 BOOL ImeClass_Register(HINSTANCE hinstDLL)
,BOOL WINAPI ImeInquire(LPIMEINFO lpIMEInfo, LPTSTR lpszUIClass, LPCTSTR lpszOption)
这两个是必须要实现的。剩下的导出函数可以选择不去实现。
接着编写注射器。利用 ImmInstallIME 安装 ime 文件,当输入法切换的时候就可以注入 dll 了。