原文地址:http://blog.sina.com.cn/s/blog_628821950100xmuc.html

原文对我的帮助极大,正是由于看了原文。我才学会了HOOK。鉴于原文的排版不是非常好,

又没有原project样例源代码下载,因此我决定对其又一次整理,文章后面附有我測试时的project源代码下载地址。

注:我測试的环境为Win7+VS2008+MFC

原文出处,好像是这篇:http://blog.csdn.net/glliuxueke/article/details/2702608      //后来才看到的

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

前言

       本文主要介绍了怎样实现替换Windows上的API函数,实现Windows API Hook

(当然。对于socket的Hook仅仅是当中的一种特例)。这样的Hook API技术被广泛的採用在一些领域中,

如屏幕取词,个人防火墙等。这样的API Hook技术并非非常新,可是涉及的领域比較宽广,

要想做好有一定的技术难度。本文是採集了不少达人的曾经资料并结合自己的实验得出的心得体会,

在这里进行总结发表,希望可以给广大的读者提供參考。达到抛砖引玉的结果。

-------------------------------------------------------------------------------------------------------------------------------------------------------------

问题

       近期和同学讨论怎样构建一个Windows上的简单的个人防火墙。后来讨论涉及到了怎样让进程关联套接字port,

替换windows API,屏幕取词等技术。当中基本的问题有:

1) 採用何种机制来截获socket的调用?

一般来说。实现截获socket的方法有非常多非常多,最主要的。能够写驱动,驱动也有非常多种,TDI驱动, NDIS驱动,Mini port驱动…

因为我使用的是Win2000系统。所以截获socket也能够用Windows SPI来进行。

第二种就是Windows API Hook技术。

因为我没什么硬件基础,不会写驱动,所以第一种方法没有考虑。而用SPI相对照较简单。

可是后来认为Windows API Hook适应面更广,并且认为自己动手能学到不少东西,

就决定用Windows API Hook来尝试做socket Hook.

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

2) API Hook的实现方法?

实际上就是对系统函数的替换,当然实现替换的方法大概不下5,6种吧,能够參考《Windows核心编程》第22章。

只是我使用的方法与其不近同样。应该相对照较简单易懂。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

原理

我们知道,系统函数都是以DLL封装起来的,应用程序应用到系统函数时,应首先把该DLL载入到当前的进程空间中,

调用的系统函数的入口地址,能够通过 GetProcAddress函数进行获取。

当系统函数进行调用的时候,

首先把所必要的信息保存下来(包含參数和返回地址。等一些别的信息)。然后就跳转到函数的入口地址。继续运行。

事实上函数地址。就是系统函数“可运行代码”的開始地址。那么怎么才干让函数首先运行我们的函数呢?

呵呵。应该明确了吧,把開始的那段可运行代码替换为我们自己定制的一小段可运行代码,这样系统函数调用时,

不就按我们的意图乖乖行事了吗?事实上,就这么简单。Very very简单。

:P

实际的说。就能够改动系统函数入口的地方,让他调转到我们的函数的入口点即可了。

採用汇编代码就能简单的实现Jmp XXXX, 当中XXXX就是要跳转的相对地址。

我们的做法是:把系统函数的入口地方的内容替换为一条Jmp指令。目的就是跳到我们的函数进行运行。

而Jmp后面要求的是相对偏移,也就是我们的函数入口地址到系统函数入口地址之间的差异。再减去我们这条指令的大小。

用公式表达例如以下:(1)int nDelta = UserFunAddr – SysFunAddr - (我们定制的这条指令的大小);(2)Jmp nDleta;

为了保持原程序的健壮性,我们的函数里做完必要的处理后,要回调原来的系统函数。然后返回。

所以调用原来系统函数之前必须先把原来改动的系统函数入口地方给恢复,否则,

系统函数地方被我们改成了Jmp XXXX就会又跳到我们的函数里。死循环了。

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

