HOOK API(二)—— HOOK自己程序的 MessageBox

时间:2022-12-09 14:37:44

HOOK API(二)

—— HOOK自己程序的 MessageBox

0x00 前言

以下将给出一个简单的例子,作为HOOK API的入门。这里是HOOK 自己程序的MessageBox,即将自己程序对MessageBox API的调用重定向到自己实现的API中,在自己定义的API中实现内容的替换。

需要注意的是,本例子的HOOK仅仅对自己实现的MFC窗口程序,当开始HOOK 后,自己的程序调用MessageBox将被重定向,但其他程序滴啊用MessageBox时是正常的。

在Windows中,每个进程都有自己的进程控制块,有自己的安全运行空间,各函数在初始化时被加载到进程的地址空间中,各进程的地址空间是不相交的。本实例中,HOOK API仅仅在自己程序的地址空间中实现了地址的替换,因此不影响其他进程的工作,若想HOOK其他程序,那么就要想办法将自己实现的API注入到目标进程的地址空间中,并替换原API的地址,才能实现我们想要的功能,这将在后续的学习中进一步介绍。

本事例仅对自己的程序进行HOOK,实用性不是很大,但是对于入门,理解HOOK API的过程还是很有帮助的。

0x01 实现思想

在自己实现的窗体程序(Windows-A)中实现一个与MessageBox API定义一模一样的API(MessBox-New),这个API除了完成原API(MessBox-Old)的工作之外,还将显示内容进行修改。Windows A 加载时,对将自己所使用的API地址都加载到自己的地址空间中,这里包括我们自己写的MessBox-New,因此我们可以很方便的使用MessBox-New的调用地址来替换MessBox-Old的入口地址,进而实现对MessBox-Old的重定向、即替换。地址被替换之后,只要本程序调用MessageBox这个API,就会被重定向到我们实现的MessBox-New中。此过程中,若想要恢复正常,只需要将MessBox-Old的入口地址恢复即可。

0x02 HOOK API实现过程

本小节将介绍程序的实现过程。

  1.定义自己的API

定义自己的API,因为我们这里要HOOK 自己程序的MessageBox,因此就要定义一个原型与MessageBox API一模一样的API。查MSDN,可得MessageBox有两种调用形式,分别是MessageBoxA和MessageBoxW,前者处理窄字符串,即每个字符占一个字节;后者处理宽字符串,即一个字符占两个字节。我们这里HOOK MessageBoxW,其原型为:

int WINAPI MessageBoxW(

_In_opt_ HWND hWnd,

_In_opt_ LPCWSTR lpText,

_In_opt_ LPCWSTR lpCaption,

_In_ UINT uType

);

由此,可以定义我们自己的API如下:

这里很容易漏掉函数前面的 WINAPI,若是少了将无法正常实现HOOK,一定要注意我们实现的函数的原型要与原API一致。

//

// 自己定义的,用于替换相应API的,假的API

//

int WINAPI MyMessageBoxW(HWND hwnd,LPCWSTR lpText,LPCWSTR lpCation,UINT uType)

{

TRACE(lpText);

/*

调用原函数之前,先停止HOOK,也就是恢复原系统API函数的入口,否则无法调用到原API函数,

而是继续调用自己的API,会造成死循环,进而造成堆栈溢出,崩溃。

*/

HookOff();

/*

调用原来的MessageBoxW打印我们的信息。

*/

int ret = MessageBoxW(hwnd,_T("哈哈,被HOOK咯!!"),lpCation,uType);

/*

调用完原系统API后,记得恢复HOOK,也就是启动HOOK,将原API函数入口换成我们自己定义的函数入口,

否则下一次调用MessageBoxW的时候就无法转到我们自己定义的API函数中,也就无法实现HOOK。

*/

HookOff();

return ret;

}

  2.  定义API类型

定义原API的类型,下面的TypeMessageBoxW其它普通的数据类型的使用方法是一样的。定义一个TypeMessageBoxW类型的变量,用于存储原API的指针,还定义一个远指针类型,pfOldMsgBoxW,因为系统API是在动态链接库(DLL)中实现的,因此程序实际上是通过远地址指针来DLL中相应的API调用。而本实例中设计的MessageBox是在 User32.dll 中的。关于远地址指针这里不做多介绍,需要了解的可以查阅相关资料。

