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 窗口按钮实现

  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"按钮

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

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

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. HOOK API(二) —— HOOK自己程序的 MessageBox

    转载来源:https://www.cnblogs.com/hookjc/ 0x00 前言 以下将给出一个简单的例子,作为HOOK API的入门.这里是HOOK 自己程序的MessageBox,即将自己 ...

  2. 汇编Ring 3下实现 HOOK API

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

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

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

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

    转载来源:https://www.cnblogs.com/hookjc/ 0x00 前言 本实例要实现HOOK MessageBox,包括MessageBoxA和MessageBoxW,其实现细节与H ...

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

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

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

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

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

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

  8. 汇编 -- Hook API (MessageBoxW)

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

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

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

随机推荐

  1. (6)Xamarin.android google map v2

    原文 Xamarin.android google map v2 Google Map v1已经在2013年的3月开始停止支持了,目前若要在你的Android手机上使用到Google Map,就必须要 ...

  2. hdu 1210_(逻辑训练)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1210 #include<stdio.h> int main() { int n,t,sum ...

  3. HDU 3641 Treasure Hunting(阶乘素因子分解+二分)

    题目链接:pid=3641">传送门 题意: 求最小的 ( x! ) = 0 mod (a1^b1*a2^b2...an^bn) 分析: 首先吧a1~an进行素因子分解,然后统计下每一 ...

  4. 转换成CSV文件、Word、Excel、PDF等的方法--读取CSV文件的方法

    1. 转换成CSV文件: http://www.dotnetgallery.com/lab/resource93-Export-to-CSV-file-from-Data-Table-in-Aspne ...

  5. ASP.NET导出EXCEl方法使用EXCEl对象

    导出功能必须使用  office中EXCEl对象,整个操作如同在操作EXCEl一样,建立EXCEl应用----建立工作簿---建立sheet表单页, 代码实现过程中,如果想对单元格实现一些操作,或者汇 ...

  6. LDAP基础

    超级好的LDAP文章: Linux下基于LDAP统一用户认证的研究 : http://chenguang.blog.51cto.com/350944/285602利用LDAP实现windows和Lin ...

  7. 表A中有两个表示时间的字段A,B;如果B的值大于A的值,则把B的值更新为A的值

    sql语句:表A中有两个表示时间的字段A,B:如果B的值大于A的值,则把B的值更新为A的值 update 表名 set B=A where B>A

  8. VS2013 快捷键 与 RESHARPER 冲突

    1.VS设置工具-->选项-->环境-->键盘-->重置 2.RESHARPER -->Options-->Environment → Keyboard & ...

  9. JDK源码学习--String篇(-)

    工作三年了,用了三年的JAVA,突然发现竟然没有好好的看下JDK的源码,整天用着的String,只是大概知道怎么回事,其中的实现逻辑却是一头雾水. 知耻而后勇,加油!!! java.lang.Stri ...

  10. 对Spring from中日期显示格式化问题

    开始时间 结束时间 保存 取消 想在input中让日期格式显示为HH:ss 但是各种百度没有找到答案 最后Google之 http://stackoverflow.com/questions/1173 ...