那么说一下程序运行的过程。

       我们的dll“注射”入被hook的进程 -> 保存系统函数入口处的代码 -> 替换掉进程中的系统函数入口指向我们的函数 -> 当系统函数被

调用,马上跳转到我们的函数 -> 我们函数进行处理 -> 恢复系统函数入口的代码 -> 调用原来的系统函数 -> 再改动系统函数入口指向

我们的函数(为了下次hook)-> 返回。于是。一次完整的Hook就完毕了。

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

        好,这个问题明确以后,讲一下下个问题。就是怎样进行dll“注射”?即将我们的dll注射到要Hook的进程中去呢?

非常easy哦,这里我们採用调用Windows提供给我们的一些现成的Hook来进行注射。

举个样例,鼠标钩子。

键盘钩子大家都知道吧?我们能够给系统装一个鼠标钩子,然后全部响应到鼠标事件的进程。

就会“自己主动”(事实上是系统处理了)加载我们的dll然后设置对应的钩子函数。事实上我们的目的仅仅是须要让被注射进程

加载我们的dll就能够了,我们能够再dll实例化的时候进行函数注射的,我们的这个鼠标钩子什么都不干的。

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

简单的样例OneAddOne

讲了上面的原理。如今我们应该实战一下了。

先不要考虑windows系统那些繁杂的函数,

我们自己编写一个API函数来进行Hook与被Hook的练习吧。哈哈。

第一步。首先编写一个Add.dll。非常easy,这个dll仅仅输出一个API函数,就是add啦。

新建一个win32 dllproject,

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZnJpZW5kYW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="border:none; max-width:100%">

dllmain.cpp的内容:

//千万别忘记声明WINAPI,否则调用的时候回产生声明错误哦!

int WINAPI add(int a,int b)
{
return a+b;
} BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

然后别忘了在add.def里面输出函数add:

LIBRARY  Add

DESCRIPTION "ADD LA"

EXPORTS

 add  @1;

建完project后。你会发现没有Add.def文件,这时我们自己新建一个Add.def文件。然后加入到project中就可以,

加入Add.def文件到project后,我们还须要设置project的属性,将Add.def加入到【项目】-->【Add属性】-->

【链接器】-->【输入】-->【模块定义文件】。例如以下图所看到的,不这样设置的话,我们加入的Add.def文件是

不起作用的哦。

设置好后,编译,ok,我们获得了Add.dll

-----------------------------------------------------------------------------------------------------------------------------------------------------

得到Add.dll后。我们能够用一个小工具【dll函数查看器】来打开我们的Add.dll文件,假设函数导出成功的话,我们就能够

在里面看到导出的函数名字了,例如以下图所看到的:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZnJpZW5kYW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="border:none; max-width:100%">

该工具下载地址:http://download.csdn.net/detail/friendan/6347455       //dll函数查看器

----------------------------------------------------------------------------------------------------------------------------------------------------------

有了dll文件后。接下来我们新建一个MFC对话框程序来调用该dll中导出的函数add,

程序界面即执行效果截图例如以下:

主要代码例如以下:

//调用dll函数 add(int a,int b)
void CCallAddDlg::OnBnClickedBtnCallAdd()
{
HINSTANCE hAddDll=NULL;
typedef int (WINAPI*AddProc)(int a,int b);//函数原型定义
AddProc add;
if (hAddDll==NULL)
{
hAddDll=::LoadLibrary(_T("Add.dll"));//载入dll
}
add=(AddProc)::GetProcAddress(hAddDll,"add");//获取函数add地址 int a=1;
int b=2;
int c=add(a,b);//调用函数
CString tem;
tem.Format(_T("%d+%d=%d"),a,b,c);
AfxMessageBox(tem);
}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

接下来我们进行HOOK,即HOOK我们的Add.dll文件里的函数int add(int a,int b)

新建一个MFC的 dllproject。project名为Hook。然后我们在Hook.cpp文件中面编写代码例如以下:

首先在头部声明例如以下变量:

//变量定义
//不同Instance共享的该变量
#pragma data_seg("SHARED")
static HHOOK hhk=NULL; //鼠标钩子句柄
static HINSTANCE hinst=NULL; //本dll的实例句柄 (hook.dll)
#pragma data_seg()
#pragma comment(linker, "/section:SHARED,rws")
//以上的变量共享哦! CString temp; //用于显示错误的暂时变量
bool bHook=false; //是否Hook了函数
bool m_bInjected=false; //是否对API进行了Hook
BYTE OldCode[5]; //老的系统API入口代码
BYTE NewCode[5]; //要跳转的API代码 (jmp xxxx)
typedef int (WINAPI*AddProc)(int a,int b);//add.dll中的add函数定义
AddProc add; //add.dll中的add函数
HANDLE hProcess=NULL; //所处进程的句柄
FARPROC pfadd; //指向add函数的远指针
DWORD dwPid; //所处进程ID
//end of 变量定义

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

编写鼠标钩子安装、卸载和处理函数:

//鼠标钩子过程,什么也不做,目的是注入dll到程序中
LRESULT CALLBACK MouseProc(int nCode,WPARAM wParam,LPARAM lParam)
{
return CallNextHookEx(hhk,nCode,wParam,lParam);
} //鼠标钩子安装函数:
BOOL InstallHook()
{ hhk=::SetWindowsHookEx(WH_MOUSE,MouseProc,hinst,0); return true;
} //卸载鼠标钩子函数
void UninstallHook()
{
::UnhookWindowsHookEx(hhk);
}

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

在dll实例化函数InitInstance()中,初始化变量和进行注入:

//在dll实例化中获得一些參数
BOOL CHookApp::InitInstance()
{
CWinApp::InitInstance(); //获得dll 实例,进程句柄
hinst=::AfxGetInstanceHandle();
DWORD dwPid=::GetCurrentProcessId();
hProcess=OpenProcess(PROCESS_ALL_ACCESS,0,dwPid);
//调用注射函数
Inject();
return TRUE;
}

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

编写注射函数。即HOOK函数Inject()了:

//好,最重要的HOOK函数:
void Inject()
{ if (m_bInjected==false)
{ //保证仅仅调用1次
m_bInjected=true; //获取add.dll中的add()函数
HMODULE hmod=::LoadLibrary(_T("Add.dll"));
add=(AddProc)::GetProcAddress(hmod,"add");
pfadd=(FARPROC)add; if (pfadd==NULL)
{
AfxMessageBox(L"cannot locate add()");
} // 将add()中的入口代码保存入OldCode[]
_asm
{
lea edi,OldCode
mov esi,pfadd
cld
movsd
movsb
} NewCode[0]=0xe9;//实际上0xe9就相当于jmp指令
//获取Myadd()的相对地址
_asm
{
lea eax,Myadd
mov ebx,pfadd
sub eax,ebx
sub eax,5
mov dword ptr [NewCode+1],eax
}
//填充完成,如今NewCode[]里的指令相当于Jmp Myadd
HookOn(); //能够开启钩子了
}
}

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

编写HOOK开启和停止函数HookOn()和HookOff()

//开启钩子的函数
void HookOn()
{
ASSERT(hProcess!=NULL); DWORD dwTemp=0;
DWORD dwOldProtect; //将内存保护模式改为可写,老模式保存入dwOldProtect
VirtualProtectEx(hProcess,pfadd,5,PAGE_READWRITE,&dwOldProtect);
//将所属进程中add()的前5个字节改为Jmp Myadd
WriteProcessMemory(hProcess,pfadd,NewCode,5,0);
//将内存保护模式改回为dwOldProtect
VirtualProtectEx(hProcess,pfadd,5,dwOldProtect,&dwTemp); bHook=true;
}
//关闭钩子的函数
void HookOff()//将所属进程中add()的入口代码恢复
{
ASSERT(hProcess!=NULL); DWORD dwTemp=0;
DWORD dwOldProtect; VirtualProtectEx(hProcess,pfadd,5,PAGE_READWRITE,&dwOldProtect);
WriteProcessMemory(hProcess,pfadd,OldCode,5,0);
VirtualProtectEx(hProcess,pfadd,5,dwOldProtect,&dwTemp);
bHook=false;
}

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