typedef int (WINAPI *TypeMessageBoxW)(HWND hwnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType);

TypeMessageBoxW OdlMsgBoxW = NULL;    // 指向函数原型指针

FARPROC pfOldMsgBoxW;                // 指向函数远指针

  3.获取原API函数入口

由MSDN可知,原API在User32.dll中实现,因此在此之前要加载User32.dll,并获取到原API的函数入口。实现代码如下:

HMODULE hmod = LoadLibrary(_T("User32.dll"));

if ( NULL == hmod)

{

AfxMessageBox(_T("加载User32.dll失败"));

return;

}

OdlMsgBoxW = (TypeMessageBoxW)::GetProcAddress(hmod,"MessageBoxW");

pfOldMsgBoxW = (FARPROC)OdlMsgBoxW;

if ( pfOldMsgBoxW == NULL)

{

AfxMessageBox(_T("获取原API入口地址出错"));

return;

}

  4.保存原API入口的前5个字节

为了最后恢复原API的地址,必须要在HOOK之前将原API的入口地址保存起来。而这里为什么是5个字节呢?因为我们使用jmp xxxx 指令实现原API的重定向,该指令的长度为5个字节,jmp占一个字节,而xxxx表示新API的入口地址,占4个字节,我们使用jmp xxxx这条指令来替换掉原API入口的5个字节,这样一来当本程序调用MessageBoxW时,就会跳转到我们实现的API。综上所述,我们这里需要保存原API的前5个字节,实现代码如下:

//

个字节代码保存到OdeCode[]中

//

_asm

{

lea edi,OldCode            // 取数组OldCode[]地址,存放到edi中

mov esi,pfOldMsgBoxW     // 获取原API入口地址,存入esi中

cld                        // 设置方向

movsd                    // 移动dword ,4 Byte

movsb                    // 移动 1 Byte

}

  5.设置新的(自己的)API入口的前5个字节

保存好原API的入口之后,我们这里需要设置jmp xxxx指令,xxxx为新API的入口地址,以便之后实现地址的替换。

而xxxx如何计算呢,可遵循前人总结的一条计算公式:

int xxxx = MyFunAddr – SystemFunAddr - CodeLength;

jmp xxxx;

MyFunAddr                :        我们编写的新的API的地址;

SystemFunAddr        :        原API的地址;

CodeLength               :        入口指令长度,本实例是 jmp xxxx 的长度,为5个字节。

//

个字节

//

NewCode[0] = 0xe9; //    0xe9相当于jmp指令

_asm

{

lea eax,MyMessageBoxW

mov ebx,pfOldMsgBoxW

sub eax,ebx

sub eax,CODE_LENGTH

mov dword ptr[NewCode+1],eax

}

  6.修改原(真实)API入口的前5个字节为新的(自己的)API入口地址

在保存了原API和新API的入口地址之后,接下来就是要实现地址的替换,即使用新API入口替换原API入口,从而实现HOOK MessageBoxW。这里涉及到两个重要的API,VirtualProtectEx 和 WriteProcessMemory,关于API的详细说明可以查询MSDN。

/*

启动HOOK,将原API的入口地址换成我们自己定义函数的入口地址

*/

VOID HookOn()

{

//

//    确保本程序进程句柄hProcess不为NULL

//

ASSERT(hProcess!=NULL);

DWORD dwTemp;

DWORD dwOldProtect;

SIZE_T writedByte;

//

个字节,jmp xxxx

//

VirtualProtectEx(hProcess,pfOldMsgBoxW,CODE_LENGTH,PAGE_READWRITE,&dwOldProtect);

WriteProcessMemory(hProcess,pfOldMsgBoxW,NewCode,CODE_LENGTH,&writedByte);

if (writedByte == 0)

{

AfxMessageBox(_T("替换原API地址失败"));

}

VirtualProtectEx(hProcess,pfOldMsgBoxW,CODE_LENGTH,dwOldProtect,&dwTemp);

}

  7.恢复原API入口地址

