| 
注册时间2014-5-2
阅读权限30
最后登录1970-1-1UID75402 龙战于野 
 
 TA的每日心情|  | 开心 2015-8-2 16:07
 | 
|---|
 签到天数: 2 天 [LV.1]初来乍到 | 
 
 发表于 2015-2-12 21:16:11
|
显示全部楼层 
| 本帖最后由 F8LEFT 于 2015-2-12 23:03 编辑 
 唉,慢慢编辑,不知道为啥一插入图片就自动退出编辑界面。。。是bug吧,求修复。
 64位的不懂,我就以32位的版本来说说我的看法。实验环境为Win7 64 位,文件所在目录为 C:\Windows\System32,测试文件为uxtheme.dll。
 ①导出函数
 首先,Dependency载入分析,得到的结果如下:
 
 
 与64位版本的一样,存在着其他dll的导出函数作为自己的导出函数。这里提供的信息有点少,我们再用另外一个工具来查询,这里小弟就用自己写的一个小工具,其他类似的也是可以的
 
   可以看到,这里就显示出来该导出函数的RVA地址为 0x00011AE1,与隔壁的函数有着明显的位置差距。这是为什么呢,OD载入看一看。
 我这里OD载入的dll基地址为0x6F6B0000,后文用Base代替。
 这里先看10#函数,RVA为0x3BC15,OD跑到Base + 3BC15的地方看看,为:
 
   一个非常正常的导出函数,RVA直指函数地址。
 于是,同理的看11#,也就是这个有问题的函数了。RVA为0x00011AE1,所以我们到 Base + 0x00011AE1的地方去。
 
   代码有点怪,但是一时间也看不出有什么大问题。。。等等,这里是代码段吗?我们跑去数据窗口看一看这个地址。
 
   
 晕了,原来这是一堆文字,表示SHUNIMPL的第190#函数。
 也就是说,这里的导出表有两种,一种是直指本身的导出函数的RVA,另外一种是指向其他函数的函数名的字符串。恩恩,原来如此原来如此。
 看到这里,相信懂的人已经知道了要怎么处理了吧。
 
 ②定位
 首先,取导出表地址,这里略去,我已经取得为 RVA 0x00011124
 接着,到Base + 0x00011124的位置取看,导出表结构为IMAGE_EXPORE_DIRECTORY,这里我们取其成员 AddressOfFunctions,偏移为 0x1c h,位置如下
 
   看到指向的RVA为 0x0001114C,于是到Base + 0x0001114C中看,
 
  可以看到这里是一堆导出函数的入口RVA,每4个表示一个函数,如第一个DWORD 0x0003CC1F,就是序号为1的导出函数的RVA了。这里我们跳到第11个函数中去,图中我已经高亮显示了。RVA位置为 00011AE1,与我的小工具取到的是一样的,恩,相信已经知道了吧。就是这么简单。 
 ③编程。居然还有这种的导出函数的?那么调用时会怎么样啊?
 这个用序号导出的函数不易调用,估计微软也不太想让我们用户来使用,所以从这里入手有点困难,不过,幸好,我们可以找到另外一个相似DLL,就是Version.dll了。Dependency载入,查一下导出函数
 
   哈哈,VerLanguageNameA与VerLanguageNameW这两个函数竟然是来自Kernel32的,转个工具查得RVA分别为0x158B,0x15A5,赶紧去OD中看看
 
   哈哈,相同的手法,相似的味儿。只是这里导出的是其他dll的名称函数,而不是序号函数。这样一来,就容易多了。我们写个动态地址加载的程序来看看是如何调用这种函数的。
 这里我选择加载VerLanguageNameA函数
 
 然后用OD载入,看一看是动态调用的地址为啥复制代码#include <windows.h>
typedef DWORD (APIENTRY *pfnVerLanguageNameA)( _In_ DWORD wLang, _Out_writes_(cchLang) LPSTR szLang, _In_ DWORD cchLang );
int main(_In_ int _Argc, _In_reads_(_Argc) _Pre_z_ char ** _Argv, _In_z_ char ** _Env)
{
        HMODULE hModule = LoadLibrary(TEXT("version.dll"));
        pfnVerLanguageNameA NameA = (pfnVerLanguageNameA)GetProcAddress(hModule, "VerLanguageNameA");
        CHAR Msg[MAX_PATH];
        NameA(LANG_SYSTEM_DEFAULT, Msg, MAX_PATH);
        return 0;
}
   哇咔咔,虽然加载的是Version.dll,GetProcAddress的是Version中的VerLanguangNameA,但是实际上得到的地址却是kernel32中的 VerLanguangNameA,原来系统没有这么笨,自动帮我们转过去了。所以,大家懂的,虽然dll中有其他dll中的导出函数定义,也可以调用,但是实际上调用到的地址还是其他dll中的导出函数地址。自己dll本身却是一点代码段都不含的。唉,既然这样,那么该如何弄才能达到这种效果啊。
 当然,这里可以按照一般Hook的思路,给出同名的函数,然后里面的跳转直接跳到有定义的dll中去,比如在写Version.dll中就可以 jmp [kernel32.VerLanguageNameA]这样汇编代码。何乐而不为呢?如果是序号呢?不怕,GetProcAddress同样可以通过序号来获取函数地址的。。。虽然微软并不建议这么做。。。(微软吐槽:弄成序号就是不想你调用了,干嘛非得强行调过去啊,要是版本一变,连序号都变了那么程序不就奔溃了?)怎么,怕出错,不妨试一下其他的方法。这里我以前写过version的Hook,其中的def文件定义文件是这样写的。
 
 其中Version_##???一系列函数时我在主文件中有声明过的转接的函数,而那个12#与13#的函数VerLanguangNameA与VerLanguangNameW则是连声明都不给一个的。编译时居然就能自动识别为kernel32的函数了,厉害吧,估计是因为这两个函数已经在windows.h中有定义了,所以编译器就能自动识别了。惯例Dependency中看一下复制代码LIBRARY version
DESCRIPTION "A simple dll"
EXPORTS
GetFileVersionInfoA=Version_GetFileVersionInfoA @1
GetFileVersionInfoByHandle=Version_GetFileVersionInfoByHandle @2            
GetFileVersionInfoExW=Version_GetFileVersionInfoExW @3
GetFileVersionInfoSizeA=Version_GetFileVersionInfoSizeA @4
GetFileVersionInfoSizeExW=Version_GetFileVersionInfoSizeExW @5
GetFileVersionInfoSizeW=Version_GetFileVersionInfoSizeW @6
GetFileVersionInfoW=Version_GetFileVersionInfoW @7
VerFindFileA=Version_VerFindFileA @8
VerFindFileW=Version_VerFindFileW @9
VerInstallFileA=Version_VerInstallFileA @10
VerInstallFileW=Version_VerInstallFileW @11
VerLanguageNameA @12
VerLanguageNameW @13
VerQueryValueA=Version_VerQueryValueA @14
VerQueryValueW=Version_VerQueryValueW @15
   恩恩,差别出来了,OD中看看,
  与原dll的效果有着微妙的不同的一点还真是让人气愤,不过,算了,既然都能自动的跳过去就原谅了吧。{:soso_e112:}咪啪,拍键盘拍了这么久,手都累了,其他软件的进一步完善啊。 
 | 
 评分
查看全部评分
 |