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);// 同上,操作剩下的 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相关推荐
- Vue源码探究-全局API
Vue源码探究-全局API 本篇代码位于vue/src/core/global-api/ Vue暴露了一些全局API来强化功能开发,API的使用示例官网上都有说明,无需多言.这里主要来看一下全局API ...
- Vue官方文档梳理-全局API
Vue.extend 配置项data必须为function,否则配置无效.data的合并规则(可以看<Vue官方文档梳理-全局配置>)源码如下: 传入非function类型的data(上图 ...
- vue怎么把api 挂载到全局_深入理解Vue官方文档梳理之全局API
Vue.extend 配置项data必须为function,否则配置无效.data的合并规则(可以看<Vue官方文档梳理-全局配置>)源码如下: 传入非function类型的data(上图 ...
- HOOK Windows API
HOOK Windows API 只能HOOK 本地的API,适用于入门学习 HOOK API的一般步骤: 1.定义假API函数 假API函数,除了函数名称和真API不一样之外,其它的都要跟真API的 ...
- Vue 实例之全局API,实例属性,全局配置,组件进阶
文章目录 写在前面 Vue 全局 API Vue.directive Vue.use Vue.extend Vue.set Vue.mixin Vue 实例属性 vm.$props vm.$optio ...
- 深入浅出Vue.js阅读——整体流程——实例方法与全局API的实现原理
深入浅出Vue.js阅读--整体流程--实例方法与全局API的实现原理 1. 数据相关的实例方法 2. 事件相关的实例方法 1. vm.$on 2. vm.$off 3. vm.$once 4. vm ...
- hook android api伪造设备信息做刷量
hook android api伪造设备信息做刷量 概述 Android平台上app的统计数据都是基于Android的设备信息的,比如首次使用(激活),活跃(日活跃DAU,月活跃MAU)等都需要根据设 ...
- 13个Vue3中的全局API的源码浅析汇总整理
前言 不知不觉vue-next的版本已经来到了3.1.2,最近对照着源码学习vue3的全局Api,边学习边整理了下来,希望可以和大家一起进步. 我们以官方定义.用法.源码浅析三个维度来一起看看它们.下 ...
- java hook全局钩子_钩子(hook)
钩子(hook)编程 一.钩子介绍 1.1钩子的实现机制 钩子英文名叫Hook,是一种截获windows系统中某应用程序或者所有进程的消息的一种技术.下图是windows应用程序传递消息的过程: 如在 ...
最新文章
- Go 学习笔记(66)— Go 并发同步原语(sync.Mutex、sync.RWMutex、sync.Once)
- 「跨域」利用node.js实践前端各种跨域方式(上)
- CODEFORCES 484E Sign on Fence
- python可以写桌面软件吗-用Python编写一个桌面软件系统的步骤是什么?
- 子进程会继承父进程的哪些内容_【学习教程】Node.js创建子进程方法
- 求最长回文串-从动态规划到马拉车之路(下)
- 2021年商业地产趋势洞察白皮书
- sobel prewitt算法 模板加权模糊的解释 + 两类边缘下的二阶导数值
- java接收前台tex格式t数据_java 下载文件时,设置response.setContentType 根据文件类型...
- 使用回调技术实现局部刷新
- Spring Boot 2 快速教程:WebFlux 集成 Mongodb(四)
- .Net Attribute特性
- 使用SQL Server日志传送将SQL数据库移动到其他服务器
- 和发光的人在一起,慢慢地你也会发光
- Linux系统管理员应该知道的20个系统监控工具
- NVR录像机 人机界面鼠标光标消失如何解决
- 解决ubuntu下微信不能发图片的问题。
- 不能不知道的分布式基础理论
- 移植安装ModBus到ARM开发板
- 【yolo5】目标检测数据集制作
热门文章
- 江苏教育考试院官网显示服务器联系不上,浙江教育考试网进不去怎么回?
- php批量生成百度加密地址,用php改写百度mp3地址的加密模式
- 《毛毛虫团队》第四次作业:基于原型的团队项目需求调研与分析
- 论文笔记 | The effect of tax avoidance crackdown on corporate innovation
- python用matplotlib画雷达图_python使用matplotlib绘制雷达图
- Flowable集成钉钉实现抄送发送消息
- C语言及程序设计 实践参考——个人所得税计算器if语句版
- crc 16 1021 java_【软件升级】CCITT标准CRC16(1021)算法 Java代码【转】
- Linux下轻松刻录CD-ROM数据光盘
- 《SICP》习题第3章(施工中)