在HOOK后,对MessageBox的调用会被重定向到我们实现的API中,若需要调用原API,则必须回复原API的入口地址,否则会出现死循环。实现代码如下:

/*

停止HOOK,将入口换成原来的API入口地址

*/

VOID HookOff()

{

ASSERT(hProcess != NULL);

DWORD dwTemp;

DWORD dwOldProtect;

SIZE_T wirtedByte;

//

//    回复原API地址

//

VirtualProtectEx(hProcess,pfOldMsgBoxW,CODE_LENGTH,PAGE_READWRITE,&dwOldProtect);

WriteProcessMemory(hProcess,pfOldMsgBoxW,OldCode,CODE_LENGTH,&wirtedByte);

if (wirtedByte == 0)

{

AfxMessageBox(_T("回复原API地址失败"));

}

VirtualProtectEx(hProcess,pfOldMsgBoxW,CODE_LENGTH,dwOldProtect,&dwTemp);

}

0x03 窗口按钮实现

HOOK API(二)—— HOOK自己程序的 MessageBox

  1.启动HookMessageBoxW

//

//    启动 HookMessageBoxW

//

void CHookMessageboxWindowDlg::OnBnClickedButtonStart()

{

// TODO: 在此添加控件通知处理程序代码

AdjustPrivileges();        //    提升权限,因为调用 OpenProcess() 需要合适的权限

DWORD dwPid = ::GetCurrentProcessId();

hProcess = OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);

if (hProcess == NULL)

{

CString logInfo;

logInfo.Format(_T("获取进程句柄失败!!,进程 id = 0x%x ,错误代码 = 0x%x"),dwPid,GetLastError());

AfxMessageBox(logInfo);

return;

}

GetApiEntrancy();    //    获取新旧API入口,并开始HOOK

m_status.SetWindowText(_T("Hook已启动"));

}

  2.终止HookMessageBoxW

//

//    终止 HookMessageBoxW

//

void CHookMessageboxWindowDlg::OnBnClickedButtonStop()

{

// TODO: 在此添加控件通知处理程序代码

HookOff();

m_status.SetWindowText(_T("Hook已停止"));

}

  3.调用MessageBoxW

//

//    调用 HookMessageBoxW

//

void CHookMessageboxWindowDlg::OnBnClickedButtonCall()

{

// TODO: 在此添加控件通知处理程序代码

::MessageBoxW(m_hWnd,_T("这是正常的MessageBoxW"),_T("Hello"),0);

}

  4.提升权限

bool AdjustPrivileges() {

HANDLE hToken;

TOKEN_PRIVILEGES tp;

TOKEN_PRIVILEGES oldtp;

DWORD dwSize=sizeof(TOKEN_PRIVILEGES);

LUID luid;

if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {

if (GetLastError()==ERROR_CALL_NOT_IMPLEMENTED) return true;

else return false;

}

if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) {

CloseHandle(hToken);

return false;

}

ZeroMemory(&tp, sizeof(tp));

tp.PrivilegeCount=1;

tp.Privileges[0].Luid=luid;

tp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;

/* Adjust Token Privileges */

if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &oldtp, &dwSize)) {

CloseHandle(hToken);

return false;

}

// close handles

CloseHandle(hToken);

return true;

}

0x04 测试

  1. 启动程序,单击"调用MessageBoxW"按钮

    HOOK API(二)—— HOOK自己程序的 MessageBox

  2. 单击"启动HookMessageBoxW"按钮,单击"调用MessageBoxW"按钮

    HOOK API(二)—— HOOK自己程序的 MessageBox

  3. 单击"终止HookMessageBoxW"按钮,单击"调用MessageBoxW"按钮

    HOOK API(二)—— HOOK自己程序的 MessageBox

0x04 附录——全部源码

以下给出主要实现的所有源代码,以便从整体上把握整个实现过程。

// HookMessageboxWindowDlg.cpp : 实现文件

//

#include "stdafx.h"

#include "HookMessageboxWindow.h"

#include "HookMessageboxWindowDlg.h"

