DLL注入
通过DLL注入可以对其他进程进行hook/热补丁/修复BUG,同时也是渗透其他进程的有效方法。
DLL被加载到进程后会自动运行DIIMain()函数,用户可以把想执行的代码放到DIIMain()函数,每当加载DLL时,添加的代码就会自然而然得到执行。利用该特性可修复程序Bug,或向程序添加新功能。
本篇博客将使用Inline Hook和DLL注入技术实现控制程序执行流
DLL注入流程
使用MS编译dll动态链接库后,可以通过一些常见的方式进行DLL的注入:
- 创建远程线程(CreateRemoteThread() API)
- 使用注册表(AppInit_DLLs值)
- 消息勾取(SetWindowsHookEx() API)
通过这些方法编写注入程序,然后对写有Inline Hook逻辑的DLL程序进行加载并且注入进目标进程
代码编写
DLL编写
DLL加载到进程后会运行DllMain()函数,通过这个特点,我们可以修改上一篇博客中的Inline Hook代码为如下代码
其中,DllMain()参数中的ul_reason_for_call
与之前TLS_Callback学习博客中的dwReason是相同的。因此,在进程启动的时候我们调用InlineHook()对MessageBoxA()进行hook
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#include <windows.h>
#include <stdio.h>
#include <iostream>
#include <tchar.h>
//修改API入口为 jmp xxx 使得程序能跳转到自己的函数
BYTE __NewCode[7] = { 0xE9, 0x0, 0x0, 0x0, 0x0 };
BYTE __OldCode[7] = { 0 };
FARPROC Messagebox_FARPROC;
HMODULE g_hMod;
int WINAPI MyMessageBoxA(
HWND hWnd, // handle to owner window
LPCTSTR lpText, // text in message box
LPCTSTR lpCaption, // message box title
UINT uType // message box style
);
void InlineHook()
{
DWORD dwOldProtect = 0;
//首先获取user32.dll的模块句柄
HMODULE user32_HMODULE = LoadLibraryA("user32.dll");
//利用GetProcAddress获取MessageBoxA导出函数的地址
Messagebox_FARPROC = GetProcAddress(user32_HMODULE, "MessageBoxA");
//利用ReadProcessMemory读取内存中MessageBoxA的前5字节
if (!ReadProcessMemory(GetCurrentProcess(), Messagebox_FARPROC, __OldCode, 6, NULL)) {
printf("[!] ReadProcessMemory Failed");
}
printf("%x%x%x%x%x\n", __OldCode[0], __OldCode[1], __OldCode[2], __OldCode[3], __OldCode[4]);
//E9跳转为jmp地址到目标地址的间隔,地址需要进行计算,计算出来后赋值给newcode的后4位
*((ULONG*)(__NewCode + 1)) = (ULONG)&MyMessageBoxA - ((ULONG)Messagebox_FARPROC + 5);
//memcpy(&__NewCode[1], &JmpAddress, 4);
printf("JmpAddress : %x\n", *((ULONG*)(__NewCode + 1)));
//将权限替换为可读写执行
VirtualProtect(Messagebox_FARPROC, 6, PAGE_EXECUTE_READWRITE, &dwOldProtect);
//直接写入内存
WriteProcessMemory(GetCurrentProcess(), Messagebox_FARPROC, __NewCode, 5, NULL);
//恢复之前的权限
VirtualProtect(Messagebox_FARPROC, 6, dwOldProtect, &dwOldProtect);
}
int WINAPI MyMessageBoxA(
HWND hWnd, // handle to owner window
LPCTSTR lpText, // text in message box
LPCTSTR lpCaption, // message box title
UINT uType // message box style
)
{
printf("Hook Successful\n");
//恢复原本的MessageBoxA,否则不能调用
WriteProcessMemory(GetCurrentProcess(), Messagebox_FARPROC, __OldCode, 5, NULL);
//可以使用参数进行调用
Sleep(3000);
MessageBoxA(NULL, (const char*)lpText, (const char*)lpCaption, MB_OK);
MessageBoxA(NULL, "Xunflash", "Hook successful", MB_OK);
//恢复hook
WriteProcessMemory(GetCurrentProcess(), Messagebox_FARPROC, __NewCode, 5, NULL);
return 0;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
HANDLE hThread = NULL;
g_hMod = (HMODULE)hModule;
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
InlineHook();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
//InlineHook();
//调用MessageBoxA
//MessageBoxA(NULL, "Hello World", "Title", MB_OK);
}
被注入程序编写
简单的调用MessageBoxA即可
#include <windows.h>
#include <stdio.h>
int main()
{
MessageBoxA(NULL, "test", "test123", S_OK);
}
注入
使用代码注入器注入
这里我们先使用Github上开源的代码注入器Xenos对程序进行注入,测试效果
可以看到已经注入成功
使用CreateRemoteThread() API注入
引用《逆向工程核心原理》p204页,利用CreateRemoteThread() API进行注入的代码注入器
具体步骤:
- OpenProcess获取已运行起来的目标进程句柄
- 在目标进程中分配DLL文件名大小的内存
- 将DLL路径写入分配的内存
- 利用GetModuleHandle获取kernal32的句柄,并使用GetProcAddress获取LoadLibraryW() API的地址
- 利用CreateRemoteThread在目标进程上启动线程运行LoadLibraryW,并且参数为刚刚在内存中写入的DLL路径,实现加载DLL
#include<Windows.h>
#include<tchar.h>
BOOL InjectDll(DWORD dwPid, LPCTSTR szDllPath) {
HANDLE hProcess = NULL, hThread = NULL;
HMODULE hMod = NULL;
LPVOID pRemoteBuf = NULL;
DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR);
LPTHREAD_START_ROUTINE pThreadProc;
//1.使用dwPid获取 目标进程句柄
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (!hProcess)
{
_tprintf(L"OpenProcess(%d)v failed!!! [%d]\n", dwPid, GetLastError());
return FALSE;
}
//2.在目标进程中分配szDllName大小的内存,dll文件的路径
pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);
//3.将dll路径写入分配的内存
WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllPath, dwBufSize, NULL);
//4.获取LoadLibraryW()API的地址
hMod = GetModuleHandle(L"kernel32.dll");
pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");
//5.在目标程序进程中运行线程
//pThreadProc为目标程序进程内存中的LoadLibraryW地址
//pRemoteBuf为写入到目标程序进程内存中的myhack.dll字符串地址
hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
return TRUE;
}
int _tmain(int argc, TCHAR* argv[]) {
if (argc != 3)
{
//检查输入程序的参数是否为3个
_tprintf(L"USAGE: %s Pid Dll_Path \n", argv[0]);
}
if (InjectDll((DWORD)_tstol(argv[1]), argv[2]))
{
_tprintf(L"InjectDll(\"%s\") Success!!!\n", argv[2]);
}
else
{
_tprintf(L"InjectDll(\"%s\") Failed!!!\n", argv[2]);
}
return 0;
}
使用AppInit_DLLs进行注入
此方式其实就是对注册表进行修改,让系统初始化程序的时候就加载上DLL。
只需要进入注册表中如下路径
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows
然后将LoadAppInit_DLL的值修改为1,并修改AppInit_DLLs的值为你想要注入的DLL的路径。重启电脑即可
这种方法实际上会向系统中所有程序都注入此DLL,可能导致系统出现问题。
使用SetWindowsHookEx()进行注入
这种方法利用了Windows的消息机制在事件发送到os之间设置一条钩链,来钩取不同的消息。这种方式只能勾取到SetWindowsHookEx中第一个参数指定的几种消息,如下摘取自MSDN(谷歌机翻)
价值 | 意义 |
---|---|
WH_CALLWNDPROC | 安装一个钩子过程,在系统将消息发送到目标窗口过程之前监视消息。有关详细信息,请参阅CallWndProc挂钩过程。 |
WH_CALLWNDPROCRET | 安装一个钩子过程,在消息被目标窗口过程处理后监视消息。有关详细信息,请参阅CallWndRetProc挂钩过程。 |
WH_CBT | 安装一个钩子过程,它接收对 CBT 应用程序有用的通知。有关详细信息,请参阅CBTProc挂钩过程。 |
WH_DEBUG | 安装一个对调试其他挂钩过程有用的挂钩过程。有关详细信息,请参阅DebugProc挂钩过程。 |
WH_FOREGROUNDIDLE | 安装将在应用程序的前台线程即将变为空闲时调用的挂钩过程。此挂钩对于在空闲时间执行低优先级任务很有用。有关详细信息,请参阅ForegroundIdleProc挂钩过程。 |
WH_GETMESSAGE | 安装一个挂钩过程,用于监视发布到消息队列的消息。有关详细信息,请参阅GetMsgProc挂钩过程。 |
WH_JOURNALPLAYBACK | 安装一个挂钩过程,用于发布以前由WH_JOURNALRECORD挂钩过程记录的消息。有关详细信息,请参阅JournalPlaybackProc挂钩过程。(Windows11已弃用) |
WH_JOURNALRECORD | 安装一个挂钩过程,记录发布到系统消息队列的输入消息。这个钩子对于录制宏很有用。有关详细信息,请参阅JournalRecordProc挂钩过程。(Windows11已弃用) |
WH_KEYBOARD | 安装一个监视击键消息的挂钩过程。有关详细信息,请参阅KeyboardProc挂钩过程。 |
WH_KEYBOARD_LL | 安装一个监视低级键盘输入事件的挂钩过程。有关详细信息,请参阅LowLevelKeyboardProc挂钩过程。 |
WH_MOUSE | 安装一个监视鼠标消息的挂钩程序。有关详细信息,请参阅MouseProc挂钩过程。 |
WH_MOUSE_LL | 安装一个钩子程序来监视低级鼠标输入事件。有关详细信息,请参阅LowLevelMouseProc挂钩过程。 |
WH_MSGFILTER | 安装一个挂钩过程,用于监视由于对话框、消息框、菜单或滚动条中的输入事件而生成的消息。有关详细信息,请参阅MessageProc挂钩过程。 |
WH_SHELL | 安装一个钩子过程,它接收对 shell 应用程序有用的通知。有关详细信息,请参阅ShellProc挂钩过程。 |
WH_SYSMSGFILTER | 安装一个挂钩过程,用于监视由于对话框、消息框、菜单或滚动条中的输入事件而生成的消息。挂钩过程监视与调用线程位于同一桌面的所有应用程序的这些消息。有关详细信息,请参阅SysMsgProc挂钩过程。 |
似乎也不能通过这种方式进行一些hook...普遍是通过这种方式对键盘输入事件进行监视,而后通过相应的回调函数对指定的进程进行拦截
详细过程可以参考其他文章。这里就不赘述
您真的好厉害