C++ HOOK 指定进程的指定 API(MessageBoxA 为例)(最简单)

Posted mb62d3c286f15ed

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ HOOK 指定进程的指定 API(MessageBoxA 为例)(最简单)相关的知识,希望对你有一定的参考价值。

​这篇文章只是基于我之前的全局 HOOK 的修改,要看全局 HOOK点这里​

虽然全局 HOOK 很给力,但是越到后来越发现这个东西除了可以

C++


以外,实际上用途并没有想象中的广泛。相反,对于制定进程的指定 API 的 HOOK 却非常实用,所以就把以前的代码精简一下,去掉它浮夸的外衣,以最少的代码,实现最基本的 HOOK 功能 。

C++

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,
DWORD ul_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++



以上是关于C++ HOOK 指定进程的指定 API(MessageBoxA 为例)(最简单)的主要内容,如果未能解决你的问题,请参考以下文章

Hook CreateProcess

利用Windows API 在指定的窗口上单击一次鼠标

如何使用C或C++编程获取电脑CPU使用率?如何使用C或C++获取指定进程的CPU使用率?

C++ 如何获得当前电脑里运行的所有进程ID?并关闭指定的进程?

Hook(钩子技术)基本知识讲解,原理

Frida从入门到入门—安卓逆向菜鸟的frida食用说明