#include "afxdialogex.h"

//    定义API类型

#define CODE_LENGTH 5                //    入口指令长度

typedef int (WINAPI *TypeMessageBoxW)(HWND hwnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType);

TypeMessageBoxW OdlMsgBoxW = NULL;    // 指向函数原型指针

FARPROC pfOldMsgBoxW;                // 指向函数远指针

BYTE OldCode[CODE_LENGTH];            // 原系统API入口

BYTE NewCode[CODE_LENGTH];            // 自己实现的API的入口,(jmp xxxx),xxxx为新API入口地址

HANDLE hProcess = NULL;                // 本程序进程句柄

HINSTANCE hInst = NULL;                // API所在的dll文件句柄

VOID HookOn();            //    开始HOOK

VOID HookOff();            //    停止HOOK

VOID GetApiEntrancy();    //    获取API入口地址

bool AdjustPrivileges();//    提高权限

//

// 自己定义的,用于替换相应API的,假的API

//

int WINAPI MyMessageBoxW(HWND hwnd,LPCWSTR lpText,LPCWSTR lpCation,UINT uType)

{

TRACE(lpText);

/*

调用原函数之前,先停止HOOK,也就是恢复原系统API函数的入口,

否则无法调用到原API函数,而是继续调用自己的API,会造成死

循环,进而造成堆栈溢出,崩溃。

*/

HookOff();

/*

调用原来的MessageBoxW打印我们的信息。

*/

int ret = MessageBoxW(hwnd,_T("哈哈,被HOOK咯!!"),lpCation,uType);

/*

调用完原系统API后,记得恢复HOOK,也就是启动HOOK,将原API函数入口换成我们自己定义的函数入口,

否则下一次调用MessageBoxW的时候就无法转到我们自己定义的API函数中,也就无法实现HOOK。

*/

HookOff();

return ret;

}

/*

启动HOOK,将原API的入口地址换成我们自己定义函数的入口地址

*/

VOID HookOn()

{

//

//    确保本程序进程句柄hProcess不为NULL

//

ASSERT(hProcess!=NULL);

DWORD dwTemp;

DWORD dwOldProtect;

SIZE_T writedByte;

//

个字节,jmp xxxx

//

VirtualProtectEx(hProcess,pfOldMsgBoxW,CODE_LENGTH,PAGE_READWRITE,&dwOldProtect);

WriteProcessMemory(hProcess,pfOldMsgBoxW,NewCode,CODE_LENGTH,&writedByte);

if (writedByte == 0)

{

AfxMessageBox(_T("替换原API地址失败"));

}

VirtualProtectEx(hProcess,pfOldMsgBoxW,CODE_LENGTH,dwOldProtect,&dwTemp);

}

/*

定制HOOK,将入口换成原来的API入口地址

*/

VOID HookOff()

{

ASSERT(hProcess != NULL);

DWORD dwTemp;

DWORD dwOldProtect;

SIZE_T wirtedByte;

//    回复原API地址

VirtualProtectEx(hProcess,pfOldMsgBoxW,CODE_LENGTH,PAGE_READWRITE,&dwOldProtect);

WriteProcessMemory(hProcess,pfOldMsgBoxW,OldCode,CODE_LENGTH,&wirtedByte);

if (wirtedByte == 0)

{

AfxMessageBox(_T("回复原API地址失败"));

}

VirtualProtectEx(hProcess,pfOldMsgBoxW,CODE_LENGTH,dwOldProtect,&dwTemp);

}

/*

保存原API和新API的地址

*/

VOID GetApiEntrancy()

