API Hook的几种实现

作者在 2010-08-02 23:13:08 发布以下内容
API Hook的几种实现

所谓的API Hook,就是利用某种技术将API的调用转为我们自己定义的函数的调用。这种技术在实际项目里面的应用也是很广泛的。最近,我在做关于我们项目的自动化 测试的时候,就遇到了这种情况。在写测试代码之前,我们对测试代码有一些要求。1. 不能因为测试代码而修改原代码。2. 原有的模块是以dll格式输出的,在做测试的时候,要测的类和函数也只能使用dll的导出类或者函数,而不能将源文件重新编译。由于这些限制,导致测试用 例往往不能在普通的机器上运行。比如这样一个函数:

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->int func()
{
    
//Some initializing codes
    int hardware_code = get_hardware_code();
    
if (is_valid_code(hardware_code))
    {
        
//
    }
    
//
    return ret;
}

此处,函数get_hardware_code()是与特定平台相关的,在普通PC上运行肯定无法获得正确的结果。如果拿不到正确的结果,也就不能 对函数func()进行测试了。于是,我们就可以利用API Hook技术,在测试代码里面,把所有对get_hardware_code()的调用换成我们自定义的函数 mock_get_hardware_code()的调用,这样,在我们自己定义的函数里面,可以返回一个有效的代码以保证原代码能够正确的往下执行。

经过研究,API Hook有这么几种方法。

1. 改写函数的首地址。

这个是在《Windows核心编程》里面大师提到的API Hook的方法之一。原理就是,首先获得要被Hook的函数的地址(比如get_hardware_code()),然后将其首地址之后的若干字节(通常 是5个字节)改成一条jmp指令,而jmp的目标地址就是自定义函数的地址(此处为mock_get_hardware_code())。这样,当函数每 次执行目标函数的时候,就会跳转到我们自定义的函数里面去。这种方法很简洁,据说在Win16的年代经常被使用。但是大师并不推荐,好像是因为这种方法在 多线程的环境下会有什么问题(具体的我忘记了,大家可以翻书看看)。

这样的话,只要我们能得到被调函数的地址,我们就可以随心所欲的修改。当然,由于大师的不推荐,我这种方法只是实现了一下,并未真正应用。

2. 改写导入表

这个也是《Windows核心编程》里面提到的,也是大师所推荐的。具体来说,就是遍历当前进程里面的所有模块,对其中每一个模块查找它的导入表。 如果找到被测函数所在的dll,并且发现这个函数,那么就把这个地址修改成自定义函数的地址。关于如何从导入表中发现被测函数,我也总结了两种方式。1) 对于一般的C导出函数,可以直接通过比较地址的方式去找,这个也在核心编程上面有一个小例子。2)对于C++中的导出的类成员函数而言,由于C++的指向 成员函数的指针和普通的指针有所差别(我没仔细研究过,从网上查的),在将一个成员函数指针转化成普通的函数指针的时候编译通不过,因此我采取了第二种查 找方式,也就是查找函数名。这还有一个问题,由于C++的导出成员函数名都进行了修饰,类似于?MethodName@ClassName@...@Z这 种怪异的名字,不过,只要知道类名和方法名,然后查找MethodName@ClassName字 符串就行。如果找到了这样的函数,然后在修改它的地址就行了。

另,关于导入表,大家可以去看雪论坛上有关Windows PE文件格式的介绍。此处就不多说了。

3. 改写虚函数表。

本来以为通过方法2就能hook住所有的导出类成员函数和普通函数,但还是出现了一个问题,因为我在尝试hook一个成员函数的时候,发现这个函数 根本没有在导入表里面。后通过反汇编发现,由于那个导出类成员函数是一个虚函数,因为在通过指针调用的时候,它实际上是从虚函数表里面获得的函数地址进行 调用的。因此对于hook这类函数,就需要改写它的虚函数表了。关于这个需要对C++的内存布局有所了解。我在这里就说一种比较简单的方式吧。

一般来说,对于某个含有虚函数表的C++类,this指针指向的地址,取值就是虚函数表指针。虚函数表指针指向了虚函数表,里面的每一个元素都指向 了实际要调用的函数的地址。因此,可以按照这样的方式访问虚函数表指针:

int** pVTable = (int**)this;

也就是将指向对象的指针强制转化成指针的指针,这样就可以通过取值就可以访问虚函数表:

(*pVTable)[0] = address of virtual function 1;

(*pVTable)[1] = address of virtual function 2;

...

因此,我们就可以改写虚函数的地址了,从而达到hook的目的。这种技术来源于网上。当然,我对C++的内存布局也不是十分的清楚,如果一个类进行 了多重继承,它的虚函数表是什么样子我也不太明白,这里只是说明了这样一种技术。

以上就是目前为止我应用到的三种API Hook的技术,其实实际应用到的也就是后两种,这两种技术能够满足我目前项目的需要了。如果还有其他关于API Hook技术的话大家也可以交流。

另外需要说明的一点是,上述三种方法中不管哪一种在改写地址的时候,由于Windows一般将那个地址所在的页面设置了保护属性,因为你需要用 VirtualProtect函数将页面改为可读可写的属性才能改写,否则会有异常的。

