一、DLL注入概述
1.简介
dll注入是一种将Windows动态链接库注入到目标进程中的技术,具体的说,就是将dll文件加载到一个进程的虚拟地址空间中。对某个进程进行dll注入,也就意味着dll模块与该进程共用一个进程空间,则这个dll文件就有了操纵这个进程空间的能力,以达到执行dll模块中的代码修改进程数据的能力。
2. 原理
DLL注入的工作原理是将自己的dll文件强制注入进其他进程,同时,被注入的DLL拥有目标进程的内存访问权限从而实现一些有用的功能。
例如:
改善功能与修复BUg
消息钩取
API钩取
恶意代码
....
二、几种常见的DLL注入手段
各个方式都分为注入程序和DLL程序两部分,其中DLL基本都是一样的,我们的目标是把我们自己的DLL注入程序中,不同的方式使用不同的注入程序,从而实现DLL注入。
1.远程线程注入
所用主要API
LoadLibrary():用来加载DLL文件,
CreateRemoteThread() :在其他程序中启动远程线程,使程序自动启用LoadLibrary()函数,从而加载DLL文件
WriteProcessMemory():在其他进程中写入数据
程序源码
BOOL InjectProgram(DWORD dwPID, LPCTSTR path){HANDLE hProcess = NULL, hThread = NULL;HMODULE hMod = NULL;LPVOID pRemoteBuf = NULL;DWORD dwBufSize = (DWORD)(_tcslen(path) + 1) * sizeof(TCHAR);LPTHREAD_START_ROUTINE pThreadProc;//用获取的PID找到注入进程的句柄hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);if (!hProcess){MessageBox(NULL, L"句柄失败", L"Error",MB_OK);_tprintf(L"Inject fail PID=%d,error is %d", dwPID, GetLastError);return 0;}//在目标进程的内存中分配空间pRemoteBuf = (LPVOID)VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);//在分配好的空间中写入dll文件名WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)path, dwBufSize, NULL);//获取LoadLibrary API的地址hMod = GetModuleHandle(L"kernel32.dll");pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");hThread = CreateRemoteThread(hProcess,NULL,0,pThreadProc,pRemoteBuf,0,NULL);if (!hThread){return 0;}WaitForSingleObject(hThread, INFINITE);CloseHandle(hThread);CloseHandle(hProcess);return TRUE;}int _tmain(int argc,TCHAR* argv[]) //主函数名为_tmain(),否则无法接受参数{if (argc != 3){MessageBox(NULL, L"Error", L"参数错误", MB_OK);OutputDebugString(L"参数数量不对");}else{BOOL flag;_tprintf(L"%d,Inject PID=%d", argc,(DWORD)_tstol(argv[1]));flag=InjectProgram((DWORD)_tstol(argv[1]), argv[2]);if (flag){MessageBox(NULL, L"YES", L"成功", MB_OK);}else{MessageBox(NULL, (LPCWSTR)&argv[1], L"错误", MB_OK);}} }
2.修改注册表注入
手动改注册表
运行regedit.exe,可以打开注册表编辑器,
找到HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\windows NT\windows,我们需要该其中的两项
需要改AppInit_DLLs,以及LoadAppInt_DLLs。
AppInit_DLLs的值会让程序在加载user32.dll时同时加载,就像你在食堂打一碗饭,食堂大妈顺手给了你一碗汤。
LoadAppInt_DLLs的值改为1,让程序加载上一项中修改的DLL地址。
重启之后生效。
程序自动注入
注入程序:
#define DSTKEY L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Windows"BOOL RegInject(WCHAR* path);int main(){WCHAR Dllpath[MAX_PATH] = L"C:\\Users\\Y\\Desktop\\myhack.dll";BOOL flag = RegInject(Dllpath);if (flag){_tprintf(L"SUCCESS");}else{printf("erro\n");} }BOOL RegInject(WCHAR* path){HKEY key = NULL;BYTE Dllpath[MAX_PATH] = { 0 };if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, DSTKEY, 0, KEY_ALL_ACCESS, &key) != ERROR_SUCCESS){printf("Inject faile\n");return false;}memcpy((void*)Dllpath, path,_tcslen(path)*2+1);if (RegSetValueEx(key, L"AppInit_DLLs", 0, REG_SZ, Dllpath, (_tcslen(path) + 1)*sizeof(TCHAR))!=ERROR_SUCCESS){printf("set error\n");return false;}DWORD dwValue = 1;if (RegSetValueEx(key, L"LoadAppInit_DLLs", 0, REG_DWORD,(BYTE*)&dwValue, sizeof(dwValue)) != ERROR_SUCCESS){printf("set2 error\n");return false;}return true;}
3.消息钩取
利用windows提供的setWindowsHook()API,我们可以利用其实现DLL注入。他会截取不同的消息,通过你设置的参数,来注入相应的进程。以下是MSDN给出的用法,
HHOOK SetWindowsHookExA( int idHook, //钩子类型,比如键盘输入(WH_KEYBOARD),鼠标点击(WH_MOUSE)等 HOOKPROC lpfn, //钩子调用函数,当截取到想要的消息时,会调用这个函数 HINSTANCE hmod, //dll句柄 DWORD dwThreadId //进程PID,为0表示为全局钩子);
当他监听到你想要的消息类型,会自动将dll注入到相应的进程,并执行相应的函数。
主要代码如下:
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); }}
三、Dll注入实战--扫雷的自动游戏
通过对扫雷程序的逆向研究(这个不属于本次的范围,我就不展开讲了),我们可以找到扫雷实现的一个原理:维护一段内存,并通过这段内存上的值来确定某个位置是炸弹还是空白区域,所以,我们要做的就是通过读取这块内存的值,从而判断某个地方是炸弹还是空白,要不要插旗子。
因为我们知道,dll注入后的dll文件可以访问和操作被注入文件的内存地址,所以我们就可以采用dll注入的方法,把dll文件注入到扫雷程序中去,然后执行一段代码,代替我们去点击方块。
以下是dll文件主要源码:
void _stdcall Kill(){FILE* f = NULL;BYTE Flag;//标志是否有雷int a;char s[10] = { 0 };for (int i = 1; i <= 9; i++){for (int ii = 1; ii <= 9; ii++){if (!ReadProcessMemory(GetCurrentProcess(), (LPCVOID)(0x01005340 + i * 32 + ii), &Flag, 1, NULL))MessageBox(NULL, TEXT("读取失败"), NULL, MB_OK);if (Flag == 0x0f) //0x0f表示没有雷,左键点击{a = ((63 + (i-1) * 16) << 16) + 19 + (ii-1) * 16;PostMessage(FindWindow(NULL, TEXT("扫雷")), WM_LBUTTONDOWN, 0, a);PostMessage(FindWindow(NULL, TEXT("扫雷")), WM_LBUTTONUP, 0, a);//Sleep(1000);}else if (Flag == 0x8f) //0x8f表示有雷,右键插旗子{a = ((63 + (i - 1) * 16) << 16) + 19 + (ii - 1) * 16;PostMessage(FindWindow(NULL, TEXT("扫雷")), WM_RBUTTONDOWN, 0, a);PostMessage(FindWindow(NULL, TEXT("扫雷")), WM_RBUTTONUP, 0, a);//Sleep(1000);}}}}BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ){ switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH:Kill();break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE;}
参考:《逆向工程核心原理》
https://www.cnblogs.com/HsinTsao/p/6541883.html
https://bbs.pediy.com/thread-253918.htm
- END -
TimeKeeper扫码关注,学习更多