IDAPython 教程3
Using IDAPython to Make Your Life Easier: Part 3
原文:https://unit42.paloaltonetworks.com/using-idapython-to-make-your-life-easier-part-3/
在本系列的前两篇文章(第1部分和第2部分)中,我们讨论了使用IDAPython简化逆向工程师的生活。现在让我们看一下条件断点。
在IDA Pro中进行调试时,通常情况下,分析人员希望仅在特定条件发生时才中断特定地址。这样的一个示例可能包括中断对特定函数的调用,但仅在将特定参数传递给它时才中断。当将特定的库加载到我的分析虚拟机中时,我个人遇到的另一个实例崩溃。今天,我将研究这个特定的问题,并讨论使用IDAPython处理该问题的方法。
背景
分析师通常负责反转DLL。在某些情况下,所讨论的文件可能由其他可执行文件加载。解决此问题的一种方法是确保调试器在加载库的每个实例时都中断,然后在DLL或驱动程序最终加载时停止。
图1调试选项可在每次加载库时中断
但是,此过程通常很麻烦,因为它通常需要分析师停止和启动:恢复执行,确定最新的库或驱动程序已加载并查看其是否正确。对于分析人员而言,仅运行单个命令,坐下来等待感兴趣的文件加载会容易得多。
条件断点
对于此特定示例,我们正在使用名为“ dd.dll”的文件。在运行时提取该特定文件后,通常会将该文件注入到另一个进程中。要将其动态运行到IDA Pro中,我将加载位于system32目录中的rundll32.exe可执行文件,并提供dd.dll文件作为参数,如下所示。可执行文件rundll32.exe允许用户以给定的导出名称加载指定的DLL。对于此特定示例,我对DLL的“设置”导出功能感兴趣。然后,我向IDA Pro提供以下参数:
该过程的下一步是确定可执行文件加载DLL后设置断点的正确位置。为此,我首先在调试器设置中检查“在库加载/卸载时挂起”。在挂载DLL的第一个实例后,我可以看到在调用NtMapViewOfSection之后就设置了一个断点。
接下来,我将0x7C91ADFB的地址用作断点。在我的代码中,我使用对add_bpt()和enable_bpt()的调用来创建和启用此断点。
1
2
3
4
5
6
7
8
9
10
|
'''
ntdll.dll:7C91ADF1 push 0FFFFFFFFh
ntdll.dll:7C91ADF3 push dword ptr [ebp-20h]
ntdll.dll:7C91ADF6 call near ptr ntdll_NtMapViewOfSection
ntdll.dll:7C91ADFB mov edi, eax
'''
address = 0x7C91ADFB # Just after NtMapViewOfSection
add_bpt(address, 0, BPT_SOFT)
enable_bpt(address, True)
|
在此阶段,我已在0x7C91ADFB处设置了一个断点,并且每次加载DLL时调试器都会中断。为了确保断点仅在加载“ dd.dll”时发生,我们必须使用条件断点。条件断点允许分析人员使用最新版本的IDC或Python代码来确定是否实际触发了断点。如果代码的返回值为True,则会触发断点。否则,它将被忽略。要使用Python,我们首先将Python设置为默认编程语言。然后,我们将Python代码存储到一个变量中,并在对SetBptCnd()的调用中使用此变量,该调用将此代码设置为断点的条件。设置条件后,我们将继续调试器并等待,直到看到调试器挂起事件为止。最终看起来如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
address = 0x7C91ADFB # Just after NtMapViewOfSection
RunPlugin("python", 3) # Python default programming
StartDebugger("","","");
dll = "dd.dll"
condition = """
for m in Modules():
if "%s".lower() in m.name.lower():
print "Breaking on", m.name.lower()
del_bpt(%d)
return True
return False
""" % (dll, address)
add_bpt(address, 0, BPT_SOFT)
enable_bpt(address, True)
SetBptCnd(address, condition)
continue_process()
GetDebuggerEvent(WFNE_SUSP, -1)
|
用于条件断点的实际代码是:
1
2
3
4
5
6
|
for m in Modules():
if "dd.dll".lower() in m.name.lower():
print "Breaking on", m.name.lower()
del_bpt(0x7C91ADFB)
return True
return False
|
该代码将通过可执行文件遍历所有已加载的模块,并搜索以确定是否已加载“ dd.dll”。如果已设置,则将打印一条调试消息,删除初始断点,并返回布尔值True,表示应触发该断点。执行后,我们将在IDA Pro输出窗口中看到以下内容:
在此阶段,我们可以将断点设置为所需的“设置”导出功能,然后运行程序直到断点触发。为了自动执行此步骤,可以使用以下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
def get_names(base, size, desired_name):
current_address = base
while current_address <= base+size:
current_address = NextHead(current_address)
print hex(current_address)
if desired_name in Name(current_address):
return current_address
for m in Modules():
if 'dd.dll' in m.name.lower():
base = m.base
size = m.size
analyze_area(base, base+size)
setting = get_names(base, size, "Setting")
if setting:
add_bpt(setting, 0, BPT_SOFT)
enable_bpt(setting, True)
continue_process()
GetDebuggerEvent(WFNE_SUSP, -1)
|
此代码将迭代加载的模块,查找“ dd.dll”。确定后,我们将分析包含此DLL的代码。然后,我们遍历此代码以查找名称,并搜索“设置”的名称。一旦确定,我们就在该地址上设置一个断点,继续调试器,并等待该断点被触发。执行后,我们看到断点已按期望的地址触发。
结论
尽管这种设置条件断点的技术似乎微不足道,但它可以为分析师节省大量的总体时间。一小段代码可以代替手动过程,不断地在单个位置上中断直到满足所需的条件。再次,我们看到了在反向工程样本时使用IDAPython的强大功能,这为我们节省了宝贵的时间和精力。