个人认为学习分两种,
- 一种是当面请教和直接从书本网络中的资料学习.
- 其二就是看着令你惊叹的作品-顿悟.
- 什么?顿悟不了?那我们就一起来逆向学习吧!差点忘了,我并不打算提供Demo,这并不重要,难道你打算遇到一个相同的情景?重在方法.
注意:
本文为了照顾新手人群,对于某些内容可能会非常详细的推演.
名词解释:
- 一个实例:一个对象,这里指一个程序被创建后在内存中的数量.大白话就是:成功运行了几次.
事由:
今天闲来无事(忙里偷闲)运行了两次Dr_COM的宽带认证客户端.如咱所料,
如图:
我们知道,限制程序只运行一个实例的方法很多,如下主流的几种:
- 查找窗口.
- 使用互斥对象之类的.病毒经常也这么干.
- 使用共享区块.
- 使用临时文件.
那我们的COM先生是否如何实现的呢?
开弓or工
好的操起家伙(OllyDBG,IDA)开始一次内科手术.
如果你不是很熟悉这两个工具可以在需要的时候参考这里:
首先,用OD载入我们Dr.COM
这里的入口点似乎是早期的VS版本编译的Release版本C/C++程序.用工具ExeInfoPE验证一下我们的经验并简单看下是否是伪装壳:
ExeInfoPe的使用方法及介绍可以看这里:
我们需要找到哪句提示语“程序xxx已经在运行”,在系统中的位置.在界面中央(反汇编窗口)右键,选择“中文字符串搜索->智能搜索”(这是一个插件,你可以从主流的OD版本中相同位置找到类似的字符串查找功能).弹窗如图:
很幸运,我们找到了它.我们并不是每次都这么幸运,因为一些程序员会将它们加密存储.对于处理种种加密情况我们有机会再谈.
双击这个字符串即可来到这个字符串的地址.
我们看到一个红色箭头指向这里.而本字符串地址的上面是一个jmp,它指向了下面的某处,这表明指令执行的时候只能从远处跳到本字符串地址而不可能是一行一行走下来的.
请牢记我们的目的,我们的目的是找到程序如何进行”是否运行了多个实例”的判断.这个判断导致指令走向两条或者更多的路.其中一条就是这条提示信息.顺藤摸瓜即可.
我们逆着跳到本字符串地址的箭头找到根源.
如上图,我们找到了jmp的根源它就在我们圈住的地方.这是一个动态的jmp.它根据eax的值进行运算,eax*4+0x401D98,运算结果是一个地址跳转表,指向结果.eax就是关键.是谁修改了eax呢?
这里有条mov 指令,原来eax的值来自于[esp+0x174],而esp是栈顶寄存器,只有在运行到该条指令的时候才会有真正参与运算的值.好的.F2在该条指令下断点.然后运行起我们的程序,聪明的OD已经帮助我们计算出了地址,原来eax的值来自于ss:[0018FC64].ss是段选择寄存器,就是我们右下角的窗口显示的内容咯.
我们要监视这个地址是谁将’1’这个值写入其中的.这里提供三种方法:
- 紧盯住该地址的内容,我们通过栈回溯跟踪到修改者.
- 通过硬件断点,在左下角窗口-“数据窗口”找到该地址,右键”断点”->”写入断点”.长度根据具体情况,这里WORD就行.
- 使用RUN跟踪.菜单栏”调试”->”设置条件”,将中断条件设置为该地址为’1’则中断.至于关于强大的RUN跟踪的使用方法,如果你感兴趣,你需要找OD的资料学习.
下图描述了观察栈回溯的两种方法:
皇天不负有心人,我最终找到了将’1’写入的操作,它的地址是:403992.如图:
检测到多个实例的情况已经找到.那么单个示例运行的情况如何呢?我们关闭上面打开的两个Dr.COM程序.现在我们要实验一下只运行一个该程序的时候eax是多少?
将程序拖入OD中一个客户端,找到我们上次的eax*4+0x401D98的地方.发现正常情况下eax的值为A.继续用上面的方法寻找是谁修改了这个eax,找到了一个新的地址403466.这真是很醉,这个程序看来是在判断之后进行了不少其他任务才进行push操作的.VS2005…
既然我们有了两次操作的地址,只要找到它们的共同调用者即可.一般通过反复对比OD中的栈回溯可以帮助我们找到它们的祖先函数.但是这次我不打算用这种毫无趣味的方法.
IDA上场!!!!
IDA作为一款擅长图形化程序流程的超棒静态分析工具非常适合此次任务.将程序拖入IDA.
小经验:注意:不要把快捷方式拖进去.OD是会将快捷方式指向的程序进行分析,**而**IDA连快捷方式本身都不放过!
如图:
如果你发现你的界面是汇编界面而不是像上图的流程图,这往往意味你刚刚不小心关闭了流程图.放心,你可以通过View->Opensubviews->Proximity browsers 重新打开它.
在图形界面按”G”,写上push 0xA的地址”403466”如图:
OK,跳过去:
同样的办法按”G”,写上push 0x1的地址.找到push 0x1在流程图的显示.
在IDA中使用你的Ctrl+鼠标滚轮,可以放大or缩小流程图.让你感觉到更直观.
我的天两幅图竟然没有在一幅”大图”里面!!!这意味这两个函数在调用上相隔很远.
我们通过”大图”的顶端.点击函数名称,按住”X”键可以看到交叉引用的界面.这项功能非常实用,它可以将所有对目标函数或变量的调用列出来.我们看到对于push 0xA这一路的顶端函数只有一个父函数调用过它.双击这个父函数.
这下面的图中,我们看到它们的关系:
让我们放大分道扬镳之地.如图:
从后往前看,如果cmp结果是0,就会跳到右边(loc_403A99),没有发现已经运行了本程序.cmp比较的是eax是否等于0FFFFFFFF(八个F).如果等于就是正常.OK,显然是call sub_40A4E0做了某种判断导致eax的值.
现在记住eax如果等于0FFFFFFFF就是没有检测到其它示例.
在OD中进入这个函数:
发现新大陆
我和我的小伙伴们都惊呆了.这完全超出了我的预期.本来以为这个程序会用传统的实例检测方法,但是我们看到了CreateFileMappimg之类的函数.初步推断此程序利用多次映射文件产生的冲突来做的文章.继续看:
我们发现一个新的分道扬镳的跳转,它位于40A529.
它用下面这句做了比较:
cmp ebp,0xB7
向上追溯:
mov ebp,eax
ebp来自eax.按照约定,函数的调用可以修改eax的值来达到传递结果的目的.而这里的eax正是GetLastError函数的执行结果.
地址40A529的代码的意思是如果执行GetLastError返回的不是0xB7,则跳走.跳走的结果是:
0040A54A mov eax,edi
而此时的edi为FFFFFFFF,回想上面,这个值意味着我们没有检测到另一个示例在运行.
有同学可能会问,’0xB7是嘛意思呀?随机的一个值?’.当然不是,0xB7是错误码,它拥有明确的含义,你可以通过VisualStudio中菜单栏中的工具->”错误查找”小工具,找到它的含义:
果然如此.
收工总结
到此我们已经达到了我们预期的目的-探明Dr. Com检测到自己是否只运行了一个实例的方法.通过逆向分析了解到它是通过:
设置CreateFileMapping函数的最后一个参数MapName,该参数是一个共享值.如果该值被重复命名,会报错,该错误可以通过GetLastError函数得到,返回值为ERROR_ALREADY_EXISTS(0xB7).
知识拓展
更多关于该函数的情况可以阅读这里:
学到了什么?
预计一名纯新手读者通过阅读本文会学到:
- OD对程序的基本分析方法.
- 栈回溯概念
- 利用IDA流程图分析程序.
- 一种独特的进程互斥方法.(目的)
- ……
我从中学到了什么:
- 复习了以上所有知识
- 沿途看了数个感兴趣的WindowsAPI的实现.
- 直接下断常用互斥函数找捷径差点被坑.
- 了解了一些字符串函数和目录路径设置函数的用法.
- ……
尾声
在我幼稚园时,老师问我,”你从哪里懂得这么多”.”我的老师是书本.”我回答道.
有人说书本是不会说话的老师.如果你学到更多知识,请珍惜和程序在一起的时光吧.
有问题请留言评论以便讨论.
如果本文为你来到了新鲜空气,请点(~ o ~)~
版权声明:本文为博主原创文章,未经博主允许不得转载。