{

//

// 保存原来API地址

//

HMODULE hmod = LoadLibrary(_T("User32.dll"));

if ( NULL == hmod)

{

AfxMessageBox(_T("加载User32.dll失败"));

return;

}

OdlMsgBoxW = (TypeMessageBoxW)::GetProcAddress(hmod,"MessageBoxW");

pfOldMsgBoxW = (FARPROC)OdlMsgBoxW;

if ( pfOldMsgBoxW == NULL)

{

AfxMessageBox(_T("获取原API入口地址出错"));

return;

}

//

个字节代码保存到OdeCode[]中

//

_asm

{

lea edi,OldCode            // 取数组OldCode[]地址,存放到edi中

mov esi,pfOldMsgBoxW    // 获取原API入口地址,存入esi中

cld                        // 设置方向

movsd                    // 移动dword ,4 Byte

movsb                    // 移动 1 Byte

}

//

个字节

//

NewCode[0] = 0xe9; //    0xe9相当于jmp指令

_asm

{

lea eax,MyMessageBoxW

mov ebx,pfOldMsgBoxW

sub eax,ebx

sub eax,CODE_LENGTH

mov dword ptr[NewCode+1],eax

}

//

// 填充完毕,开始HOOK,即使用NewCode[]替换原API入口

//

HookOn();

}

bool AdjustPrivileges() {

HANDLE hToken;

TOKEN_PRIVILEGES tp;

TOKEN_PRIVILEGES oldtp;

DWORD dwSize=sizeof(TOKEN_PRIVILEGES);

LUID luid;

if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {

if (GetLastError()==ERROR_CALL_NOT_IMPLEMENTED) return true;

else return false;

}

if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) {

CloseHandle(hToken);

return false;

}

ZeroMemory(&tp, sizeof(tp));

tp.PrivilegeCount=1;

tp.Privileges[0].Luid=luid;

tp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;

/* Adjust Token Privileges */

if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), &oldtp, &dwSize)) {

CloseHandle(hToken);

return false;

}

// close handles

CloseHandle(hToken);

return true;

}

//

//    启动 HookMessageBoxW

//

void CHookMessageboxWindowDlg::OnBnClickedButtonStart()

{

// TODO: 在此添加控件通知处理程序代码

AdjustPrivileges();        //    提升权限,因为调用 OpenProcess() 需要合适的权限

DWORD dwPid = ::GetCurrentProcessId();

hProcess = OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);

if (hProcess == NULL)

{

CString logInfo;

logInfo.Format(_T("获取进程句柄失败!!,进程 id = 0x%x ,错误代码 = 0x%x"),dwPid,GetLastError());

AfxMessageBox(logInfo);

return;

}

GetApiEntrancy();    //    获取新旧API入口,并开始HOOK

m_status.SetWindowText(_T("Hook已启动"));

}

//

//    终止 HookMessageBoxW

//

void CHookMessageboxWindowDlg::OnBnClickedButtonStop()

{

// TODO: 在此添加控件通知处理程序代码

HookOff();

m_status.SetWindowText(_T("Hook已停止"));

}

//

//    调用 HookMessageBoxW

//

void CHookMessageboxWindowDlg::OnBnClickedButtonCall()

{

// TODO: 在此添加控件通知处理程序代码

::MessageBoxW(m_hWnd,_T("这是正常的MessageBoxW"),_T("Hello"),0);

}

