代码实现(被注入进程):
private: DWORD m_dwId; HHOOK m_hHook; HMODULE m_hmDll; private: DWORD GetPIdByProcessName(const char* pszProcessName); BOOL InjectDllBySetWindowsHook(ULONG32 ulTargetProcessID,char* pszDllName); DWORD GetThreadID(ULONG32 ulTargetProcessID);
/*获取ID按钮*/ void CInjectDLLDlg::OnBnClickedBtnGetid() { char szProName[MAX_PATH] = {0}; GetDlgItemText(IDC_ED_NAME,szProName,MAX_PATH); if(strstr(szProName,".exe") == NULL) strcat_s(szProName,MAX_PATH * sizeof(char),".exe"); m_dwId = GetPIdByProcessName(szProName); SetDlgItemInt(IDC_ED_ID,(int)m_dwId,FALSE); } /*Windows挂钩DLL注入*/ DWORD CInjectDLLDlg::GetPIdByProcessName(const char* pszProcessName) { DWORD id = 0; //获得系统快照句柄 (得到当前的所有进程) HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0) ; PROCESSENTRY32 pInfo; //用于保存进程信息的一个数据结构 pInfo.dwSize = sizeof(pInfo); //从快照中获取进程列表 Process32First(hSnapShot, &pInfo) ; //从第一个进程开始循环 do { //这里的 pszProcessName 为你的进程名称 if(strcmp(strlwr(_strdup(pInfo.szExeFile)), pszProcessName) == 0) { id = pInfo.th32ProcessID ; break ; } }while(Process32Next(hSnapShot, &pInfo) != FALSE); return id; } /*Windows挂钩Dll注入 按钮*/ void CInjectDLLDlg::OnBnClickedOk() { char szDllName[MAX_PATH] = {0}; GetDlgItemText(IDC_ED_DLLNAME,szDllName,MAX_PATH); BOOL bRet = InjectDllBySetWindowsHook((ULONG32)m_dwId,szDllName); if (bRet) { //MessageBox("注入成功"); } } /*进程注入*/ BOOL CInjectDLLDlg::InjectDllBySetWindowsHook(ULONG32 ulTargetProcessID,char* pszDllName) { HMODULE m_hmDll = LoadLibrary(pszDllName); if (NULL == m_hmDll) { MessageBox("LoadLibraryError!"); return FALSE; } HOOKPROC sub_address = NULL; sub_address = (HOOKPROC)GetProcAddress(m_hmDll,"MyMessageProcess"); if (NULL == sub_address) { MessageBox("GetProcAddressError!"); return FALSE; } DWORD dwThreadID = GetThreadID(ulTargetProcessID); /* 参数1:要安装的挂钩类型 参数2:指定系统调用的窗口消息处理函数 参数3:标示一个包含窗口处理消息函数(参数2)的DLL 参数4:安装挂钩的线程ID */ m_hHook = SetWindowsHookEx(WH_KEYBOARD, sub_address, m_hmDll, dwThreadID); } /*获取进程的主线程ID*/ DWORD CInjectDLLDlg::GetThreadID(ULONG32 ulTargetProcessID) { HANDLE Handle = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); if (Handle != INVALID_HANDLE_VALUE) { THREADENTRY32 te; te.dwSize = sizeof(te); if (Thread32First(Handle, &te)) { do { if (te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID)) { if (te.th32OwnerProcessID == ulTargetProcessID) { HANDLE hThread = OpenThread(READ_CONTROL, FALSE, te.th32ThreadID); if (!hThread) { printf("Couldn't get thread handle\r\n"); } else { return te.th32ThreadID; } } } } while (Thread32Next(Handle, &te)); } } CloseHandle(Handle); return (DWORD)0; } /*退出按钮*/ void CInjectDLLDlg::OnBnClickedCancel() { if(m_hHook) UnhookWindowsHookEx(m_hHook); if(m_hmDll) FreeLibrary(m_hmDll); CDialogEx::OnCancel(); }
Dll:
#ifdef MyDll #else #define MyDll extern "C" __declspec(dllimport) #endif MyDll LRESULT MyMessageProcess(int Code, WPARAM wParam, LPARAM lParam);
#include "stdafx.h" #define MyDll extern "C" __declspec(dllexport) #include "MyDll.h" MyDll LRESULT MyMessageProcess(int Code, WPARAM wParam, LPARAM lParam) { MessageBoxA(NULL, "GetMessage!", "Message", 0); return 0; }
////////////////////////////////////////////////////引用////////////////////////////////////////////////////////////////
用windows钩子将dll注入到其它进程地址空间中,然后做些自己想做的事情,比如搞点破坏什么的,这些都是可以的。关于这节的理论部分不是特别好说,所以,就通过例子以及我在这个过程中遇到的问题来讲述。
一般情况,我们会说windows为每个进程提供了4GB(32位系统)的地址空间,而且每个进程的地址空间是独立的。也就说,进程A是不能访问进程B的地址空间中的内容的。这样做有一个好处,就是安全!但是windows也给我们提供了一点后门,可以让进程A对进程B做点什么……这里我所要讲到的windows钩子就是其方法一种。
windows钩子的原理就是首先设置一个钩子,这个钩子可以是全局的,也可以是针对某个线程的。设置钩子的时候SetWindowHookEx() 可以设置一些属性。说的通俗点,就是你想钩住什么东西,消息?比如:键盘消息、鼠标消息等等。
HHOOK SetWindowsHookEx( int idHook, // hook type 钩子的类型,消息钩子还是键盘钩子
HOOKPROC lpfn, // hook procedure钩子函数地址
HINSTANCE hMod, // handle to application instance钩子所在的dll
DWORD dwThreadId // thread identifier线程ID );
钩子的种类是很多的,这个可以通过MSDN自己查看,第三个参数是钩子函数所在的dll模块,如果钩子函数是要钩当前进程,那么DLL必须为NULL,dwThreadId是要钩住哪个线程,如果其值为0,表示给系统中所有的GUI线程都安装钩子。
为了便于理解,我假定设置的是WH_GETMESSAGE 类型的全局钩子。然后我要截获某个GUI窗口的消息。消息通过GetMessage()之后会派送到相应的WndProc函数。但是为了截获这些消息,我肯定得在消息被送到WndProc之前处理它。由于我们安装了钩子,所以每当有消息产生的时候会调用GetMsgProc函数,然后再通过在GetMsgProc中可以通过SetWindowLongPtr()将窗口子类化,当然,子类窗口的窗口过程必须与GetMsgProc在同一个DLL中。
/////////////////////////Hook.dll/////////////////////////////////// #include HWND g_hWnd=NULL; HHOOK hHook; WNDPROC lpOldProc; HMODULE hDLL; BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD dwReason, LPVOID lpvReserved) { hDLL = hinstDLL; return TRUE; } LRESULT CALLBACK SubclassWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { if ( uMsg ==WM_COMMAND ) { } else if(uMsg ==WM_MOVE) { MessageBox(NULL,TEXT("WM_MOVE msg triggered"),TEXT("提醒"),MB_OK); } return CallWindowProc(lpOldProc,hwnd,uMsg,wParam,lParam); } LRESULT CALLBACK HookProc(int nCode,WPARAM wParam,LPARAM lParam) { static BOOL isFirst = TRUE; if(isFirst) { HWND hWindow=FindWindow(NULL,TEXT("成绩计算器")); g_hWnd=hWindow; if(hWindow==NULL) {isFirst =FALSE;return 0;} else { lpOldProc = (WNDPROC)SetWindowLongPtr(g_hWnd,GWLP_WNDPROC,(LONG_PTR)SubclassWndProc); } } return CallNextHookEx(hHook,nCode, wParam, lParam); } extern"C" __declspec(dllexport) void SetHook() { hHook = SetWindowsHookEx(WH_GETMESSAGE,(HOOKPROC)HookProc,hDLL,0); }这个是我写的程序的一个例子。首先主程序加载该dll,然后调用SetHook函数设置一个全局钩子。然后当有任何消息的时候HookProc会被调用,在HookProc,我们找到想要监听的窗口“成绩计算器”的句柄,然后通过SetWindowLongPtr子类化这个窗口(函数返回值是原窗口过程的地址),这样新的窗口过程处理函数SubclassWndProc就会替代原来的窗口过程。窗口所有消息的处理将有SubclassWndProc完成。
在这里有两点需要注意:
1)在HookProc中,需要进行一个判断。这样确保SetWindowLongPtr只调用一次。我刚开始的时候没有处理这里所以动不动机器就死机了,也出现过每次运行我的主程序,“成绩计算器”窗口就会消失。后来才意识到这个地方会多次调用,当然也得感谢高手的帮助。
2)在SubclassWndProc中必须将不处理的消息还给原来的窗口,也就是必须调用CallWindowProc(lpOldProc,hwnd,uMsg,wParam,lParam);这个应该很好理解。我刚开始时还以为是像着样子调用呢,lpOldProc(hwnd,uMsg,wParam,lParam),这种方式是错误的。
总的来说,windows钩子注入DLL就是对SetWindowsHook,和SetWindowLongPtr的组合使用。但是他的灵活性不好,不如后面我要见到的远程线程注入Dll的方式灵活。