6.3 隐藏DLL文件

前面的章节介绍了如何隐藏进程,隐藏进程的方法是把要在进程中完成的功能放在DLL文件中完成,然后将DLL文件注入到其他进程当中,从而达到隐藏进程的目的。现在要做的是隐藏进程中的DLL文件,当把DLL文件注入到远程进程后,可以将DLL也隐藏掉。操作系统在进程中维护着一个叫做TEB的结构体,这个结构体是线程环境块。下面就要通过WinDBG这个调试工具来一步一步地学习TEB,并通过TEB来学习如何隐藏DLL文件。

6.3.1 启动WinDBG

启动WinDBG工具,如图6-16所示。

依次单击菜单栏的“File” -> “Symbol File Path…”命令,在里面输入符号文件路径,这里直接填入微软为我们提供的符号服务器,“srvF:\Program Files\symbolcachehttp://msdl.microsoft.com/download/symbols”,如图6-17所示。

设置好符号路径,就可以开始调试了。这里调试的目标就是WinDBG,因为原理是相同的。我们来进行本地调试,依次单击菜单“File” -> “Kernel Debug”命令,出现如图6-18所示的窗口。

6.3 隐藏DLL文件 - 图1

图6-16 WinDBG启动界面

6.3 隐藏DLL文件 - 图2

图6-17 设置符号文件路径

6.3 隐藏DLL文件 - 图3

图6-18 Kernel Debugging窗口

选择“Local”选项卡,也就是进行本地调试,单击“确定”按钮。这样,就可以用WinDBG开始调试了,跟着步骤一步一步做就可以了。

6.3.2 调试步骤

首先获取TEB,也就是线程环境块。在编程的时候,TEB始终保存在寄存器FS中。获取TEB的命令为“!teb”。在WinDBG的命令提示处输入该命令,WinDBG将输出如下内容。

6.3 隐藏DLL文件 - 图4

从上面的输出内容可以看出,TEB地址为7ffde000。

获得TEB以后,通过TEB的地址来解析TEB的数据结构,从而获得PEB,也就是进程环境块,命令为“dt_teb 7ffde000”,WinDBG的输出内容如下:

6.3 隐藏DLL文件 - 图5

上面的输出只是部分输出,该结构体非常长,这里只查看其中的一部分内容,只要找到PEB在TEB中的偏移就可以了。从该命令的输出可以看出,PEB结构体的地址位于TEB结构体偏移0×30的位置,该位置保存的地址是7ffd5000。也就是说PEB的地址是7ffd5000,通过该地址来解析PEB,并获得LDR。在命令提示符处输入命令“dt nt!_peb 7ffd5000”,输出内容如下:

6.3 隐藏DLL文件 - 图6

6.3 隐藏DLL文件 - 图7

从输出结果可以看出,LDR在PEB结构体偏移的0×0C处,该地址保存的地址是001ale90。通过该地址来解析LDR结构体。在命令提示符输入命令“dt _peb_ldr_data 001ale90”, WinDBG输出如下内容:

6.3 隐藏DLL文件 - 图8

在这个结构体中,可以看到3个相同的数据结构,也就是在偏移0×0c、0×14、0×24处的3个结构体_LIST_ENTRY。该结构体是个链表,定义如下:

6.3 隐藏DLL文件 - 图9

上面这个结构体在SDK提供的帮助中是找不到的,需要去WDK的帮助中才可以找到。这3条链表分别保存的是_LDR_DATA_TABLE_ENTRY,也就是LDR_DATA表的入口。

现在来手动遍历一下第一条链表,输入如下命令“dd lalec0”。

6.3 隐藏DLL文件 - 图10

在这么多的输出中,在链表偏移0×18的位置是模块的映射地址,即ImageBase,在链表偏移0×28的位置是模块的路径及名称的地址,在0×30的位置是模块名称的地址。查看一下, lalec0偏移0×28的位置中保存的地址是20c64,接下来输入命令“du 20c64”。

6.3 隐藏DLL文件 - 图11