编写我们自己的Myadd函数()

//然后,写我们自己的Myadd()函数
int WINAPI Myadd(int a,int b)
{
//截获了对add()的调用。我们给a,b都加1
a=a+1;
b=b+1; HookOff();//关掉Myadd()钩子防止死循环 int ret;
ret=add(a,b); HookOn();//开启Myadd()钩子 return ret;
}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

然后别忘记在hook.def里面导出我们的两个函数 :

InstallHook  

UninstallHook

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZnJpZW5kYW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="border:none; max-width:100%">

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

接下来就能够进行HOOK的測试了,给前面的对话框程序,再加入两个button。一个用于安装钩子,还有一个用于卸载钩子,

程序和执行效果截图例如以下:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZnJpZW5kYW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="" style="border:none; max-width:100%">

//未HOOK之前

//HOOK之后

-------------------------------------------------------------------------------------------------------------------------------------------------------------------

安装钩子和卸载钩子主要代码例如以下:

HINSTANCE hinst=NULL;
//安装鼠标钩子,进行HOOK
void CCallAddDlg::OnBnClickedBtnStartHook()
{
typedef BOOL (CALLBACK *inshook)(); //函数原型定义
inshook insthook; hinst=LoadLibrary(_T("Hook.dll"));//载入dll文件
if(hinst==NULL)
{
AfxMessageBox(_T("no Hook.dll!"));
return;
}
insthook=::GetProcAddress(hinst,"InstallHook");//获取函数地址
if(insthook==NULL)
{
AfxMessageBox(_T("func not found!"));
return;
}
insthook();//開始HOOK
} //卸载鼠标钩子,停止HOOK
void CCallAddDlg::OnBnClickedBtnStopHook()
{
if (hinst==NULL)
{
return;
}
typedef BOOL (CALLBACK *UnhookProc)(); //函数原型定义
UnhookProc UninstallHook; UninstallHook=::GetProcAddress(hinst,"UninstallHook");//获取函数地址
if(UninstallHook!=NULL)
{
UninstallHook();
}
if (hinst!=NULL)
{
::FreeLibrary(hinst);
}
}

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

以上就是之前我看的那篇文章的主要内容了,关于HOOK系统API,我会在其他的文章里面进行说明。

这里再说一下原文的缺点,我觉得其有两个缺点:

1.停止HOOK时,没有恢复被HOOK函数的入口。

2.没有处理dll退出事件,没有在dll退出事件中恢复被HOOK函数入口。

以上两个缺点。非常easy导致程序的崩溃,因此在我的样例程序中,都对它们进行了处理:

//卸载鼠标钩子函数
void UninstallHook()
{
if (hhk!=NULL)
{
::UnhookWindowsHookEx(hhk);
}
HookOff();//记得恢复原函数入口
} //dll退出时
int CHookApp::ExitInstance()
{
HookOff();//记得恢复原函数入口
return CWinApp::ExitInstance();
}

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

以上我这个样例project的下载地址:hook dll文件里的函数add.zip

http://download.csdn.net/detail/friendan/6348209