技术 | 阅读 2112 次
文章评论,共1条
vfdff(作者)
2010-08-11 00:17
1
#include &lt;stdio.h&gt;<br />
#include &lt;windows.h&gt;<br />
#include &lt;dbghelp.h&gt; // 需要win32dsk <br />
#pragma comment(lib,&quot;dbghelp.lib&quot;)<br />
#pragma comment(lib,&quot;user32.lib&quot;)<br />
<br />
typedef int (__stdcall *old_messagebox)( hwnd hwnd, lpctstr lptext, lpctstr lpcaption,uint utype );<br />
old_messagebox g_procoldmessagebox = null;<br />
<br />
int __stdcall hook_messagebox( hwnd hwnd, lpctstr lptext, lpctstr lpcaption,uint utype)<br />
{<br />
&nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;%s\t%d\r\n&quot;,__function__,__line__);<br />
&nbsp; &nbsp; &nbsp; &nbsp; if (null != g_procoldmessagebox)<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return g_procoldmessagebox(hwnd,lptext,&quot;不好意思,hook到了!&quot;,utype); <br />
&nbsp; &nbsp; &nbsp; &nbsp; else<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return messagebox(hwnd,lptext,lpcaption,utype); ;<br />
}<br />
<br />
<br />
int replace_iat(const char *pdllname,const char *papiname,bool breplace)<br />
{<br />
&nbsp; &nbsp; &nbsp; &nbsp; handle hprocess = ::getmodulehandle (null);<br />
&nbsp; &nbsp; &nbsp; &nbsp; dword dwsize = 0;<br />
&nbsp; &nbsp; &nbsp; &nbsp; pimage_import_descriptor pimageimport = (pimage_import_descriptor)imagedirectoryentrytodata(hprocess,true,<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; image_directory_entry_import,&amp;dwsize);<br />
&nbsp; &nbsp; &nbsp; &nbsp; if (null == pimageimport)<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return 1;<br />
&nbsp; &nbsp; &nbsp; &nbsp; pimage_import_by_name pimageimportbyname = null;<br />
&nbsp; &nbsp; &nbsp; &nbsp; pimage_thunk_data&nbsp;&nbsp;pimagethunkoriginal = null;<br />
&nbsp; &nbsp; &nbsp; &nbsp; pimage_thunk_data&nbsp;&nbsp;pimagethunkreal&nbsp;&nbsp;= null;<br />
&nbsp; &nbsp; &nbsp; &nbsp; while (pimageimport-&gt;name)<br />
&nbsp; &nbsp; &nbsp; &nbsp; {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (0 == strcmpi((char*)((pbyte)hprocess+pimageimport-&gt;name),pdllname))<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ++pimageimport;<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; if (! pimageimport-&gt;name)<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return 2;<br />
&nbsp; &nbsp; &nbsp; &nbsp; pimagethunkoriginal = (pimage_thunk_data)((pbyte)hprocess+pimageimport-&gt;originalfirstthunk&nbsp;&nbsp;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; pimagethunkreal = (pimage_thunk_data)((pbyte)hprocess+pimageimport-&gt;firstthunk&nbsp; &nbsp;);<br />
&nbsp; &nbsp; &nbsp; &nbsp; while (pimagethunkoriginal-&gt;u1.function)<br />
&nbsp; &nbsp; &nbsp; &nbsp; {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if ((pimagethunkoriginal-&gt;u1 .ordinal &amp; image_ordinal_flag) != image_ordinal_flag)<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pimageimportbyname = (pimage_import_by_name)((pbyte)hprocess+pimagethunkoriginal-&gt;u1 .addressofdata );<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (0 == strcmpi(papiname,(char*)pimageimportbyname-&gt;name))<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; memory_basic_information mbi_thunk;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; virtualquery(pimagethunkreal, &amp;mbi_thunk, sizeof(memory_basic_information)); <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; virtualprotect(mbi_thunk.baseaddress,mbi_thunk.regionsize, page_readwrite, &amp;mbi_thunk.protect); <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (true == breplace)<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; g_procoldmessagebox =(old_messagebox) pimagethunkreal-&gt;u1.function; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pimagethunkreal-&gt;u1.function = (dword)hook_messagebox;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pimagethunkreal-&gt;u1.function = (dword)g_procoldmessagebox;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; dword dwoldprotect; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; virtualprotect(mbi_thunk.baseaddress, mbi_thunk.regionsize, mbi_thunk.protect, &amp;dwoldprotect);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; break;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ++pimagethunkoriginal;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ++pimagethunkreal; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return 0;<br />
&nbsp; &nbsp; &nbsp; &nbsp; }<br />
&nbsp; &nbsp; &nbsp; &nbsp; int main()<br />
&nbsp; &nbsp; &nbsp; &nbsp; {<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; replace_iat(&quot;user32.dll&quot;,&quot;messageboxa&quot;,true);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; messagebox(null,&quot;enumiat user32.dll messageboxa true;&quot;,&quot;&quot;,mb_ok);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; replace_iat(&quot;user32.dll&quot;,&quot;messageboxa&quot;,false);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; messagebox(null,&quot;enumiat user32.dll messageboxa false;&quot;,&quot;&quot;,mb_ok);<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return getchar();<br />
&nbsp; &nbsp; &nbsp; &nbsp; }
游客请输入验证码
浏览1970380次