敢说敢作敢为, 无怨无恨无悔。这篇文章主要讲述C++ HOOK 指定进程的指定 API(MessageBoxA 为例)(最简单)相关的知识,希望能为你提供帮助。
??这篇文章只是基于我之前的全局 HOOK 的修改,要看全局 HOOK点这里??
虽然全局 HOOK 很给力,但是越到后来越发现这个东西除了可以
以外,实际上用途并没有想象中的广泛。相反,对于制定进程的指定 API 的 HOOK 却非常实用,所以就把以前的代码精简一下,去掉它浮夸的外衣,以最少的代码,实现最基本的 HOOK 功能 。
dll 代码:
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include <
iostream>
#include <
Windows.h>
#include <
tlhelp32.h>
#include <
process.h>
#pragma
HANDLE hProcess=NULL;
// 进程句柄
BOOL bIsInjected=FALSE;
// 是否注入完成
typedef int (WINAPI *MsgBoxA)(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType);
// 声明一个别名 MsgBoxA
MsgBoxA oldMsgBoxA=NULL;
// 保存原函数地址
FARPROC pfMsgBoxA=NULL;
// 指向原函数地址的远指针
BYTE OldCodeA[5];
// 老的系统API入口代码
BYTE NewCodeA[5];
// 要跳转的API代码 (jmp xxxx)
int WINAPI MyMessageBoxA(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType);
// 我们自己的 MessageBoxA 函数
#pragma
#pragma
// 开启钩子(修改 API 头 5 个字节)
void HookOn()
DWORD dwTemp = 0,// 修改后的内存保护属性
dwOldProtect,// 之前的内存保护属性
dwRet = 0;
// 内存写入成功标志,0不成功、1成功
SIZE_T dwWrite;
// 写入进程内存的字节数
// 更改虚拟内存保护
VirtualProtectEx(
hProcess,// 进程句柄
pfMsgBoxA,// 指向保护区域地址的指针
5,// 要更改的区域的字节大小
PAGE_READWRITE,// 内存保护类型,PAGE_READWRITE:可读可写
&
dwOldProtect// 接收原来的内存保护属性
);
// 判断是否成功写入内存
dwRet = WriteProcessMemory(
hProcess,// 进程句柄
pfMsgBoxA,// 指向写入地址的指针
NewCodeA,// 指向存放写入内容的缓冲区指针
5,// 写入字节数
&
dwWrite// 接收传输到进程中的字节数
);
if (0==dwRet||0==dwWrite)
MessageBoxW(NULL,L"NewCodeA 写入失败",L"",NULL);
// 记录日志信息
// 恢复内存保护状态
VirtualProtectEx(hProcess,pfMsgBoxA,5,dwOldProtect,&
dwTemp);
// 关闭钩子(修改 API 头 5 个字节)
void HookOff()
DWORD dwTemp = 0,// 修改后的内存保护属性
dwOldProtect = 0,// 之前的内存保护属性
dwRet = 0;
// 内存写入成功标志,0不成功、1成功
SIZE_T dwWrite;
// 写入进程内存的字节数
// 更改虚拟内存保护
VirtualProtectEx(
hProcess,// 进程句柄
pfMsgBoxA,// 指向保护区域地址的指针
5,// 要更改的区域的字节大小
PAGE_READWRITE,// 内存保护类型,PAGE_READWRITE:可读可写
&
dwOldProtect// 接收原来的内存保护属性
);
dwRet = WriteProcessMemory(
hProcess,// 进程句柄
pfMsgBoxA,// 指向写入地址的指针
OldCodeA,// 指向存放写入内容的缓冲区指针
5,// 写入字节数
&
dwWrite// 接收传输到进程中的字节数
);
if (0==dwRet||0==dwWrite)
MessageBoxW(NULL,L"OldCodeA 写入失败",L"",NULL);
// 记录日志信息
// 恢复内存保护状态
VirtualProtectEx(hProcess,pfMsgBoxA,5,dwOldProtect,&
dwTemp);
// 代码注入
void Inject()
// 如果还没有注入
if (!bIsInjected)
//保证只调用1次
bIsInjected=TRUE;
// 获取函数地址
HMODULE hmod=::LoadLibrary(L"User32.dll");
oldMsgBoxA=(MsgBoxA)::GetProcAddress(hmod,"MessageBoxA");
// 原 MessageBoxA 地址
pfMsgBoxA=(FARPROC)oldMsgBoxA;
// 指向原 MessageBoxA 地址的指针
// 指针为空则结束运行
if (pfMsgBoxA==NULL)MessageBox(NULL,L"cannot get MessageBoxA()",L"error",0);
return;
// 将原API中的入口代码保存入 OldCodeA[]
_asm
lea edi,OldCodeA;
把 OldCodeA 的地址给 edi
mov esi,pfMsgBoxA;
把 MessageBoxA 的地址给 esi
cld;
方向标志位复位
movsd;
复制双子
movsb;
复制字节
// 将原 API 第一个字节改为 jmp
NewCodeA[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
// 开始 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;
#pragma
BOOL APIENTRY DllMain( HMODULE hModule,
DWORDul_reason_for_call,
LPVOID lpReserved
)
// 获取调用 dll 的进程 ID
DWORD dwPid = ::GetCurrentProcessId();
switch (ul_reason_for_call)
case DLL_PROCESS_ATTACH:
// 获取调用 dll 的进程句柄
hProcess = ::OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);
// 开始注入
Inject();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
return TRUE;
好吧,这已经是最短的代码量了。下面演示下效果:
【C++ HOOK 指定进程的指定 API(MessageBoxA 为例)(最简单)】
推荐阅读
- 源文件与模块生成时的文件不同。是否希望调试器使用它()
- Windows 10 系统进入测试模式命令
- markdown 实现代码折叠效果
- Python中hashlib模块的使用
- Android系统自带的android.util.Base64的实现源码
- 驱动实现强制重启与关机操作
- Java中的线程
- Java中的结构语句
- Java中的接口