detours 笔记
花了一周的时间来了解detours,现在终于有了进展。一周前下载了detours的时候,茫然了好一阵子才找到了头绪。现在总结下方便像我一样的初学者了。
我不再介绍Detours到底是什么了,因为已经有很好的帮助文档了,从detours官网上可以下载detours包。我下载的版本是detours2.1 express版本。包中有帮助文档,有detours的源码,还有很多例子。编译的时候,CMD下在detours的目录输入nmake,就可以编译了。需要注意的是,detours2.1平台要求是NT内核的,所以98下是不能用的。
编译好后,下面就说使用之前几点需要知道的,看帮助文档是可以获得这些信息的。了解了这几点,使用detours会更加明了。detours是通过在目标进程中”打桩“来完成拦截目标函数的(语出Sandy)。这个概念需要理清。介绍文档中有示意图,可以仔细看。detours修改的是目标进程在内存中的二进制映像,并不修改硬盘上的文件,所以可以恢复。
detours编译好后,有几个文件需要注意。detours.h包含了detours提供的API的声明,如果需要使用这些函数,需要包含此头文件。detours.lib就是这些API的实现,是静态链接库,跟DLL生成的lib文件是不同的,如果不是很清楚的话可以看我之前的一篇”Dll的两种链接方式与LIB”。当调用了detours的API的时候,需要在项目中声明,在程序连接时包含此lib,这样编译器就会将detours.lib链接到目标程序中生成最后的可执行程序,这个程序运行时就不再需要detours.lib了。还有一个是detoured.dll,使用了detours的话是总是需要在项目文件夹下添加此文件的,按照文档说明,detoured.dll 是一个标志,帮助微软的开发人员和工具判断某个进程是否已经被detours拦截。
下面创建一个非常简单的例子,VC2005拦截Sleep API。detours包中的例子中的simple也是拦截的Sleep API。
1. 首先创建一个Non-MFC DLL工程HookSleepDLL。将detours.h,detoured.dll,detours.lib三个文件拷贝这个工程目录中。在HookSleepDLL.cpp中添加三行声明:
#include "detours.h" #pragma comment(lib,"detoured.lib") #pragma comment(lib,"detours.lib")
2. 接着需要定义Sleep函数的指针:
static VOID (WINAPI * TrueSleep)(DWORD dwMilliseconds) = Sleep;
detours通过这个函数指针来找到Sleep在内存中的映像,当然作为常识,参数和返回值和Sleep需要一致,不然怎么叫这个函数的指针^.^。
3.定义替换Sleep函数的函数,这个函数的参数类型和返回值需要和Sleep函数一摸一样。
VOID WINAPI DelaySleep(DWORD dwMilliseconds){
TrueSleep(dwMilliseconds+15000);
}
我这里为了简单,就在原来Sleep的时间上加15秒。这就是说,如果Sleep被Hook住了,那么Sleep(1000)也就是睡一秒会变为睡16秒。
4.下面说明在DLL被加载的时候开始拦截Sleep API。
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { int error; switch(ul_reason_for_call){ case DLL_PROCESS_ATTACH: DetourTransactionBegin(); DetourUpdateThread(::GetCurrentThread()); DetourAttach(&(PVOID&)TrueSleep, DelaySleep); error = DetourTransactionCommit(); if(NO_ERROR!=error){ ::MessageBox(NULL,"Error!","Error in Detours!",MB_OK); } break; case DLL_PROCESS_DETACH: DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourDetach(&(PVOID&)TrueSleep, DelaySleep); error = DetourTransactionCommit(); ::MessageBox(NULL,"Detour ends","Prompt!",MB_OK); break; } return TRUE; }
用到的几个API简要说明下:
DetourTransactionBegin: 开始一个新的detours事务。
DetourUpdateThread:讲一个线程入队等待更新。(不知道理解的有没有偏差)
DetourAttach:将目标进程(Sleep)和替换它的函数DelaySleep绑定。第一个参数是一个指针,这个指针指向目标函数的指针(有点绕…)。
DetourTransactionCommit: 事务提交,如果不提交之前的更改是不会生效的。这个函数会返回一个值,如果是NO_ERROR 表明是成功,如果是其他的表示失败。
DetourDetach: 解除目标进程和替换函数的绑定,参数和DetourDetach一摸一样。
5.生成DLL。如果提示诸如无法将const char *转换到LPCSTR之类的错误,这是字符集的问题,将项目的属性设置为多字节的。或者使用_T,L之类的标记,具体可google。
测试程序。创建一个MFC对话框程序。将前一步生成的HookSleepDLL.dll和detoured.dll拷贝到当前项目目录下。在界面上画两个Button控件BtnTest和BtnLoadLibrary。在BtnTest的事件中写:
Sleep(1000);
MessageBox(NULL,”hello,test”,”test”,MB_OK);
说明在停一秒后弹出对话框。
然后在BtnLoadLibrary的事件中写:
HINSTANCE g_dll;
g_dll=LoadLibrary(“SleepHOOKDll.dll”);
6.编译运行,首先点击BtnLoadLibrary,将DLL加载到当前进程空间中。这个时候可以用IceSword查看模块信息,是否已经被加载。然后点击BtnTest,如果没有问题的话,效果会如预期,在16秒后才弹出对话框。
至此,一个简单的利用detours拦截API的例子已经完成。想要完成更复杂的工程是需要更多的知识储备和耐心的。在了解detours的时候,我顺便加深了对DLL的了解程度。需要特别注意的是,假如现在有一个DLL工程并没有输出任何的符号,在编译后有lib文件,当然这个DLL可以在被加载的时候干点事情。然后使用这个DLL的工程如果采用了静态链接的方式,那么编译器会对其进行优化,在进程运行时,并不会加载这个DLL。或者是你有输出函数,但是工程并没有调用任何函数,那么也会被优化掉。所以不错的选择是选择DLL的动态链接方式。
还有一点,作为DLL的补充吧,因为之后要完成一个进程保护程序,提前说下准备的知识吧,关于DLL的。windows为DLL等系统资源预留了2g-4g的虚拟内存空间地址,所以加载DLL的时候,系统会将DLL加载到这个位置,这样多个进程可以共享。所以虽然DLL可以被多个进程加载多次,但是系统只会保留一份在内存,也就是说系统检测到内存中已经有这个DLL,就不会再加载了,而是简单的映射下就可以了。这种共享在大家都只是读DLL的时候是没问题的,但是如果DLL中的某个变量可写,那么就会很危险。所以系统采取了COW(copy on write)机制。在某个进程需要写的时候,就单独申请空间,然后将这个新的页面映射到进程的写数据页面。这样,某个进程写数据并不会干扰其他的进程,因为改的只是一个拷贝。回到我们今天的主题detours上,拿上面的例子,拦截Sleep,也只是拦截本进程中调用Sleep的操作,并不会拦截其他进程调用Sleep操作,因为拦截时发生了写操作,所以修改的时候修改的是一份针对本进程的拷贝,并不是所有的进程。如果想达到拦截所有进程调用Sleep的操作,我目前想到的方法就是对所有的进程注入DLL实现拦截了。
终于写完了,两点多了,睡哦。
看完了^.^,如果觉得这篇文章对你有用或者有
问题,请留言告诉我,thank you !
文章为原创的话,转载请注明出处.不敢流泪-《detours 笔记》
7 Comments are ready?
总结得很好,顶一个!
[回复]
不过有点小问题,建立DLL工程名用 HookSleepDLL,生成DLL名应该为HookSleepDLL.dll,而在测试程序中却是 g_dll=LoadLibrary(”SleepHOOKDll.dll”);
总结不错,瑕不掩瑜,再顶一个!
[回复]
boluor 回复 于 十月 12th, 2009 at 23:07
呵呵,谢谢!今后多交流^.^
[回复]
我现在学习使用detours拦截API,像文件拷贝,创建进程啥的,就想利用detours在这些系统调用之前先做个日志,打算生成自己的dll,然后在外壳程序中调用我的dll。现在问题是自己的dll怎么生成,DllMain是必需的吗?利用detours生成自己的dll的流程是怎样的,呵呵,新手的问题比较多
[回复]
boluor 回复 于 十二月 20th, 2009 at 15:39
你可以先看看这篇文章:DLL基础。有问题再联系我。
[回复]
在不在呀,我现在昨天直接把你程序拿去测试的时候,提示 error LNK2005: _DllMain@12 已经在 HookSleep.obj 中定义(找到一个或多个多重定义的符号)。自己查了一天还是没解决。惭愧啊!!我现在知道的是,MFC 常规 dll 具有由 MFC 自动提供一个默认 DllMain 函数,这样就重定义了,但是我先定义自己的DllMain呀,你当时是怎么解决的哦???
[回复]
boluor 回复 于 四月 21st, 2010 at 10:57
在上面第1点我写的是,创建一个Non-MFC DLL工程。三种DLL区别可以参考《DLL基础》
[回复]
果然可以了,谢谢学长。哈哈………………
[回复]
boluor 回复 于 四月 21st, 2010 at 11:26
不谢~~顺提一句,你那个猜测没错。+U~
[回复]
jerry 回复 于 四月 21st, 2010 at 14:07
学长,你在不哦?把你qq告诉我吧。嘿嘿……
[回复]
谢谢你的讲解~我也照着做了~很棒~呵呵~不过我感觉等待了不止16秒呢~
想请教您的是:在我们的应用程序中,是用LoardLibrary,然后自己调用了Sleep函数来体现我们在DLL中的关于函数的修改。我想问的是,如果是别的API函数,而不是这个sleep的话,就是那些不是我们调用,而是系统调用的那些函数:比如TextOut一类的函数,那我们到底该怎么处理呀?
如果可以的话,你发个邮件到我的邮箱吧llinggao@gmail.com谢谢你喽~
但愿您能看到。但愿您能联系我~期待~
[回复]
boluor 回复 于 五月 16th, 2010 at 17:06
看你很急,先简单回复下。
在例子中我们手动显示地调用Sleep函数,是因为想确保在执行这个函数之前SleepHOOKDll.dll已经被加载。如果想拦截其他的函数,道理也一样,需要在他们执行之前加载相关的Hook Dll。可以采用静态链接的方式来保证在程序启动时就加载了DLL,但是请注意我在倒数第三段所提到的,编译器可能会把它优化掉,可以用一些方法暂时把优化关闭。比如#pragma optimize(“off”),还可以重新开启#pragma optimize(“off”)。相关的查下MSDN吧。
[回复]
linger 回复 于 五月 16th, 2010 at 22:47
好开心~你竟然能看到~
我不是很能理解“静态链接的方式”这句话。
我对静态链接和动态链接一个最浅显也是最基本的理解是:静态链接的执行程序能够在其它同类操作系统的机器上直接运行,比如一个EXE文件是在WIN2000系统上静态链接的,那么将该文件直接拷贝到另一台WIN2000的机器上,是可以运行的。 而动态链接的执行程序则不可以,除非把该EXE文件所需的DLL文件都一并拷贝过去,或者对方机器上也有所需的相同版本的DLL文件,否则是不能保证正常运行的。
我觉得我们把SleepHOOKDll.dll拷贝到我们的应用程序中,就是完成了一个动态链接的过程。不知我的理解对否。
其次,我想问,如果系统调用了比如TextOut 这个函数,虽然我在DLL工程中修改了这个TextOut函数,即:将TextOut 的东西也拷贝到了我的一个txt中,但是,我是怎么知道系统调用了这个TextOut函数呢?也就是说,我觉得我做的这个DLL并没能够挂钩到系统中,或者某个特定的进程中~要达到这个目的,到底该怎么做?
最后,在 http://topic.csdn.net/u/20100421/15/bebe3007-6ecd-4af6-b80f-c1a221c61ef9.html 中,这个人说,他已经将DLL注入IE了,我想问问你,这个注入是怎么注入的呀?如果我想把我编写的修改了TextOut 的DLL注入QQ进程中,该怎么做呀?
恳请你的回答~期待你的答复~
[回复]
boluor 回复 于 五月 16th, 2010 at 23:03
DLL的链接方式先看这篇文章吧。
Dll的两种链接方式与LIB
要想注入到其他的进程,就不仅仅是修改个DLL了。WIN32下进程都是在自己的虚拟的空间中运行,你的程序要想去把别的程序注入,就必须获得进入的特权,这就是线程注入的知识了。简单说就是OpenProcess,WriteProcessMemory,CreateRemoteThread等函数的使用。你搜下远程线程注入应该可以搜到很多。
ps:qq,360不是那么好注入的,它把自己放到一个沙盒中运行了。你不要着急,走一步问一步的方式对你学习无益,把进程,DLL,内存管理等等相关的好好看看再说吧。
[回复]
linger 回复 于 五月 18th, 2010 at 19:57
我看了下您所提到的沙盒的概念。有人解释:沙盘是一种安全软件,可以将一个程序放入沙盘运行,这样它所创建修改删除的所有文件和注册表都会被虚拟化重定向,也就是说所有操作都是虚拟的,真实的文件和注册表不会被改动,这样可以确保病毒无法对系统关键部位进行改动破坏系统。
我的理解为,沙盒或者说沙盘,虽说是一个安全软件,某种程度上,更像一个世外桃源的操作环境,在这个环境中进行的操作不改变系统的任何设置。不知我的理解对否。
另,我看到有人提及360有沙盒模式了,但是QQ好像没有提到~
这是否意味着我们可以像QQ中添加新的进程,注入DLL啦?
ps,I’ve send an email to your gmail,please check.
[回复]
linger 回复 于 五月 16th, 2010 at 22:50
准确地说,我想知道,到底怎么挂钩,即到底将我们写的DLL中进行的修改体现出来~可能是我看书太不够细心,但我真的看不懂~也不知道怎么做~
恳请你能告诉我~
[回复]
LZ,我想用wordpress做个blog网站,使用美国的服务器。发现你的站点速度挺快的,还能告诉我你是在哪个卖家买到的wordpress主机吗?
[回复]
boluor 回复 于 五月 19th, 2010 at 14:59
看这篇日志:今天给博客搬家啦
[回复]