IDAPython 教程5
Using IDAPython to Make Your Life Easier: Part 5
原文:https://unit42.paloaltonetworks.com/using-idapython-to-make-your-life-easier-part-5
我们将继续使用IDAPython通过减少几乎每天处理的恶意软件分析师所遇到的问题来使逆向工程师更轻松地进行开发的系列:提取嵌入式可执行文件。恶意软件通常会以多种方式存储嵌入式可执行文件。一些示例包括将这些文件附加到文件的覆盖图中,包括将它们作为PE资源,或将其存储在恶意软件中的缓冲区中。
当出现这种情况时,恶意软件分析人员有几种选择。他们可以动态运行样本并在写入/提取文件后中断。或者,如果文件存储在资源部分中,则他们可以使用诸如CFFExplorer之类的实用程序来提取资源。在IDA Pro中,分析人员还可以在十六进制视图中突出显示必要的数据,然后右键单击“另存为”以提取此数据。
图1 在IDA Pro中提取数据
尽管这些选择中的许多可行,但每种选择都有其自身的局限性。自动检测和提取这些嵌入式可执行文件的功能将有助于节省分析师宝贵的时间。为此,我们将结合使用IDAPython和名为“ pefile”的第三方库。这种特殊情况带来了一些独特的挑战:
- 我们必须在IDA Pro环境中启用随PIP安装的第三方Python库。
- 必须确定嵌入式可执行文件。
- 为了提取它们,必须计算任何发现的可执行文件的大小。
让我们一次解决这些挑战。
在IDA Pro中启用第三方Python库
在IDA Pro中启用与PIP一起安装的第三方Python库带来了一个有趣的挑战。如果不进行任何修改,分析人员将无法在IDAPython解释器中加载第三方库,例如pefile,如下所示。
要纠正此问题,我们必须将PIP的“ site-packages”目录添加到Python环境变量中。可以使用以下代码查看此变量:
1
2
3
4
5
|
import sys
print sys.path
Result:
['/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python27.zip', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac', '<Truncated>']
|
要包括PIP安装的库,我们可以简单地将“ site-packages”目录附加到此数组中,然后再执行“ import pefile”语句。该解决方案并不完美,因为它需要分析师手动确定“ site-packages”目录,但是我还没有找到一个独立于平台的解决方案来提供帮助(如果您知道一个解决方案,请在下方评论或发送给我发推文@jgrunzweig。包含必要的代码后,我们可以加载pefile库。
图3 在IDA Pro中成功加载pefile库
识别嵌入式可执行文件
为了发现恶意软件是否包含任何嵌入式可执行文件,我们可以对MZ标头中的已知字符串执行二进制搜索。请注意,分析人员必须确保在加载文件时选中“加载资源”按钮,以查看存储为资源的任何数据。此外,如果覆盖部分中包含嵌入式文件,则必须选中“手动加载”按钮才能在IDA Pro中查看此数据。
图4 选中的选项以查看IDA Pro中的各种数据
现在我们已将必要的信息加载到IDA Pro中,我们可以执行搜索数据以识别PE32文件的任务。有几种方法可以解决此问题,但是我选择搜索出现在每个MZ标头中的以下静态消息。
!该程序无法在DOS模式下运行。
为了在IDA中查找此字符串的所有出现,我们可以使用带有循环的FindBinary()函数,该循环将允许我们遍历二进制字符串的每个实例。可以使用以下代码以通用方式完成此操作:
1
2
3
4
5
6
7
8
9
10
11
|
def find_string_occurrences(string):
results = []
base = idaapi.get_imagebase() + 1024
while True:
ea = FindBinary(base, SEARCH_NEXT|SEARCH_DOWN|SEARCH_CASE, '"%s"' % string)
if ea != 0xFFFFFFFF:
base = ea+1
else:
break
results.append(ea)
return results
|
虽然找到MZ标头字符串可以很好地表明我们已找到PE32文件,但我们还需要通过查找在MZ标头开头的标志性“ MZ”字符来进行验证。由于我们先前搜索的字符串位于静态偏移处,因此我们可以简单地检查已知偏移处是否存在“ MZ”。
1
2
3
4
5
6
7
8
9
10
11
12
|
def find_embedded_exes():
results = []
exes = find_string_occurrences("!This program cannot be run in DOS mode.")
if len(exes) > 1:
for exe in exes:
m = Byte(exe-77)
z = Byte(exe-76)
if m == ord("M") and z == ord("Z"):
mz_start = exe-77
print "[*] Identified embedded executable at the following offset: 0x%x" % mz_start
results.append(mz_start)
return results
|
将这些代码串在一起可以使我们识别IDA Pro中嵌入式可移植可执行文件的所有实例。
图5 识别IDA Pro中的嵌入式可执行文件
确定可执行文件的大小
为了确定所标识的嵌入式可执行文件的大小,我们将使用前面提到的pefile Python库。该库将使我们能够解析各种可执行标头,从而使我们能够计算PE的大小。为此,我们将在可选标题中添加“ SizeOfHeaders”参数,以及每个部分的“ SizeOfRawData”字段。以下代码将读取已标识的嵌入式可执行文件的前1024个字节,使用pefile解析此数据,并计算各种大小字段。
1
2
3
4
5
6
7
8
9
10
11
12
|
def calculate_exe_size(begin):
buff = ""
for c in range(0, 1024):
buff += chr(Byte(begin+c))
pe = pefile.PE(data=buff)
total_size = 0
# Add total size of headers
total_size += pe.OPTIONAL_HEADER.SizeOfHeaders
# Iterate through each section and add section size
for section in pe.sections:
total_size += section.SizeOfRawData
return total_size
|
最后,我们可以使用该总大小值提取可执行数据并将其写入我们选择的文件中。
1
2
3
4
5
6
7
|
def extract_exe(name, begin, size):
buff = ""
for c in range(0, size):
buff += chr(Byte(begin+c))
f = open(name, 'wb')
f.write(buff)
f.close()
|
结论
综合所有这些,我们得出以下脚本。在示例恶意软件样本上运行此代码会产生以下结果:
图6 运行IDAPython脚本的结果
如我们所见,我们现在可以自动从IDA Pro中提取PE文件。稍作修改,该方法当然也可以应用于其他文件类型。我希望这篇简短的教程可以帮助读者了解IDAPython绑定为逆向工程师带来的众多可能性和功能。