使用IDA Pro 7.4 Automation对二进制文件分析指南

 

使用IDAPython模块编写分析脚本很容易,但是我们如果要对10,000个文件进行批量自动化分析呢?我快速浏览了一下Google,但是一无所获,没有找到任何关于IDA Pro 7.x自动化执行批处理分析二进制任务的指南。

通过本篇文章,我将分享一下我的一些经验,以免大家走弯路。我将以Windows平台的IDA Pro为例,但方法应该也适用于其他平台(Linux或Mac)。

 

简单Binary 分析

首先,让我们编写一些简单的IDAPython分析脚本,并在IDA Pro控制台中运行它。该脚本会循环遍历可执行文件中的所有函数,并打印出其地址和函数名称:

import idc
import idautils

print 'count %d' % len(list(idautils.Functions()))
for ea in idautils.Functions():
    print hex(ea), idc.get_func_name(ea)

idautils模块的功能不仅局限于此,它还可以获取函数列表,或查找某一数据或代码的地址。如果您熟悉IDC脚本,您还可以在IDC模块中找到其它具有类似功能的函数。
在Windows上的notepad.exe中运行,会得到如下输出:

count 381
0x140001008L sub_140001008
0x1400010b0L EnableCallback
0x140001130L sub_140001130
 .
 .
0x14002107cL __GSHandlerCheckCommon
0x1400210e0L __GSHandlerCheck_SEH
0x140021176L memcmp
0x140021182L memcpy
0x14002118eL memset
0x14002119aL wcscmp
0x1400211b0L sub_1400211B0
0x1400211d0L sub_1400211D0
 .
 .
0x1400213b0L sub_1400213B0
0x140021410L sub_140021410
0x140021440L sub_140021440

因为我没有从Microsoft的服务器加载PDB文件,所以会看到很多未命名的函数。但是我们也看到了几个熟悉的C函数,如memcmp和wcscmp。现在我们要做的是筛选出典型的库函数以及二进制文件中的thunk。

for ea in idautils.Functions():
    if idc.get_func_flags(ea) & (idc.FUNC_LIB | idc.FUNC_THUNK): continue
    print hex(ea), idc.get_func_name(ea)

您还可以统计函数的数量,获取函数的长度,或用idautils.CodeRefsTo()查找对这些函数的调用。具体的实现取决于您要进行哪种分析。

 

扩展自动化

现在我们已经实现了对单个文件的分析,让我们尝试将其扩展到目录甚至整个文件系统上。

自IDA Pro 5.7起,引入了-S参数。从那时起,命令行调用IDA的方式也发生了改变。我看到一些参考指南中用的是idaq.exe,这是一个QT二进制文件。在更早的版本中,该文件为idag.exe,其中g是GUI的意思。不过,在较新的版本中,此文件为ida.exe或ida64.exe。
您可以使用以下命令行参数来调用IDA Pro:

ida64.exe -c -A -Syour-script.py C:notepad.exe

您可以使用-S来指定脚本,您还需要使用-B 参数指定批量模式,等效于-A –c 。否则,您的脚本将无法运行。
文本界面(idat.exe/ idat)对于批处理模式更好,因为它使用较少的系统资源。
IDA Pro文本模式用户界面至今仍保持不变,调用时的外观如下:

但是,当您使用-A参数时,UI不会显示。

 

脚本完善

在进行自动化工作之前,您需要对脚本进行一些关键更改。

1.您需要一个日志记录文件。因为stdout不会显示脚本输出,所以要选择一个日志文件对输出进行记录。

2.您需要退出。在脚本末尾调用idc.qexit(0),否则该进程将挂起。

修改后:

import idc
import idautils
 
# (1) open our log file here
f = open('/Users/darell/Desktop/analysis.txt', 'a')
 
# probably a good idea to log the current filename
f.write(idc.get_input_file_path() + '\n')
 
f.write( 'count %d\n' % len(list(idautils.Functions())) )
for ea in idautils.Functions():
    if idc.get_func_flags(ea) & (idc.FUNC_LIB | idc.FUNC_THUNK): continue
    f.write( hex(ea) + ' ' + idc.get_func_name(ea) + '\n' )
 
f.close()
 
# (2) remember to exit IDA Pro
idc.qexit(0)

您可能还需要记录当前文件路径。

使用IDA Pro(文本UI)运行脚本后,如下所示:

ida64t.exe -c -A -Syour-script.py C:\notepad.exe

运行脚本后,桌面上会出现一个日志文件(确保首先修改硬编码日志文件路径)

打开日志文件,您应该获得如下输出:

c:notepad.exe
count 300
0x140001008L _TlgWrite
0x1400010b0L _TlgEnableCallback
0x140001150L wil::details::_dynamic_initializer_for__g_threadFailureCallbacks__
0x140001210L wil::details::_dynamic_initializer_for__g_featureStateManager__
  .
  .
0x140021350L wil::details::_dynamic_atexit_destructor_for__g_featureStateManager__
0x1400213b0L wil::details::_dynamic_atexit_destructor_for__g_enabledStateManager__
0x140021410L _dynamic_atexit_destructor_for__szFileName__
0x140021440L _UpdateTitle_::_2_::_dynamic_atexit_destructor_for__previousFileTitle__

您应该注意到一些不同之处:

函数有了名字。这是因为IDA Pro在默认情况下会加载PDB文件, 但是在您正在分析的未知二进制文件上,很难获得符号名称。

只有300个函数,而不是以前的381个。

之所以会缺少~80个函数,是因为没有运行自动分析。您需要先调用ida_auto.auto_wait()或idc.auto_wait()等待自动分析完成。

 

只读卷

有时您希望将二进制文件中的卷或图像设置为只读,但是IDA Pro无法打开它:

默认情况下,IDA Pro会在目标文件的目录下创建IDB数据库文件(和其它配置文件)。但是如果目标二进制文件位于只读区域(例如此处的Windows system32目录),则会出现问题。一种解决方法是将目标文件临时复制到可读写区域,并运行IDA Pro。但是这样会导致在在执行脚本时,不知道二进制文件的完整路径。

幸运的是,可以通过-o参数指定数据库文件文章:

-o%TEMP%\foobar.idb

 

调试

有时编写好的脚本并不会像预期那样执行,您或许会选择添加-z参数对脚本进行调试。但是这样做是徒劳的,调试的最佳选择是将其加载到IDA Pro GUI中并检查错误。

有时您可能还会遇到idc.qexit(0)的困扰。有时您的脚本没有问题,但是IDA Pro会不断的退出。更糟糕的是,如果你忘记在脚本末尾调用idc.qexit(0)。则该进程会被挂起或者消耗大量CPU资源。我们可以尝试以下方法解决。

更改旧代码

从IDA Pro 7.4开始,原本用于6.x版的旧代码将不再起作用,在默认情况下它们关闭了向后兼容性。如果您使用的是较新版本的IDA Pro,并且脚本是从网上找的,则肯定会遇到此问题。

例如,idc.Exit(0)将不再起作用,您需要使用idc.qexit(0)。

有些脚本还会调用idaapi.autoWait(),您需要将此函数更换为ida_auto.auto_wait()或者idc.auto_wait()。

更多信息请参考

更好的脚本

我们已经了解了编写脚本的基本方法,我们可以对脚本进行升级,使其可以在GUI中运行,也可以用于自动化分析。我们需要向脚本传递参数,可以使用以下命令:

-S"your-script.py arg1 arg2"

通过这条命令我们可以实现以下操作

1)删除硬编码的日志文件路径

2)向脚本发送它正在外部运行的信号

脚本参数还可以通过idc.ARGV传递就像这样:

f = open(idc.ARGV[1], 'a') if len(idc.ARGV) > 1 else sys.stdout
log = f.write

因此,现在,log具备日志记录功能(而不是print),根据参数是否传递,它将成为该路径下的文件句柄,或者是sys.stdout(IDA Pro输出窗口的文件句柄)。

在脚本的最后,我们根据文件句柄的值决定是否退出IDA Pro:

if f != sys.stdout:
    f.close()
    idc.qexit(0)

经过这些修改完善,在对庞大的函数库进行自动化分析时,您就可以在命令行或者IDA Pro GUI运行相同的脚本。

结合以上所有参数,我们可以对 C:Windows下所有的exe进行分析:

FOR %f IN (C:\Windows\*.exe) DO idat64.exe -c -A ^
        -S"analysis.py %USERPROFILE%\Desktop\analysis.txt" ^
        -o%TEMP%\foobar.idb ^
        %f

日志文件将在桌面生成,因为数据库文件将在%TEMP%中创建。

所以允许您从只读位置(如C:\ Windows)打开文件,

 

参考文献

您可以在此处下载完整脚本:

https://gist.github.com/geekman/dc368bda9ab3b72cedda867f86b2bc07

关于IDA Pro其它命令行参数可以在这里找到:https://www.hex-rays.com/products/ida/support/idadoc/417.shtml

介绍IDA Pro的-s脚本参数的博客文章:https://www.hex-rays.com/blog/running-scripts-from-the-command-line-with-idascript/

GitHub上的IDAPython源代码:https://github.com/idapython/src

随后,您还可以执行更高级的分析,抛砖引玉:https://unit42.paloaltonetworks.com/unit42-using-idapython-to-make-your-life-easier-part-6/

(完)