友情提示:我在Debug模式执行程序时,HOOK会失败,在Release模式执行程序则HOOK成功。

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Windows API Hook的更多相关文章

  1. 逆向实用干货分享,Hook技术第一讲,之Hook Windows API

    逆向实用干货分享,Hook技术第一讲,之Hook Windows API 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) ...

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

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

  3. Windows API Hooking in Python

    catalogue . 相关基础知识 . Deviare API Hook Overview . 使用ctypes调用Windows API . pydbg . winappdbg . dll inj ...

  4. 学习之路三十九:新手学习 - Windows API

    来到了新公司,一开始就要做个程序去获取另外一个程序里的数据,哇,挑战性很大. 经过两周的学习,终于搞定,主要还是对Windows API有了更多的了解. 文中所有的消息常量,API,结构体都整理出来了 ...

  5. API HOOK

    API HOOK技术是一种用于改变API执行结果的技术,Microsoft 自身也在Windows操作系统里面使用了这个技术,如Windows兼容模式等. API HOOK 技术并不是计算机病毒专有技 ...

  6. API HOOK技术

    API HOOK技术是一种用于改变API执行结果的技术,Microsoft 自身也在Windows操作系统里面使用了这个技术,如Windows兼容模式等. API HOOK 技术并不是计算机病毒专有技 ...

  7. API Hook完全手册

    文章来源: http://blog.csdn.net/atfield 原文作者: ATField 整理日期: 2008-07-16 发表评论 字体大小: 小 中 大   注:本文是根据我两年前写的一个 ...

  8. API HOOK库

    API HOOK库 API HOOK有两种做法,一种是SetWindowHookEx,简单易用,但如果做其它的HOOK,如HOOK OpenProcess,就需要修改内存地址了,内存地址可以通过Wri ...

  9. API HOOK和PE文件的关系

    api hook技术的难点,并不在于hook技术,而在于对PE结构的学习和理解.如何修改api函数的入口地址?这就需要学习pe可执行文件(.exe,.dll等)如何被系统映射到进程空间中,这需要学习p ...

随机推荐

  1. 题解 CF821D 【Okabe and City】

    其实,这道题不用long long也能AC. 题意是给你一个矩阵,有一些格子被点亮有一些没有,每一次只能在被点亮的格子上面走. 然后你每一次都可以选择点亮一行或一排(非永久),现在问你最少点多少次可以 ...

  2. Windows 8.1硬盘安装Ubuntu 14.04双系统

    Windows 8.1硬盘安装Ubuntu 14.04双系统 学习了: http://www.jb51.net/os/windows/298507.html http://www.linuxidc.c ...

  3. 我是怎么从项目中的lib加JAR更换为maven管理的

    原来我对maven的使用应该还是去年的时候吧,当时对maven并不感冒(请不要吐槽哈),认为为什么一定要用maven来管理呢,我自己管理jar不是一样么,当时还认为自己管理jar还各种方便还对mave ...

  4. hdu 5335 Walk Out 搜索+贪心

    Walk Out Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total S ...

  5. VS2008执行MFC程序,提示microsoft incremental linker已停止工作解决方法

    事实上这边是由于设置有问题.详细的解决方式例如以下: 第一步:点击项目->"你的文件"属性->配置属性->链接器->启用增量链接   将  是(/INCRE ...

  6. ORA-01261: Parameter db_recovery_file_dest destination string cannot be translat

    查看了Oracle的初始化文件. $cat /oracle/oms/102_64/dbs/initSID.ora 如:db_recovery_file_dest='/oracle/oms/flash_ ...

  7. luogu 1558 色板游戏

    写这篇博客不是为了总结我的算法,而是为了纪念让我爆零的套路..... 色板游戏 色板长度为\(L\),\(L\)是一个正整数,所以我们可以均匀地将它划分成\(L\)块\(1\)厘米长的小方格.并从左到 ...

  8. SpringMVC(一) 简单代码编写,注解,重定向与转发

    SpringMVC是什么 SpringMVC是目前最好的实现MVC设计模式的框架,是Spring框架的一个分支产品,以SpringIOC容器为基础,并利用容器的特性来简化它的配置.SpringMVC相 ...

  9. Oracle数据库to_date函数注意事项

    使用PL/SQL连接到Oracle数据库服务器,执行一条update语句: update pjnl set transtime = to_date('2015-05-14 12:13:20','yyy ...

  10. 你不知道的JavaScript博文参考书籍

    you don't know js系列书籍是谷歌地图开发人员编写,内容非常好,四卷已收集齐全. 笔者打包上传到了CSDN,下载地址: http://download.csdn.net/detail/r ...