一、Detours库的来历及下载:
Detours库类似于WTL的来历,是由Galen Hunt and Doug Brubacher自己开发出来,于99年7月发表在一篇名为《Detours: Binary Interception of Win32 Functions.》的论文中。基本原理是改写函数的头5个字节(因为一般函数开头都是保存堆栈环境的三条指令共5个字节:8B FF 55 8B EC)为一条跳转指令,直接跳转到自己的函数开头,从而实现API拦截的。后来得到MS的支持并在其网站上提供下载空间:
http://research.microsoft.com/research/downloads/Details/d36340fb-4d3c-4ddd-bf5b-1db25d03713d/Details.aspx
目前最新的版本是:Detours Express 2.1。
二、Detours的使用准备:
Detours库是以源码形式提供的,这给我们的使用带来极大的方便。你可以选择把它编译成库、也可以直接把源码加入工程……形式使用。农夫采取的方法是编译成库后使用的。编译库的方法很简单,下载包中已经制作好了makefile,我们只须直接用vc下的nmake工具直接编译即可。鉴于前段时间在网上看见有部分朋友对此也存疑惑,农夫在此“浪费”一下空间,详细解说一下编译的过程(括号中为本人的例子):
1、运行你下载的安装包,把文件解压到磁盘上
此处建议您把解压后的src文件夹拷贝到VC的安装目录的VC98子目录下(D:/SDK/6.0/VC98)。对于像我一样使用库的方式会有好处,稍后即讲:)。
2、编译并设置开发环境
在你刚才拷贝过去的src文件夹下建立一个*.bat文件,里面填上“../bin/nmake”内容后保存即可。运行该批处理文件,恭喜您:库已经编译完成,唯一要做的是要把../bin/Detoured.dll拷贝到您的系统目录下。现在您的工程里面包含如下文件即可运用Detours库来进行开发了:
#include <detours.h>
#pragma comment(lib, "detours.lib")
#pragma comment(lib, "detoured.lib")
对于没有把src文件拷贝过来的朋友,也不用着急。bat文件中把路径用全路径即可进行编译。不过你除了拷贝.dll文件外,还得要把.lib、.h文件拷贝到VC目录下,或者在你的VC环境中设置这些文件的包含路径,才可正常使用VC进行开发。看,是不是要麻烦些?
另外的建议:在Detours.h文件中定义一个函数指针类型,到用的时候您就知道会很方便了:
typedef LONG (WINAPI* Detour)(PVOID*, PVOID);
三、几个重要的函数:
1、DetourAttach & DetourDetach
这两个函数就是实际实现API挂钩的(改写头5个字节为一个跳转指令),前一个实现API拦截,后一个为在不需要的时候恢复原来的API。
第一个参数为自己定义的一个用于保存原来系统API的函数,该函数就相当于您没挂钩时的API。农夫习惯于在原有API的基础上加以“Sys”前缀来命名;
第二个参数为自己拦截API的功能函数,您想干什么“坏事”都是在这个函数中实现的。农夫习惯于在原有API的基础上加以“Hook”前缀来命名。
2、DetourCreateProcessWithDll
该函数是在以DLL注入方式拦截API时使用的,它其实就是封装“CreateProcess”,以“CREATE_SUSPEND”方式创建进程,然后修改IAT,把Detoured.dll和您的*.dll插入到它的导入表,然后再启动进程。所以它的参数就是在普通的CreateProcess基础上增加了两个DLL的路径参数,最后一个参数为创建进程的函数指针,默认为CreateProcessA!这点要特别注意:如果您的程序拦截了该函数,而在HookCreateProcessA中又调用DetourCreateProcessWithDll函数的话,一定要在此传入SysCreateProcessA,否则您的程序就会递归调用了,当心您的程序崩溃!至于为何要这么调用?呵呵,问我干嘛?:)
当然其它的API也很有用,不过对于开发者来说,这三个最重要!
四、开发实例:
随源码有一个帮助文档,上面列举了很多例子:包含普通的API、类成员函数、COM接口、DLL方式注入……。包含了我们的大多应用领域。
下面举个拦截CreateFileA函数的例子。该例子将所有创建的文件移动到指定目录下(晕,第一次使用,怎么不能插入C++代码?):
1、定义保存系统原来函数的函数SysCreateProcessA:(听起来有点拗口)
static HANDLE (WINAPI* SysCreateFileA)( LPCTSTR lpFileName, // pointer to name of the file
DWORD dwDesiredAccess, // access (read-write) mode
DWORD dwShareMode, // share mode
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // pointer to security attributes
DWORD dwCreationDisposition, // how to create
DWORD dwFlagsAndAttributes, // file attributes
HANDLE hTemplateFile // handle to file with attributes to copy
) = CreateFileA;
2、编写自己的功能函数:
HANDLE WINAPI HookCreateFileA( LPCTSTR lpFileName, // pointer to name of the file
DWORD dwDesiredAccess, // access (read-write) mode
DWORD dwShareMode, // share mode
LPSECURITY_ATTRIBUTES lpSecurityAttributes, // pointer to security attributes
DWORD dwCreationDisposition, // how to create
DWORD dwFlagsAndAttributes, // file attributes
HANDLE hTemplateFile // handle to file with attributes to copy
)
{
char chDestFile[256];
strcpy(chDestFile, lpFileName);
if( strstr(lpFileName, "////.//") != NULL ) // 放过设备
{
// 创建的普通文件全部转到D盘去,这里没考虑是“读”访问
char *p = strrchr(lpFileName, '//');
if( p++ == NULL )
{
p = lpFileName;
}
sprintf(chDestFile, "D://%s", p);
}
// 创建文件,注意这里不可以再调用CreateFileA,否则您就递归调用了!取而代之的应该是SysCreateFileA!!!!
return SysCreateFileA(chDestFile, .....); // 后面的参数照搬下来就可以了
}
3、挂钩,用自己的函数替换系统API:
DetourAttach(&(PVOID&)SysCreateFileA, HookCreateFileA);
恢复时用DetourDetach即可。
这下运行您的程序,发现所有用CreateFileA创建的新文件都被转移到D盘下了。
五、几个问题:
1、字节编码:
很多Win32函数都有多字节A版和宽字符W版,之所以平时没有附加A或W后缀,那是因为编译器已经帮我们做了这个工作。但在汇编惜字节如金的条件下,如果有两种版本,请务必明确指出,不要用CreateFile这种函数;
2、应用对象:
该库适合于初学者在RING3下对大多数API的拦截。对于那些逆向高手来说,这个简直不值一提;
3、发布:
由于该库并没有包含在MS 的SDK中,所以要随程序一块打包Detoured.dll。当然如果您是直接用源码加入工程编译的则可免去这个文件;
4、拦截DLL参数设置:
拿上面CreateFile函数来说,假如我是想在文件名称符合特定条件下才将其转移到D盘上,比如当文件类型为TXT文件时才转移。我们可以把盘符“D”及文件类型“TXT”直接写死在程序里面。这样的话,假如有其它程序是把“PDF”文件放在F盘不是又要重写一个?
不要以为加一个接口就可以解决。如是,祝贺您步入了农夫当初的歧途:)!其实要传这个参数进去的实质是进程间通信问题。本人采取的是FileMapping,改写了一下Detours的源码。当然其它进程间通信方式也是可以的。
5、拦截DLL必须导出一个函数
一般搞个空函数就可以了。如果没有任何导出函数,您辛苦编写的DLL将无法工作,还可能为此花上一大堆时间去排查。像如下声明一个就行了:
////////////////////////////////////////////////////////////////////////////////
// Must at least ONE export function:
__declspec(dllexport) void ExportFunc(void)
{
}
六、后记
乡村野夫,恍惚于世。本应扶犁,贸入IT。
三十而立,一事无成。慨殇岁逝,辗转反侧。
写文静心,闲以思远。悠悠我祖,自爱陶潜。
拙作开篇,文中如有不当/错误之处,烦请各位朋友留下您宝贵的笔墨。
======================
初版写了很久怎么都不见??????