汇编 -- Hook API (MessageBoxW)
说到HOOK。我看了非常多的资料和教程。无奈就是学不会HOOK。不懂是我的理解能力差。还是你们说的
不够明确,直到我看了下面这篇文章,最终学会了HOOK:
http://blog.sina.com.cn/s/blog_628821950100xmuc.html //感谢文章作者的分享,让我学会了HOOK
文章出处,好像是这篇:http://blog.csdn.net/glliuxueke/article/details/2702608 //后来才看到
----------------------------------------------------------------------------------------------------------------------------------------------
既然窝已经入门了HOOK,窝会写几篇关于HOOK的文章,让相同想入门HOOK,却难以入门的童鞋
有个參考。这篇是第一篇,希望帮助到有此须要的盆友,我測试的环境都是:Win7+VS2008+MFC
--------------------------------------------------------------------------------------------------------------------------------------------------
第一篇说的是HOOK自己程序的MessageBoxW。诚然HOOK自己程序用到的API在实际应用中没有什么
大的用处,只是我觉得对于我们理解HOOK却有莫大的帮助,因此我的HOOK文章就从HOOK自己的程序
開始。文章后面附有本样例程序的VS2008源代码下载地址。
---------------------------------------------------------------------------------------------------------------------------------------------------
//先看下我写的样例程序及执行效果截图,后面再对其进行分析
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZnJpZW5kYW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="border:none; max-width:100%">
//未钩MessageBoxW前
//钩MessageBoxW后
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZnJpZW5kYW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="border:none; max-width:100%">
---------------------------------------------------------------------------------------------------------------------
依据我的理解,先说一下HOOK API的一般思路和步骤。
HOOK API的思路就是改动原API的入口。使其跳转到我们的假API入口。
然后运行我们的假API函数。为什么说是假API函数呢?
由于我们的假API,除了函数名称和真实API的名称不一样之外,其他都是同样的。即
它们的函数參数和返回值和调用形式都是一样的。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
HOOK API的一般步骤:
1.定义假API函数
注意假API函数,除了函数名称和真实API不一样之外,其他的都要跟真实API的定义同样,如參数类型和返回值、调用形式等。
如我们能够这样定义假的MessageBoxW:
- int WINAPI MyMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType)
- {
- //定义假API时。详细的函数体代码临时可不写...
- return 0;
- }
至于我们怎样知道真实API的原型定义呢?非常easy,查看MSDN就可以,或者你也能够在VS2008开发环境中,
写下该真实API,然后选中该API。再点鼠标右键,选择【转到定义】,就能够看到其原型声明了。
...........................................................................................................................................................................
2.定义API函数类型
由于我们要动态获取原API函数的地址。获取到后,我们要将其保存起来,保存在哪里呢?
这就是定义API函数类型的原因了,有了API函数类型的定义后。我们就能够用其定义一个变量来 保存获取到的
真实API函数的地址了。比如。我定义的MessageBoxW函数类型的语句例如以下:
- //原函数类型定义
- typedef int (WINAPI* MsgBoxW)(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType);
注意,原函数类型定义中的函数返回值和參数类型要跟MessageBoxW的定义同样。
上面就是定义了API函数类型MsgBoxW,这跟我们常见的int、char、float等类型。使用方法是一样的,
实际上,我们全然能够把MsgBoxW当成int类型来使用,这样一说。API函数类型也只是如此嘛。
有了API函数类型MsgBoxW,我们就能够用其来定义变量,从而为保存原API地址做准备了。
如:
- MsgBoxW OldMsgBoxW=NULL;//指向原函数的指针
有了API函数类型后,我们还须要一个远指针类型的变量,由于系统API是在dll文件里的。很多其它关于
远指针的信息,我如今也不是非常清楚,有兴趣的盆友,最好还是百度一下。系统已经给我们定义好了
远指针类型:FARPROC,我们直接拿来用就可以。如:
- FARPROC pfOldMsgBoxW; //指向函数的远指针
..................................................................................................................................................................................................................
3.获取API函数入口
有了保存原API函数地址的变量OldMsgBox和指向原API函数的远指针,我们就能够获取真实API的地址了。
如:
- //获取原API入口地址
- HMODULE hmod=::LoadLibrary(_T("User32.dll"));
- OldMsgBoxW=(MsgBoxW)::GetProcAddress(hmod,"MessageBoxW");
- pfOldMsgBoxW=(FARPROC)OldMsgBoxW;
注意,上面我们将原API地址OldMsgBoxW强制转换成了远指针pfOldMsgBoxW。至于为什么要强制转换。
我如今也不是非常清楚,想知道的童鞋。能够百度一下。
................................................................................................................................................................................................................
4.保存原API入口的前5个字节
保存的目的是为了恢复用的,毕竟我们HOOK了API后,还须要调用真实的API嘛,
怎样保存一个函数入口的前5个字节呢?这里用到了汇编代码,至于不用汇编能够吗?我想是能够的。
有空时,我再试一下,不用汇编能否保存一个API入口的前5个字节。这里就先用别人写的汇编代码吧,
代码例如以下:
- // 将原API的入口前5个字节代码保存到OldCode[]
- BYTE OldCode[5];
- _asm
- {
- lea edi,OldCode //获取OldCode数组的地址,放到edi
- mov esi,pfOldMsgBoxW //获取原API入口地址,放到esi
- cld //方向标志位,为下面两条指令做准备
- movsd //复制原API入口前4个字节到OldCode数组
- movsb //复制原API入口第5个字节到OldCode数组
- }
....................................................................................................................................................................................................................
5.获取新入口的前5个字节
由于我们改动真实API入口的前5个字节。使其跳转到我们假API函数的入口地址。即改成Jmp xxxxxxxx,当中xxxxxxxx就是我们
假API的入口地址,那么我们怎样得到该地址呢?
前人已经总结出了一条公式:
int nAddr= UserFunAddr – SysFunAddr - (我们定制的这条指令的大小);
Jmp nAddr; //我们要获取的就是nAddr的值
既然已经有了公式。我们拿来用就可以,UserFunAddr相当于我们的假API,SysFunAddr相当于真实API,这里指令的大小为5,
实现以上公式的汇编代码例如以下:
- //获取MyMessageBoxW的相对地址,为Jmp做准备
- //int nAddr= UserFunAddr – SysFunAddr - (我们定制的这条指令的大小);
- //Jmp nAddr;
- //(我们定制的这条指令的大小), 这里是5。5个字节嘛
- BYTE NewCode[5];
- _asm
- {
- lea eax,MyMessageBoxW //获取我们的MyMessageBoxW函数地址
- mov ebx,pfOldMsgBoxW //原系统API函数地址
- sub eax,ebx //int nAddr= UserFunAddr – SysFunAddr
- sub eax,5 //nAddr=nAddr-5
- mov dword ptr [NewCode+1],eax //将算出的地址nAddr保存到NewCode后面4个字节
- //注:一个函数地址占4个字节
- }
......................................................................................................................................................................................................................
6.改动真实API入口的前5个字节
前面我们已经保存了真实API入口的前5个字节,也已经计算出了新入口的前5个字节,
可谓万事俱备,仅仅欠东风,如今能够改动真实API入口的前5个字节了。
代码例如以下:
- //填充完成,如今NewCode[]里的指令相当于Jmp MyMessageBoxW
- //既然已经获取到了Jmp MyMessageBoxW
- //如今该是将Jmp MyMessageBoxW写入原API入口前5个字节的时候了
- //知道为什么是5个字节吗?
- //Jmp指令相当于0xe9,占一个字节的内存空间
- //MyMessageBoxW是一个地址,事实上是一个整数,占4个字节的内存空间
- //int n=0x123; n占4个字节和MyMessageBoxW占4个字节是一样的
- //1+4=5,知道为什么是5个字节了吧
- HookOn();
改动API入口前5个字节的HookOn()函数详细代码例如以下:
- //开启钩子的函数
- void HookOn()
- {
- ASSERT(hProcess!=NULL);
- DWORD dwTemp=0;
- DWORD dwOldProtect;
- //改动API函数入口前5个字节为jmp xxxxxx
- VirtualProtectEx(hProcess,pfOldMsgBoxW,5,PAGE_READWRITE,&dwOldProtect);
- WriteProcessMemory(hProcess,pfOldMsgBoxW,NewCode,5,0);
- VirtualProtectEx(hProcess,pfOldMsgBoxW,5,dwOldProtect,&dwTemp);
- }
以上改动API函数入口前5个字节,用到了两个系统API函数,百度百科里面对它们有具体解释,在此就不说了。
注:hProcess是进程句柄,我们能够这样获取它:
DWORD dwPid=::GetCurrentProcessId();
HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);
..........................................................................................................................................................................................................
7.恢复API函数入口前5个字节
有改动。就有恢复嘛,恢复函数代码例如以下:
- //关闭钩子的函数
- void HookOff()
- {
- ASSERT(hProcess!=NULL);
- DWORD dwTemp=0;
- DWORD dwOldProtect;
- //恢复API函数入口前5个字节
- VirtualProtectEx(hProcess,pfOldMsgBoxW,5,PAGE_READWRITE,&dwOldProtect);
- WriteProcessMemory(hProcess,pfOldMsgBoxW,OldCode,5,0);
- VirtualProtectEx(hProcess,pfOldMsgBoxW,5,dwOldProtect,&dwTemp);
- }
注:hProcess是进程句柄。我们能够这样获取它:
DWORD dwPid=::GetCurrentProcessId();
HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
以上就是HOOK API的大致流程了,看不明确的盆友,最好还是再看下我写的源代码。文章结合源代码,
希望有助于你们的理解。
源代码下载地址:Hook自己程序的MessageBoxW.zip
学会了HOOK自己的程序,下篇我们再接再厉。HOOK系统全部程序的MessageBoxA和MessageBoxW
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
以下贴一下以上这个程序的主要代码,算是有个总体印象吧:
- //原函数类型定义
- typedef int (WINAPI* MsgBoxW)(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType);
- MsgBoxW OldMsgBoxW=NULL;//指向原函数的指针
- FARPROC pfOldMsgBoxW; //指向函数的远指针
- BYTE OldCode[5]; //原系统API入口代码
- BYTE NewCode[5]; //原系统API新的入口代码 (jmp xxxxxxxx)
- HANDLE hProcess=NULL;//本程序进程句柄
- HINSTANCE hInst=NULL;//API所在的dll文件句柄
- void HookOn();
- void HookOff();
- int WINAPI MyMessageBoxW(HWND hWnd,LPCWSTR lpText,LPCWSTR lpCaption,UINT uType)
- {
- TRACE(lpText);
- HookOff();//调用原函数之前。记得先恢复HOOK呀。不然是调用不到的
- //假设不恢复HOOK,就调用原函数,会造成死循环
- //毕竟调用的还是我们的函数。从而造成堆栈溢出。程序崩溃。
- int nRet=::MessageBoxW(hWnd,_T("哈哈,MessageBoxW被HOOK了"),lpCaption,uType);
- HookOn();//调用完原函数后。记得继续开启HOOK,不然下次会HOOK不到。
- return nRet;
- }
- //开启钩子的函数
- void HookOn()
- {
- ASSERT(hProcess!=NULL);
- DWORD dwTemp=0;
- DWORD dwOldProtect;
- //改动API函数入口前5个字节为jmp xxxxxx
- VirtualProtectEx(hProcess,pfOldMsgBoxW,5,PAGE_READWRITE,&dwOldProtect);
- WriteProcessMemory(hProcess,pfOldMsgBoxW,NewCode,5,0);
- VirtualProtectEx(hProcess,pfOldMsgBoxW,5,dwOldProtect,&dwTemp);
- }
- //关闭钩子的函数
- void HookOff()
- {
- ASSERT(hProcess!=NULL);
- DWORD dwTemp=0;
- DWORD dwOldProtect;
- //恢复API函数入口前5个字节
- VirtualProtectEx(hProcess,pfOldMsgBoxW,5,PAGE_READWRITE,&dwOldProtect);
- WriteProcessMemory(hProcess,pfOldMsgBoxW,OldCode,5,0);
- VirtualProtectEx(hProcess,pfOldMsgBoxW,5,dwOldProtect,&dwTemp);
- }
- //获取API函数入口前5个字节
- //旧入口前5个字节保存在前面定义的字节数组BYTE OldCode[5]
- //新入口前5个字节保存在前面定义的字节数组BYTE NewCode[5]
- void GetApiEntrance()
- {
- //获取原API入口地址
- HMODULE hmod=::LoadLibrary(_T("User32.dll"));
- OldMsgBoxW=(MsgBoxW)::GetProcAddress(hmod,"MessageBoxW");
- pfOldMsgBoxW=(FARPROC)OldMsgBoxW;
- if (pfOldMsgBoxW==NULL)
- {
- MessageBox(NULL,_T("获取原API入口地址出错"),_T("error!"),0);
- return;
- }
- // 将原API的入口前5个字节代码保存到OldCode[]
- _asm
- {
- lea edi,OldCode //获取OldCode数组的地址,放到edi
- mov esi,pfOldMsgBoxW //获取原API入口地址。放到esi
- cld //方向标志位,为下面两条指令做准备
- movsd //复制原API入口前4个字节到OldCode数组
- movsb //复制原API入口第5个字节到OldCode数组
- }
- NewCode[0]=0xe9;//实际上0xe9就相当于jmp指令
- //获取MyMessageBoxW的相对地址,为Jmp做准备
- //int nAddr= UserFunAddr – SysFunAddr - (我们定制的这条指令的大小);
- //Jmp nAddr;
- //(我们定制的这条指令的大小), 这里是5,5个字节嘛
- BYTE NewCode[5];
- _asm
- {
- lea eax,MyMessageBoxW //获取我们的MyMessageBoxW函数地址
- mov ebx,pfOldMsgBoxW //原系统API函数地址
- sub eax,ebx //int nAddr= UserFunAddr – SysFunAddr
- sub eax,5 //nAddr=nAddr-5
- mov dword ptr [NewCode+1],eax //将算出的地址nAddr保存到NewCode后面4个字节
- //注:一个函数地址占4个字节
- }
- //填充完成,如今NewCode[]里的指令相当于Jmp MyMessageBoxW
- //既然已经获取到了Jmp MyMessageBoxW
- //如今该是将Jmp MyMessageBoxW写入原API入口前5个字节的时候了
- //知道为什么是5个字节吗?
- //Jmp指令相当于0xe9,占一个字节的内存空间
- //MyMessageBoxW是一个地址,事实上是一个整数。占4个字节的内存空间
- //int n=0x123; n占4个字节和MyMessageBoxW占4个字节是一样的
- //1+4=5。知道为什么是5个字节了吧
- HookOn();
- }
- //開始Hook MessageBoxW
- void CHookMsgBoxDlg::OnBnClickedBtnStartHook()
- {
- DWORD dwPid=::GetCurrentProcessId();
- hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);
- GetApiEntrance();
- SetDlgItemText(IDC_STATIC_INFO,_T("Hook已启动"));
- }
- //调用MessageBoxW
- void CHookMsgBoxDlg::OnBnClickedBtnCallMsgBox()
- {
- ::MessageBoxW(m_hWnd,_T("这是正常的MessageBoxW"),_T("Hello"),0);
- }
- //停止Hook MessageBoxW
- void CHookMsgBoxDlg::OnBnClickedBtnStopHook()
- {
- HookOff();
- SetDlgItemText(IDC_STATIC_INFO,_T("Hook未启动"));
- }
汇编 -- Hook API (MessageBoxW)的更多相关文章
- HOOK API入门之Hook自己程序的MessageBoxW(简单入门)
说到HOOK,我看了很多的资料和教程,无奈就是学不会HOOK,不懂是我的理解能力差,还是你们说的 不够明白,直到我看了以下这篇文章,终于学会了HOOK: http://blog.sina.com.cn ...
- 汇编Ring 3下实现 HOOK API
[文章标题]汇编ring3下实现HOOK API [文章作者]nohacks(非安全,hacker0058) [作者主页]hacker0058.ys168.com [文章出处]看雪论坛(bbs.ped ...
- HOOK API(三)—— HOOK 所有程序的 MessageBox
HOOK API(三) —— HOOK 所有程序的 MessageBox 0x00 前言 本实例要实现HOOK MessageBox,包括MessageBoxA和MessageBoxW,其实现细节与H ...
- HOOK API(二)—— HOOK自己程序的 MessageBox
HOOK API(二) —— HOOK自己程序的 MessageBox 0x00 前言 以下将给出一个简单的例子,作为HOOK API的入门.这里是HOOK 自己程序的MessageBox,即将自己程 ...
- HOOK API(二) —— HOOK自己程序的 MessageBox
转载来源:https://www.cnblogs.com/hookjc/ 0x00 前言 以下将给出一个简单的例子,作为HOOK API的入门.这里是HOOK 自己程序的MessageBox,即将自己 ...
- HOOK API(三) —— HOOK 所有程序的 MessageBox
转载来源:https://www.cnblogs.com/hookjc/ 0x00 前言 本实例要实现HOOK MessageBox,包括MessageBoxA和MessageBoxW,其实现细节与H ...
- HOOK API 在多线程时应该注意的问题点
在使用INLINE HOOK API实现对系统API的拦截时,正常情况下并没有太大问题,但一旦涉及到多线程,不管是修改IAT还是JMP,2种方法均会出现不可预料的问题,特别是在HOOK一些复杂的大型系 ...
- HOOK API(四)—— 进程防终止
HOOK API(四) —— 进程防终止 0x00 前言 这算是一个实战吧,做的一个应用需要实现进程的防终止保护,查了相关资料后决定用HOOK API的方式实现.起初学习HOOK API ...
- HOOK API (一)——HOOK基础+一个鼠标钩子实例
HOOK API (一)——HOOK基础+一个鼠标钩子实例 0x00 起因 最近在做毕业设计,有一个功能是需要实现对剪切板的监控和进程的防终止保护.原本想从内核层实现,但没有头绪.最后决定从调用层入手 ...
随机推荐
- STM32F030, 使用嘀嗒定时器Systick实现LED闪烁
本文主要解决两个问题 1 STM32的IO口要反转,怎么实现? 2 嘀嗒定时器systick的配置 解答1: 单片机的口,反转非常easy.sbit led = P1 ^6; led = ~led; ...
- IT关键词,面试知识问与答
二叉树遍历的三种方式? 遍历是指依次访问⼆叉树中的每个元素.有三种遍历⽅法,分别是前序遍历. 中序遍历和后序遍历.它们是按照访问根节点和⼦节点的先后顺序命名的. • 前序遍历:先访问根节点,然后访问左 ...
- lightoj--1294--Largest Box(三分)
Largest Box Time Limit: 2000MS Memory Limit: 32768KB 64bit IO Format: %lld & %llu Submit Sta ...
- Ubuntu开机报错:could not update ICEauthority file /home/user/.ICEauthority(转载)
解决方法如下: 一. 代码:sudo chown $USER:$USER /home/$USER/.ICEauthority sudo chmod 644 /home/$USER/.IC ...
- 在C#中运行PowerShell
C#中运行PowerShell需要用到System.Management.Automation.dll.在Visual Studio中可以通过NuGet添加引用,package名字为"Sys ...
- Linux top命令简解
简介: top 命令是最流行的性能监视工具之一,它是一个优秀的交互式工具,用于监视性能.它提供系统整体性能,但报告进程信息才是 top 命令的长处. top 界面分为两个部份,光标上面部份显示关于系统 ...
- Core Java(七)
面向对象特性整理 知识点:一. static修饰符 static修饰符可以用来修饰类的成员变量.成员方法和代码块. . 用static修饰的成员变量表示静态变量,可以直接通过类名 ...
- js判断传入时间和当前时间大小
//判断时间是否过期 function judgeTime(time){ var strtime = time.replace("/-/g", "/");//时 ...
- [OS][ linux ] 建立新帳號, 變更密碼, 加入 sudoer
新增 linux , 設定預設 password, 新增 user 到 sudoers 1. 新增 User sudo useradd aa97 2. 設定 User password sudo pa ...
- CREATE TABLE 语句后的 ON [PRIMARY] 起什么作用
CREATE TABLE [dbo].[table1] ( [gh] [char] (10) COLLATE Chinese_PRC_CI_AS NOT NULL ...