可以看到,输出WinDBG的全部路径,来看一下偏移0×18的地址,该进程的映射基址为01000000。再来看一下偏移0×30处的地址保存着20ca6,查看该地址,输入命令“du 20ca6”。

6.3 隐藏DLL文件 - 图12

的确是模块的名称。既然是链表,那么看看下一条链表的信息。

6.3 隐藏DLL文件 - 图13

6.3 隐藏DLL文件 - 图14

按照上面介绍的解析方法自己进行解析。

6.3 隐藏DLL文件 - 图15

上面介绍的几个结构体在VC6的头文件中是找不到的,不过在网上还是可以查到的。这里给出MSDN上给出的几个结构体的定义,该MSDN的地址为: http://msdn.microsoft.com/zh-cn/library/aa813708(v=VS. 85) . aspx,方便大家查看。涉及的几个结构体的定义如下:

6.3 隐藏DLL文件 - 图16

从这两个结构体中可以看出有非常多的保留字段,这些都是微软不愿意公开的,或不愿意让大家使用的。不过在网上有大量的相关结构体的具体定义,大家可以自行查找进行阅读。

看完上面的各种结构体是不是觉得我们自己都可以实现枚举进程中模块的函数了?是的,我们来写一个吧。

6.3.3 编写枚举进程中模块的函数

枚举进程中的模块的方法就是通过上面介绍的几个结构体来完成,其步骤是。

获得TEB地址->获得PEB地址->得到Ldr->获得第二条链表的地址->遍历该链表并输出其地0×18的值和0×28指向的内容。

只要把上面在WinDBG中找到链表的方法学明白,那么就不是太大的问题了。关键的问题是TEB怎么找到。告诉大家,TEB保存在FS中,有了这个提示就很好解决了吧?看代码吧。

6.3 隐藏DLL文件 - 图17

6.3 隐藏DLL文件 - 图18

该函数的实现没有太多的技巧,主要在于对C语言中指针的掌握,还有就是对以上介绍的几个结构体之间的关系能够掌握,也就是各结构体之间的数据关系。在main()函数中调用一下这个函数,输出结果如图6-19所示。

6.3.4 指定模块的隐藏

模块的隐藏是把指定模块在链表中的节点断掉,也就是做一个数据结构中链表的删除动作,只不过不进行删除,只是将其节点脱链即可,如图6-20所示。

6.3 隐藏DLL文件 - 图19

图6-19 自实现的枚举模块函数

6.3 隐藏DLL文件 - 图20

图6-20 链表节点脱链

如果是枚举模块的话,一般情况下,只要枚举第二条链表就可以了,也就是偏移0×14处的那条。如果要做模块隐藏的话,那最好是将3条链表中的指定模块全部都脱链。对于脱链的方法,其实也是对3条链表进行遍历,然后将指定的模块脱链就可以了。和上面枚举的方法差别不大。下面给出代码,如下:

6.3 隐藏DLL文件 - 图21

在main()函数中调用这个函数,主函数如下:

6.3 隐藏DLL文件 - 图22

接下来隐藏调用“kernel32.dll”这个模块,当然,这里的隐藏只能是这个程序运行时隐藏该进程中的“kernel32.dll”模块,对其余进程中的模块并没有影响。在程序的末尾使用getchar(),其用意是希望该进程可以停留住,否则它如果退出,便没有验证“kernel32.dll”模块是否真的被隐藏的机会了。编译连接并运行,然后用第3章中写的工具来查看一下,如图6-21所示。

6.3 隐藏DLL文件 - 图23

图6-21 查看HideModule中Kernel32.dll模块被隐藏

从图6-21中可以看出,在HideModule. exe进程中看不到Kernel32. dll的模块名。当然,就算用我们自己编写的枚举的模块函数也是没用的,因为模块已经不在链表中了。虽然这个程序把进程中“kernel32.dll”隐藏了,但是并没有多大的实际意义。隐藏模块主要是用在被注入的DLL中,也就是一个DLL文件被注入到远程线程中后,为了不被发现而隐藏。