HOOK API(二)—— HOOK自己程序的 MessageBox的更多相关文章

  1. 汇编Ring 3下实现 HOOK API

    [文章标题]汇编ring3下实现HOOK API [文章作者]nohacks(非安全,hacker0058) [作者主页]hacker0058.ys168.com [文章出处]看雪论坛(bbs.ped ...

  2. HOOK API(三)—— HOOK 所有程序的 MessageBox

    HOOK API(三) —— HOOK 所有程序的 MessageBox 0x00 前言 本实例要实现HOOK MessageBox,包括MessageBoxA和MessageBoxW,其实现细节与H ...

  3. Windows Dll Injection、Process Injection、API Hook、DLL后门/恶意程序入侵技术

    catalogue 1. 引言2. 使用注册表注入DLL3. 使用Windows挂钩来注入DLL4. 使用远程线程来注入DLL5. 使用木马DLL来注入DLL6. 把DLL作为调试器来注入7. 使用c ...

  4. HOOK API入门之Hook自己程序的MessageBoxW(简单入门)

    说到HOOK,我看了很多的资料和教程,无奈就是学不会HOOK,不懂是我的理解能力差,还是你们说的 不够明白,直到我看了以下这篇文章,终于学会了HOOK: http://blog.sina.com.cn ...

  5. HOOK大法实现不修改程序代码给程序添加功能

    [文章标题]: HOOK大法实现不修改程序代码给程序添加功能[文章作者]: 0x18c0[软件名称]: Scylla[使用工具]: OD.Stub_PE.ResHacker[版权声明]: 本文原创于0 ...

  6. 汇编 -- Hook API (MessageBoxW)

    说到HOOK.我看了非常多的资料和教程.无奈就是学不会HOOK.不懂是我的理解能力差.还是你们说的 不够明确,直到我看了下面这篇文章,最终学会了HOOK: http://blog.sina.com.c ...

  7. HOOK API(四)—— 进程防终止

    HOOK API(四) —— 进程防终止 0x00        前言 这算是一个实战吧,做的一个应用需要实现进程的防终止保护,查了相关资料后决定用HOOK API的方式实现.起初学习HOOK API ...

  8. HOOK API (一)——HOOK基础+一个鼠标钩子实例

    HOOK API (一)——HOOK基础+一个鼠标钩子实例 0x00 起因 最近在做毕业设计,有一个功能是需要实现对剪切板的监控和进程的防终止保护.原本想从内核层实现,但没有头绪.最后决定从调用层入手 ...

  9. HOOK API 在多线程时应该注意的问题点

    在使用INLINE HOOK API实现对系统API的拦截时,正常情况下并没有太大问题,但一旦涉及到多线程,不管是修改IAT还是JMP,2种方法均会出现不可预料的问题,特别是在HOOK一些复杂的大型系 ...

随机推荐

  1. 【转】c# Image获得图片路径的三种方法 winform

    代码如下:c# pictureBox1.Image的获得图片路径的三种方法 winform 1.绝对路径:this.pictureBox2.Image=Image.FromFile("D:\ ...

  2. include/linux/tasks.h

    #ifndef _LINUX_TASKS_H#define _LINUX_TASKS_H /* * This is the maximum nr of tasks - change it if you ...

  3. 爱改名的小融 2(codevs 3149)

    3149 爱改名的小融 2  时间限制: 2 s  空间限制: 128000 KB  题目等级 : 黄金 Gold 题解  查看运行结果     题目描述 Description Wikioi上有个人 ...

  4. 【坑】执行Consumer的时候发生java.net.UnknownHostException错误

    [时间]: 2016/4/8 17:30 [问题]: kafka执行Consumer实例的时候,发生了一下错误. kafka配置文件server.properties如下: zookeeper配置文件 ...

  5. Windows消息编程(写的不错,有前因后果)

    本文主要包括以下内容: 1.简单理解Windows的消息2.通过一个简单的Win32程序理解Windows消息3.通过几个Win32程序实例进一步深入理解Windows消息4.队列消息和非队列消息5. ...

  6. SQL truncate 、delete与drop区别

    SQL truncate .delete与drop区别 相同点: 1.truncate和不带where子句的delete.以及drop都会删除表内的数据. 2.drop.truncate都是DDL语句 ...

  7. python continue的应用

    count = 1sum = 0while count < 100: if count == 88: count += 1 continue if count % 2 == 0: sum = s ...

  8. Codeforces Round &num;436 E&period; Fire(背包dp&plus;输出路径)

    题意:失火了,有n个物品,每个物品有价值pi,必须在时间di前(小于di)被救,否则就要被烧毁.救某个物 品需要时间ti,问最多救回多少价值的物品,并输出救物品的顺序. Examples Input ...

  9. 关于CentOS7下docker-ce无法删除镜像的问题

    未完待续.... 从旧版的docker删除后安装了新版的docker-ce 发现之前镜像无法删除,并且重新pull不能覆盖,会出现两个一模一样的镜像. [root@localhost ~]# dock ...

  10. Wannafly Winter Camp 2019&period;Day 8 div1 E&period;Souls-like Game&lpar;线段树 矩阵快速幂&rpar;

    题目链接 \(998244353\)写成\(99824435\)然后调这个线段树模板1.5h= = 以后要注意常量啊啊啊 \(Description\) 每个位置有一个\(3\times3\)的矩阵, ...