作为一个恶意软件逆向工程师,我每天都频繁的使用IDA Pro。甚至将IDA Pro作为一个工业标准都不令人惊讶(虽然radare2和Hopper可以作为替代)。 IDAPython是IDA的一个功能强大的扩展特性,对外提供了大量的IDA API调用,我强烈推荐所有的逆向工程师使用。另外,你还能在使用python 脚本语言的过程中获得能力提升,最终也能使你获利。
不幸的是,除了下面这几项,关于IDAPython的信息和教程实在太少了。
- "The IDA Pro Book" by Chris Eagle
- "The Beginner's Guide to IDAPython" by Alex Hanel
- "IDAPython Wiki" by Magic Lantern
为了提升IDAPython相关的教程资料,我将会提供我写的一些有趣的实例代码。 在这个系列的第一部分,我会写脚本来解码一个恶意软件里面的大量被混淆字的符串。
背景
在逆向恶意样本的过程中,我遇到了下面这个函数:
基于过去的经验,我觉得这个函数应该是用来解密二进制数据的。这个函数的交叉引用次数证明了我的猜测:
正如图2所示,这个特殊的函数被调用了116次。这个函数的每一次调用,都有一个二进制数据对象通过ESI寄存器作为参数传入。
通过这一分析,我更加确定这个函数是恶意软件运行时用来解密字符串的。面对这一情况,我可以选择下面这几种解决方案:
- 手动解密并重命名这些被混淆的字符串
- 运行这些样本,遇到这些字符串的时候进行重命名
- 编写一个脚本来解密并重命名这些字符串
如果恶意软件只是解密少量的字符串,我会选择第一种或者第二种方案。然而,正如我们前面了解到的,这个函数被调用了116次,所以编写一个脚本似乎更有意义。
编写IDAPYTHON脚本
解决混淆字符串问题的第一个步骤是找到并重写解密函数。幸运的是,这里的解密函数比较简单。这个函数简单的将二进制数组中的第一个字符与剩下的数据逐字节的进行异或。
E4 91 96 88 89 8B 8A CA 80 88 88
在上面这个例子中,取出0XE4跟剩下的其他数据进行异或。解密的结果是'urlmon.dll'。我们可以用python这样实现:
运行这段代码,我们得到了我们预期的结果
接下来就是找出代码中引用了解密函数的地方,提取作为参数出入的数据。 通过IDA找到函数的引用比较简单,IDA提供的API 函数XrefsTo()完美的解决了这个问题。下面这个脚本中,我将地址硬编码到解密脚本中。 下面的代码能够找出解密函数的引用地址。下面这个测试,我简单的将地址用16进制的格式打印出来。
从交叉引用处识别参数并提取原始数据稍微复杂一点,但显然不是不可能的。首先我们要得到字符串解密函数调用点之前最近的一个 'mov esi, offset unk_??'指令的偏移地址。为了达到这个目的,我们会对字符串解密函数的每一处调用,逐条指令的回溯去查找 'mov esi, offset [addr]' 指令。我们可以使用GetOperandValue()函数(API)来获取真正的偏移地址。
下面是代码实现:
现在我们只要简单的将偏移地址处的字符串提取出来。 通常我们会使用GetString()函数,然而,由于混淆过的字串是原始的二进制数据,这个函数无法得到我们期望的结果。 所以我们通过逐字节的重复读取,直到遇到null(0x00)结束符为止。
下面是代码实现:
接下来就是将之前实现的功能整合到一起:
我们可以不用运行恶意软件也能看到所有加密后的字符串。下一个步骤我们可以将解密后的字符串以注释的形式写到引用处,让字符串明文与密文同时存在,这样就很便于分析了。我们使用 MakeComm() 函数来实现这一功能。将下面这两行代码加入到上面代码print语句的后面:
如下图所示,我们能看到的,通过这一额外的步骤,我们可以结合交叉引用看得更清楚。现在我们能够很容易的找到被引用的特殊字符串。
另外的,通过反编译,我们可以看到解密后的字符串作为注释存在与代码中。
总结
通过使用IDAPython,我们完成一个困难的任务,完成了恶意二进制样本中的161个加密字符串的解密。 正如我们看到的,IDAPython对于逆向工程来说是一款强大的工具,简化任务,节省宝贵的时间。
http://researchcenter.paloaltonetworks.com/2015/12/using-idapython-to-make-your-life-easier-part-1/