全局 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);// 同上,操作剩下的 MessageBoxWVirtualProtectEx(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); // 同上,操作剩下的 MessageBoxWVirtualProtectEx(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 的地址给 edimov esi,pfMsgBoxA      ; 把 MessageBoxA 的地址给 esicld                     ; 方向标志位复位movsd                  ; 复制双子movsb                 ; 复制字节}_asm { lea edi,OldCodeW      ; 以相同的方式操作 MessageBoxWmov esi,pfMsgBoxWcld movsd movsb }// 将原 API 第一个字节改为 jmpNewCodeA[0]=0xe9;         NewCodeW[0]=0xe9;          // 计算 jmp 后面要跟的地址_asm { lea eax,MyMessageBoxA               ; 将 MyMessageBoxA 的地址给 eaxmov ebx,pfMsgBoxA                 ; 将 MessageBoxA 的地址给 ebxsub eax,ebx                         ; 计算 jmp 后面要跟的地址sub eax,5 mov dword ptr [NewCodeA+1],eax } _asm { lea eax,MyMessageBoxW                ; 以相同的方式操作 MessageBoxWmov ebx,pfMsgBoxWsub eax,ebx sub eax,5 mov dword ptr [NewCodeW+1],eax } // 开始 HookHookOn(); }
}// 假 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_DllmfcAppBEGIN_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 的进程 IDDWORD dwPid = ::GetCurrentProcessId();// 获取调用 dll 的进程句柄hProcess = ::OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);// 开始注入Inject();#pragma endregionreturn TRUE;
}// 程序出口
int CTest_DllmfcApp::ExitInstance()
{// TODO: 在此添加专用代码和/或调用基类#pragma region 我的代码// 恢复其他进程的的 API HookOff();#pragma endregionreturn 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()
{// 这里只恢复了自身 APIHookOff();if (hhk!=NULL){// UnHook 鼠标钩子UnhookWindowsHookEx(hhk);// 卸载 dllFreeLibrary(hInst);}
}#pragma endregion

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

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

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

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

HINSTANCE g_hInst;     // 全局变量,同 HMODULEvoid 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 中的导出函数 StartHookStartHook Hook = (StartHook)::GetProcAddress(g_hInst,"StartHook");// 判断导出函数是否调用成功if (Hook==NULL){AfxMessageBox(_T("StartHook 调用失败"));}// 开始 HookHook(m_hWnd);
}
void CTest_MFCDlg::UNHOOK()
{// TODO: 在此添加控件通知处理程序代码// 检查是否需要 UnHookif (g_hInst==NULL){return;}// 声明别名typedef VOID (WINAPI* StopHook)();// 调用 dll 中的导出函数 StopHookStopHook UnHook = (StopHook)::GetProcAddress(g_hInst,"StopHook");// 判断导出函数是否调用成功if (UnHook==NULL){AfxMessageBox(_T("StopHook 调用失败"));return;}// 开始 UnHookUnHook();// 卸载 dllFreeLibrary(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);
}

效果图:

C/C++ HOOK 全局 API相关推荐

  1. Vue源码探究-全局API

    Vue源码探究-全局API 本篇代码位于vue/src/core/global-api/ Vue暴露了一些全局API来强化功能开发,API的使用示例官网上都有说明,无需多言.这里主要来看一下全局API ...

  2. Vue官方文档梳理-全局API

    Vue.extend 配置项data必须为function,否则配置无效.data的合并规则(可以看<Vue官方文档梳理-全局配置>)源码如下: 传入非function类型的data(上图 ...

  3. vue怎么把api 挂载到全局_深入理解Vue官方文档梳理之全局API

    Vue.extend 配置项data必须为function,否则配置无效.data的合并规则(可以看<Vue官方文档梳理-全局配置>)源码如下: 传入非function类型的data(上图 ...

  4. HOOK Windows API

    HOOK Windows API 只能HOOK 本地的API,适用于入门学习 HOOK API的一般步骤: 1.定义假API函数 假API函数,除了函数名称和真API不一样之外,其它的都要跟真API的 ...

  5. Vue 实例之全局API,实例属性,全局配置,组件进阶

    文章目录 写在前面 Vue 全局 API Vue.directive Vue.use Vue.extend Vue.set Vue.mixin Vue 实例属性 vm.$props vm.$optio ...

  6. 深入浅出Vue.js阅读——整体流程——实例方法与全局API的实现原理

    深入浅出Vue.js阅读--整体流程--实例方法与全局API的实现原理 1. 数据相关的实例方法 2. 事件相关的实例方法 1. vm.$on 2. vm.$off 3. vm.$once 4. vm ...

  7. hook android api伪造设备信息做刷量

    hook android api伪造设备信息做刷量 概述 Android平台上app的统计数据都是基于Android的设备信息的,比如首次使用(激活),活跃(日活跃DAU,月活跃MAU)等都需要根据设 ...

  8. 13个Vue3中的全局API的源码浅析汇总整理

    前言 不知不觉vue-next的版本已经来到了3.1.2,最近对照着源码学习vue3的全局Api,边学习边整理了下来,希望可以和大家一起进步. 我们以官方定义.用法.源码浅析三个维度来一起看看它们.下 ...

  9. java hook全局钩子_钩子(hook)

    钩子(hook)编程 一.钩子介绍 1.1钩子的实现机制 钩子英文名叫Hook,是一种截获windows系统中某应用程序或者所有进程的消息的一种技术.下图是windows应用程序传递消息的过程: 如在 ...

最新文章

  1. Go 学习笔记(66)— Go 并发同步原语(sync.Mutex、sync.RWMutex、sync.Once)
  2. 「跨域」利用node.js实践前端各种跨域方式(上)
  3. CODEFORCES 484E Sign on Fence
  4. python可以写桌面软件吗-用Python编写一个桌面软件系统的步骤是什么?
  5. 子进程会继承父进程的哪些内容_【学习教程】Node.js创建子进程方法
  6. 求最长回文串-从动态规划到马拉车之路(下)
  7. 2021年商业地产趋势洞察白皮书
  8. sobel prewitt算法 模板加权模糊的解释 + 两类边缘下的二阶导数值
  9. java接收前台tex格式t数据_java 下载文件时,设置response.setContentType 根据文件类型...
  10. 使用回调技术实现局部刷新
  11. Spring Boot 2 快速教程:WebFlux 集成 Mongodb(四)
  12. .Net Attribute特性
  13. 使用SQL Server日志传送将SQL数据库移动到其他服务器
  14. 和发光的人在一起,慢慢地你也会发光
  15. Linux系统管理员应该知道的20个系统监控工具
  16. NVR录像机 人机界面鼠标光标消失如何解决
  17. 解决ubuntu下微信不能发图片的问题。
  18. 不能不知道的分布式基础理论
  19. 移植安装ModBus到ARM开发板
  20. 【yolo5】目标检测数据集制作

热门文章

  1. 江苏教育考试院官网显示服务器联系不上,浙江教育考试网进不去怎么回?
  2. php批量生成百度加密地址,用php改写百度mp3地址的加密模式
  3. 《毛毛虫团队》第四次作业:基于原型的团队项目需求调研与分析
  4. 论文笔记 | The effect of tax avoidance crackdown on corporate innovation
  5. python用matplotlib画雷达图_python使用matplotlib绘制雷达图
  6. Flowable集成钉钉实现抄送发送消息
  7. C语言及程序设计 实践参考——个人所得税计算器if语句版
  8. crc 16 1021 java_【软件升级】CCITT标准CRC16(1021)算法 Java代码【转】
  9. Linux下轻松刻录CD-ROM数据光盘
  10. 《SICP》习题第3章(施工中)