11.4.1 逆向Defender的初始化程序
因为程序显然没有直接调用任何API函数,所以你也就无法找到一个可以设置断点的API函数供你找到程序中输出“Sorry … Bad key, try again.”消息的代码。因此除了检查程序的入口点来碰碰运气,看能不能找到一些有所启示的代码外,你没有其他选择。让我们在IDA中加载这个程序,然后仔仔细细地分析它。现在你可以快速浏览一下这个程序入口点处的代码。
列表11.6 由IDA产生的Defender的入口函数的反汇编
代码(待续)
列表11.6(续)
列表11.6给出了Defender的入口函数。快速地扫描一下这个函数,你会发现一个很重要的特征——这里的入口点并非一个普通的运行库初始化程序。即使你之前从来没见过运行库初始化程序,你也可以非常肯定它不可能以IsDebuggerPresent函数调用结尾。当程序执行到IsDebuggerPresent调用时,你可以看到函数一返回EAX就与自己做了一次异或操作(XORed)——其返回值居然被忽略了!在网址http://msdn.microsoft.com上快速搜索一下,我们可以得知IsDebuggerPresent函数应该根据当前是否存在调试器而返回一个布尔(Boolean)值。在这个API函数刚刚返回就对EAX做异或意味着这个调用没有任何的实际意义。
不管怎样,我们回到列表11.6中前边的代码,了解一些有关Defender的信息。我们来看看对402EA8处的调用做了些什么。
前面的程序以一段有趣的代码序列开始:从fs:30h中加载了一个值到EAX寄存器。一般来说,在基于NT的操作系统中,fs寄存器用于访问线程本地信息(thread local information)。对任一给定的线程,fs:0指向的都是本地线程环境块(Thread Environment Block,TEB)数据结构,这个数据结构中包含了大量在运行时系统所需的线程私有(thread- private)信息。在这里,函数访问了TEB中偏移地址+30处的信息。幸运的是,你可以得到详细的Windows的符号信息(symbolic information),通过这些符号信息你可以知道TEB中偏移地址+30处存放的是什么信息。通过在WinDbg中加载NTDLL的符号并使用DT命令你就可以得到这些符号信息(关于WinDbg和DT命令的更多信息你可以参考Microsoft Debugging Tools的网页,网址是:www.microsoft.com/whdc/devtools/debugging/default.mspx)。
TEB的结构列表非常长,所以在这里我只列出了其中的前边部分,一直到偏移地址+30处——也就是程序访问的那个偏移地址。
很显然,.h3mf85n:00402EA9这一行是通过TEB访问进程环境块(Process Environment Block,PEB)。PEB是Windows中的进程信息数据结构,就像TEB是线程信息数据结构一样。在地址00402EB5处程序访问了PEB中偏移地址+c处的信息。让我们看看这里存放的是什么。同样,整个PEB的定义也非常长,所以这里我只列出了定义的开始部分。
在这里,偏移地址+c处是_PEB_LDR_DATA,也就是加载程序(loader)信息。我们来看一下这个数据结构,弄清楚它内部到底有些什么。
看起来这个数据结构是用来管理在当前进程中加载的可执行模块的。这个数据结构内有好几个模块列表,每个模块列表中都包含了当前进程加载的不同顺序的可执行模块。这个函数读取偏移地址+c处,也就是访问InLoadOrderModuleList模块。我们看一下模块数据结构LDR_DATA_TABLE_ENTRY,并试着理解这个函数在找什么东西。
下面关于LDR_DATA_TABLE_ENTRY的定义是在WinDbg中使用DT命令生成的。一些Windows的符号文件实际上都可以使用这个命令来转储其中所包含的数据结构定义。你只需键入“DT ModuleName!*”,就可以得到模块中所有名字的列表,然后再键入“DT ModuleName!StructureName”即可看到其成员的完美的列表。
在获取了指向InLoadOrderModuleList的指针后,函数好像读取了第一个模块中偏移地址+0处的信息。从这个结构中可以看出,这个偏移地址+0处是LIST_ENTRY数据结构的一部分。我们转储LIST_ENTRY,看看偏移地址+0处是什么内容。
偏移地址+0处是Flink,它可能是指的是“前向链接(foreward link)”。这就是说函数通过硬编码的方式跳过第一个入口,而不管这个入口中是什么。这很异乎寻常,因为对一个链表(linked list)来说你会想着应该有个循环才对——这里却没有循环,函数通过硬编码的方式跳过第一个入口。在这之后,函数只是简单地返回第二个入口中偏移地址+18处的值给主调函数。_LDR_DATA_TABLE_ENTRY中偏移地址+18处是DllBase。所以,看上去函数所做的只是为了寻找某个DLL的基地址。这时,用WinDbg加载Defender.EXE是明智之举,我们查看一下加载程序的信息,看看第二个模块是什么。为此,我们使用了“!dlls”命令,“!dlls”可以转储得到一个界面友好的(相对而言)加载程序数据结构的视图。选“-l”参数,命令会以加载顺序转储模块,实际上就是你刚才从PEB_LDR_DATA中取InloadOrderModuleList所遍历的列表。