• C/C++ HOOK 全局 API


    全局 Hook 不一定需要用到 Dll ,比如全局的鼠标钩子、键盘钩子都是不需要 Dll 的,但是要钩住 API,就需要 Dll 的协助了,下面直接放上 Dll 的代码:(注意这里使用的是 MFC DLL)

    // Test_Dll(mfc).cpp : 定义 DLL 的初始化例程。
    //
    
    #include "stdafx.h"
    #include "Test_Dll(mfc).h"
    
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    
    #pragma region 我的代码
    
    #define  UM_WNDTITLE WM_USER+100	// 自定义消息(私有窗口类的消息标识符)
    
    // 全局共享变量(多进程之间共享数据)
    #pragma data_seg(".Share")
    	HWND g_hWnd = NULL;				// 主窗口句柄
    	HHOOK hhk = NULL;				// 鼠标钩子句柄
    	HINSTANCE hInst = NULL;			// 本dll实例句柄
    #pragma data_seg()
    #pragma comment(linker, "/section:.Share,rws")
    
    
    // 全局变量
    HANDLE hProcess=NULL;				// 进程句柄
    BOOL bIsInjected=FALSE;				// 是否注入完成
    typedef int (WINAPI *MsgBoxA)(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType);			// 声明一个别名 MsgBoxA
    typedef int (WINAPI *MsgBoxW)(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType);		// 声明一个别名 MsgBoxW
    MsgBoxA oldMsgBoxA=NULL;			// 保存原函数地址
    MsgBoxW oldMsgBoxW=NULL;			// 保存原函数地址
    FARPROC pfMsgBoxA=NULL;				// 指向原函数地址的远指针
    FARPROC pfMsgBoxW=NULL;				// 指向原函数地址的远指针
    BYTE OldCodeA[5];					// 老的系统API入口代码
    BYTE NewCodeA[5];					// 要跳转的API代码 (jmp xxxx)
    BYTE OldCodeW[5];					// 老的系统API入口代码
    BYTE NewCodeW[5];					// 要跳转的API代码 (jmp xxxx)
    int WINAPI MyMessageBoxA(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType);				// 我们自己的 MessageBoxA 函数
    int WINAPI MyMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType);			// 我们自己的 MessageBoxW 函数
    
    
    // 开启钩子(修改 API 头 5 个字节)
    void HookOn() 
    { 
    	// 检验进程句柄是否为空
    	ASSERT(hProcess!=NULL);
    
    	DWORD dwTemp = 0,		// 修改后的内存保护属性
    		dwOldProtect,		// 之前的内存保护属性
    		dwRet = 0,			// 内存写入成功标志,0不成功、1成功
    		dwWrite;			// 写入进程内存的字节数
     
    	// 更改虚拟内存保护
    	VirtualProtectEx(		
    		hProcess,			// 进程句柄
    		pfMsgBoxA,			// 指向保护区域地址的指针
    		5,					// 要更改的区域的字节大小
    		PAGE_READWRITE,		// 内存保护类型,PAGE_READWRITE:可读可写
    		&dwOldProtect		// 接收原来的内存保护属性
    		); 
    
    	// 判断是否成功写入内存
    	dwRet = WriteProcessMemory(
    		hProcess,			// 进程句柄
    		pfMsgBoxA,			// 指向写入地址的指针
    		NewCodeA,			// 指向存放写入内容的缓冲区指针
    		5,					// 写入字节数
    		&dwWrite			// 接收传输到进程中的字节数
    		);
    	if (0==dwRet||0==dwWrite){
    		TRACE("NewCodeA 写入失败");	// 记录日志信息
    	}	
    
    	// 恢复内存保护状态
    	VirtualProtectEx(hProcess,pfMsgBoxA,5,dwOldProtect,&dwTemp);
    
    	// 同上,操作剩下的 MessageBoxW
    	VirtualProtectEx(hProcess,pfMsgBoxW,5,PAGE_READWRITE,&dwOldProtect); 
    	dwRet=WriteProcessMemory(hProcess,pfMsgBoxW,NewCodeW,5,&dwWrite);
    	if (0==dwRet||0==dwWrite){TRACE("NewCodeW 写入失败");}
    	VirtualProtectEx(hProcess,pfMsgBoxW,5,dwOldProtect,&dwTemp);
    }
    
    // 关闭钩子(修改 API 头 5 个字节)
    void HookOff()
    { 
    	// 检验进程句柄是否为空
    	ASSERT(hProcess!=NULL);
    
    	DWORD dwTemp = 0,			// 修改后的内存保护属性
    		dwOldProtect = 0,		// 之前的内存保护属性
    		dwRet = 0,				// 内存写入成功标志,0不成功、1成功
    		dwWrite = 0;			// 写入进程内存的字节数
    
    	// 更改虚拟内存保护
    	VirtualProtectEx(
    		hProcess,				// 进程句柄
    		pfMsgBoxA,				// 指向保护区域地址的指针
    		5,						// 要更改的区域的字节大小
    		PAGE_READWRITE,			// 内存保护类型,PAGE_READWRITE:可读可写
    		&dwOldProtect			// 接收原来的内存保护属性
    		); 
    
    	dwRet = WriteProcessMemory(
    		hProcess,				// 进程句柄
    		pfMsgBoxA,				// 指向写入地址的指针
    		OldCodeA,				// 指向存放写入内容的缓冲区指针
    		5,						// 写入字节数
    		&dwWrite				// 接收传输到进程中的字节数
    		); 
    	if (0==dwRet||0==dwWrite){
    		TRACE("OldCodeA 写入失败");	// 记录日志信息
    	}
    
    	// 恢复内存保护状态
    	VirtualProtectEx(hProcess,pfMsgBoxA,5,dwOldProtect,&dwTemp); 
    
    	// 同上,操作剩下的 MessageBoxW
    	VirtualProtectEx(hProcess,pfMsgBoxW,5,PAGE_READWRITE,&dwOldProtect); 
    	WriteProcessMemory(hProcess,pfMsgBoxW,OldCodeW,5,&dwWrite); 
    	if (0==dwRet||0==dwWrite){TRACE("OldCodeW 写入失败");}
    	VirtualProtectEx(hProcess,pfMsgBoxW,5,dwOldProtect,&dwTemp);  
    }
    
    // 代码注入
    void Inject()
    {
    	// 如果还没有注入
    	if (!bIsInjected){ 
    
    		//保证只调用1次
    		bIsInjected=TRUE;
    
    		// 获取函数地址
    		HMODULE hmod=::LoadLibrary(_T("User32.dll"));
    		oldMsgBoxA=(MsgBoxA)::GetProcAddress(hmod,"MessageBoxA");	// 原 MessageBoxA 地址
    		pfMsgBoxA=(FARPROC)oldMsgBoxA;								// 指向原 MessageBoxA 地址的指针
    		oldMsgBoxW=(MsgBoxW)::GetProcAddress(hmod,"MessageBoxW");	// 原 MessageBoxW 地址
    		pfMsgBoxW=(FARPROC)oldMsgBoxW;								// 指向原 MessageBoxW 地址的指针
      
    		// 指针为空则结束运行
    		if (pfMsgBoxA==NULL){MessageBox(NULL,_T("cannot get MessageBoxA()"),_T("error"),0);return;}
    		if (pfMsgBoxW==NULL){MessageBox(NULL,_T("cannot get MessageBoxW()"),_T("error"),0);return;}
    
    		// 将原API中的入口代码保存入 OldCodeA[],OldCodeW[]
    		_asm 
    		{ 
    			lea edi,OldCodeA		; 把 OldCodeA 的地址给 edi
    			mov esi,pfMsgBoxA		; 把 MessageBoxA 的地址给 esi
    			cld						; 方向标志位复位
    			movsd					; 复制双子
    			movsb					; 复制字节
    		}
    		_asm 
    		{ 
    			lea edi,OldCodeW		; 以相同的方式操作 MessageBoxW
    			mov esi,pfMsgBoxW
    			cld 
    			movsd 
    			movsb 
    		}
    
    		// 将原 API 第一个字节改为 jmp
    		NewCodeA[0]=0xe9;			
    		NewCodeW[0]=0xe9;			
    
    		// 计算 jmp 后面要跟的地址
    		_asm 
    		{ 
    			lea eax,MyMessageBoxA				; 将 MyMessageBoxA 的地址给 eax
    			mov ebx,pfMsgBoxA					; 将 MessageBoxA 的地址给 ebx
    			sub eax,ebx							; 计算 jmp 后面要跟的地址
    			sub eax,5 
    			mov dword ptr [NewCodeA+1],eax 
    		} 
    		_asm 
    		{ 
    			lea eax,MyMessageBoxW				; 以相同的方式操作 MessageBoxW
    			mov ebx,pfMsgBoxW
    			sub eax,ebx 
    			sub eax,5 
    			mov dword ptr [NewCodeW+1],eax 
    		} 
     
    		// 开始 Hook
    		HookOn(); 
    	}
    }
    
    // 假 MessageBoxA
    int WINAPI MyMessageBoxA(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType)
    {
    	int nRet = 0;		
    
    	// 先恢复 Hook,不然会造成死循环
    	HookOff();
    
    	// 检验 MessageBoxA 是否失败(失败返回 0)
    	nRet = ::MessageBoxA(hWnd,"Hook MessageBoxA",lpCaption,uType);
    	//nRet=::MessageBoxA(hWnd,lpText,lpCaption,uType);	// 调用原函数(如果你想暗箱操作的话)
    
    	// 再次 HookOn,否则只生效一次
    	HookOn();
    
    	return nRet;
    }
    
    // 假 MessageBoxW
    int WINAPI MyMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType)
    {
    	int nRet = 0;
    
    	// 先恢复 Hook,不然会造成死循环
    	HookOff();
    
    	// 检验 MessageBoxW 是否失败(失败返回 0)
    	nRet = ::MessageBoxW(hWnd,_T("Hook MessageBoxW"),lpCaption,uType);
    	//nRet=::MessageBoxW(hWnd,lpText,lpCaption,uType);	// 调用原函数(如果你想暗箱操作的话)
    
    	// 再次 HookOn,否则只生效一次
    	HookOn();
    
    	return nRet;
    }
    #pragma endregion
    
    #pragma region 忽略掉
    //
    //TODO: 如果此 DLL 相对于 MFC DLL 是动态链接的,
    //		则从此 DLL 导出的任何调入
    //		MFC 的函数必须将 AFX_MANAGE_STATE 宏添加到
    //		该函数的最前面。
    //
    //		例如:
    //
    //		extern "C" BOOL PASCAL EXPORT ExportedFunction()
    //		{
    //			AFX_MANAGE_STATE(AfxGetStaticModuleState());
    //			// 此处为普通函数体
    //		}
    //
    //		此宏先于任何 MFC 调用
    //		出现在每个函数中十分重要。这意味着
    //		它必须作为函数中的第一个语句
    //		出现,甚至先于所有对象变量声明,
    //		这是因为它们的构造函数可能生成 MFC
    //		DLL 调用。
    //
    //		有关其他详细信息,
    //		请参阅 MFC 技术说明 33 和 58。
    //
    
    // CTest_DllmfcApp
    
    BEGIN_MESSAGE_MAP(CTest_DllmfcApp, CWinApp)
    END_MESSAGE_MAP()
    
    
    // CTest_DllmfcApp 构造
    
    CTest_DllmfcApp::CTest_DllmfcApp()
    {
    	// TODO: 在此处添加构造代码,
    	// 将所有重要的初始化放置在 InitInstance 中
    }
    
    
    // 唯一的一个 CTest_DllmfcApp 对象
    
    CTest_DllmfcApp theApp;
    #pragma endregion
    
    // 程序入口
    BOOL CTest_DllmfcApp::InitInstance()
    {
    	CWinApp::InitInstance();
    
    	#pragma region 我的代码
    	
    	// 获取 dll 自身实例句柄
    	hInst = AfxGetInstanceHandle();
    
    	// 获取调用 dll 的进程 ID
    	DWORD dwPid = ::GetCurrentProcessId();
    	
    	// 获取调用 dll 的进程句柄
    	hProcess = ::OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);
    
    	// 开始注入
    	Inject();
    
    	#pragma endregion
    
    	return TRUE;
    }
    
    // 程序出口
    int CTest_DllmfcApp::ExitInstance()
    {
    	// TODO: 在此添加专用代码和/或调用基类
    
    	#pragma region 我的代码
    	
    	// 恢复其他进程的的 API 
    	HookOff();
    
    	#pragma endregion
    
    	return CWinApp::ExitInstance();
    }
    
    #pragma region 我的代码
    
    // 鼠标钩子回调
    LRESULT CALLBACK MouseProc(
    						   int nCode,      // 钩子代号
    						   WPARAM wParam,  // 消息标识符
    						   LPARAM lParam   // 光标的坐标
    						   ){
    	if (nCode==HC_ACTION){
    		
    		// 将钩子所在窗口句柄发给主程序
    		::SendMessage(
    			g_hWnd,											// 接收消息的窗口句柄
    			UM_WNDTITLE,									// 发送的消息
    			wParam,											// 附加消息信息
    			(LPARAM)(((PMOUSEHOOKSTRUCT)lParam)->hwnd)		// 附加消息信息,此处为鼠标所在窗口的窗口句柄
    			);
    		/* 
    		typedef struct tagMOUSEHOOKSTRUCT { // 传递给 WH_MOUSE 的鼠标事件信息结构体
    			POINT     pt;					// 光标的 xy 坐标
    			HWND      hwnd;					// 光标对应的窗口句柄
    			UINT      wHitTestCode;			// 是否击中
    			ULONG_PTR dwExtraInfo;			// 消息关联
    		} MOUSEHOOKSTRUCT, *PMOUSEHOOKSTRUCT, *LPMOUSEHOOKSTRUCT;
    		*/
    	}
    
    	// 讲消息传递给下一个钩子
    	return CallNextHookEx(
    		hhk,		// 钩子句柄,此处为鼠标钩子
    		nCode,		
    		wParam,
    		lParam
    		);
    }
    
    // 安装钩子
    BOOL WINAPI StartHook(HWND hWnd)
    {
    	// 获取鼠标所在的主窗口句柄
    	g_hWnd = hWnd;
    	
    	// 获取鼠标钩子句柄
    	hhk = ::SetWindowsHookEx(
    		WH_MOUSE,		// 钩子类型
    		MouseProc,		// 指向回调函数的指针
    		hInst,			// dll句柄,这里为本 dll 的实例句柄
    		NULL			// 表示与所在桌面的所有线程相关联
    		);
    	
    	// 判断 SetWindowsHookEx 是否执行成功
    	if (hhk==NULL){return FALSE;} 
    	else{return TRUE;}
    }
    
    // 卸载钩子
    VOID WINAPI StopHook()
    {
    	// 这里只恢复了自身 API
    	HookOff();
    
    	
    	if (hhk!=NULL)
    	{
    		// UnHook 鼠标钩子
    		UnhookWindowsHookEx(hhk);
    
    		// 卸载 dll
    		FreeLibrary(hInst);
    	}
    }
    
    #pragma endregion
    

    因为这里没法使用代码折叠,所以不太直观,我放一张折叠后的图:

    在 .def 文件中添加导出函数:(一般就在 .cpp 文件的下面)

    ; Test_Dll(mfc).def : 声明 DLL 的模块参数。
    
    LIBRARY
    
    EXPORTS
    StartHook
    StopHook
        ; 此处可以是显式导出
    

    然后开始写调用 Dll 的代码:(这里要用 MFC 项目,因为全局鼠标钩子需要用到 CWnd 中的 m_hWnd)
    由于我认为大部分的全局 HOOK 需要在隐藏自己然后默默执行,这与 MFC 的窗口交互模式风格相冲突,所以我在这里隐藏了 MFC 的窗口,

    具体方法可以参考:https://blog.csdn.net/Simon798/article/details/99063945

    HINSTANCE g_hInst;		// 全局变量,同 HMODULE
    
    void CTest_MFCDlg::HOOK()
    {
    	// TODO: 在此添加控件通知处理程序代码
    
    	// 加载 dll(需要根据自己 dll 的实际路径而定,建议使用相对路径)
    	g_hInst = ::LoadLibrary(_T("E:\MyFiles\Programing\vs2012\MyPrograms\Test_Dll(mfc)\Debug\Test_Dll(mfc).dll"));
    
    	// 判断是否加载成功
    	if (g_hInst==NULL){AfxMessageBox(_T("加载 dll 失败"));}
    
    	// 声明别名
    	typedef BOOL (WINAPI* StartHook)(HWND hWnd);
    
    	// 调用 dll 中的导出函数 StartHook
    	StartHook Hook = (StartHook)::GetProcAddress(g_hInst,"StartHook");
    
    	// 判断导出函数是否调用成功
    	if (Hook==NULL){AfxMessageBox(_T("StartHook 调用失败"));}
    
    	// 开始 Hook
    	Hook(m_hWnd);
    }
    void CTest_MFCDlg::UNHOOK()
    {
    	// TODO: 在此添加控件通知处理程序代码
    	
    	// 检查是否需要 UnHook
    	if (g_hInst==NULL){return;}
    
    	// 声明别名
    	typedef VOID (WINAPI* StopHook)();
    
    	// 调用 dll 中的导出函数 StopHook
    	StopHook UnHook = (StopHook)::GetProcAddress(g_hInst,"StopHook");
    	
    	// 判断导出函数是否调用成功
    	if (UnHook==NULL){AfxMessageBox(_T("StopHook 调用失败"));return;}
    
    	// 开始 UnHook
    	UnHook();
    
    	// 卸载 dll
    	FreeLibrary(g_hInst);
    
    	// 重置 g_hInst , 方便下一次 UnHook 时判断
    	g_hInst=NULL;
    }
    
    // 窗体创建事件
    int CTest_MFCDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
    	if (CDialogEx::OnCreate(lpCreateStruct) == -1)
    		return -1;
    
    	// TODO:  在此添加您专用的创建代码
    	
    	// 程序被打开时,执行 HOOK() 函数。这里就不演示 UNHOOK 了。
    	HOOK();
    }
    
    void CTest_MFCDlg::OnWindowPosChanging(WINDOWPOS* lpwndpos)
    {
    	CDialogEx::OnWindowPosChanging(lpwndpos);
    
    	// TODO: 在此处添加消息处理程序代码
    
    	// 隐藏窗体
    	lpwndpos->flags &= ~SWP_SHOWWINDOW;
        CDialog::OnWindowPosChanging(lpwndpos);
    }
    

    效果图:


    版权声明: 本博客,文章与代码均为学习时整理的笔记,博客中除去明确标注有参考文献的文章,其他文章【均为原创】作品,转载请务必【添加出处】,您添加出处是我创作的动力!

    警告:如果您恶意转载本人文章,则您的整站文章,将会变为我的原创作品,请相互尊重!
  • 相关阅读:
    Building Apache Thrift on CentOS 6.5
    ToStringBuilder 学习
    对List中对象的去重
    MyEclipse启动Tomcat服务器时老是跳到Debug调试上
    JS 实现点击展开菜单
    详解公钥、私钥、数字证书的概念 转载
    eclipse svn 忽略 target目录 等等... 我用的后边的方法 (转载)
    Log4j XML 配置
    JS完成改变新闻字体大中小的显示
    Javascript 简单学习
  • 原文地址:https://www.cnblogs.com/LyShark/p/15019792.html
Copyright © 2020-2023  润新知