调试与逆向
在这小节,我们将学习简单的调试以及逆向技能,这对于恶意软件开发领域有这些帮助:理解当下的恶意软件的技术、功能、原理,分析恶意软件并且改进,开发漏洞利用以及 Shellcode,理解安全产品的原理以规避检测等。
我们将主要使用 WinDBG 作为动态调试工具,IDA 作为逆向工具。两者相辅相成,动态与静态相结合。WinDBG 可以在微软商店中获取:
IDA 可以从 https://hex-rays.com/ida-free/ 下载,拥有专业版自然更好。
WinDBG
调试器是插入在目标应用程序和 CPU 之间的计算机程序,充当类似代理的角色。使用调试器使我们能够查看应用程序的内存和执行流程并与之交互。接下来的课程内容中,我们将与用户模式交互。
CPU以二进制级别处理代码,这对人类来说很难阅读和理解,而汇编语言引入了二进制内容和编程语言之间的一对一映射。尽管汇编语言应该是人类可读的,但它仍然是一种低级语言,并且需要时间来掌握。 操作码是由 CPU 解释为特定指令的二进制序列,这在调试器中显示为十六进制值以及汇编语言的翻译。
接下来,利用 WinDbg,我们将学习如何使用断点来单步执行和控制应用程序的流程。
自定义界面
为了能有一个舒适的调试环境,我们可以对 WinDBG 的界面进行自定义,这样当我们调试应用程序的时候,可以在用户界面中查看到所需的信息,例如内存、汇编代码、断点信息等。
在导航栏中选中 View,我们可以添加多个窗口,这里面对我们调试过程会十分有帮助的有 WinDBG 命令 (Command)、寄存器 (Registers)、内存 (Memory)、栈 (Stack)、汇编代码 (Disassembly)。
点中之后,对应的窗口会浮现出来,可以使其与 WinDBG 主窗口相互独立,也可以将其嵌入至 WinDBG 主窗口,我会更推荐将它们嵌入至主窗口。不过因为主窗口空间有限,如果添加的窗口数量过多,也会影响我们对信息的提取效率,诸如模块 (Module)、断点 (Breakpoints) 等窗口我们不一定要添加到主页面,而是通过 WinDBG 的命令来查看相关信息。
下图是个人偏好的一个界面布局,你们可能注意到我添加了 2 个内存窗口,因为我们还想查看 RSP 的状态。
当我们想要调试一个进程或者应用的时候,可以选择使用 WinDBG 启动目标程序,或者附加到正常运行的进程中。
附加上之后,进程会自动被设置一个软件断点,对应的汇编指令为 int 3。
我们可以执行 WinDBG 命令 g 来继续执行。
在进入对 WinDBG 基本命令的学习之前,我们还需要知道调试符号。符号 (Symbol) 文件允许 WinDbg 使用名称而不是地址来引用内部函数、结构体和全局变量。例如,我们想给 kernelbase.dll 中的 CreateProcessA 函数设置软件断点,我们不需要先得到载入的 kernelbase.dll 中的 CreateProcessA 函数的地址,使用名称即可,命令为 bp kernelbase!CreateProcessA。在后面,我们也可以用符号来查看一些结构体。
符号文件以 .pdb 为拓展名,当应用程序的 pdb 与 PE 文件在同一目录下,符号文件会被自动载入从而识别应用程序中的符号。
这样,我们可以使用 process_calc!Main 来定位到 process_calc.exe 程序中的 Main 函数。
基本命令
有了对 WinDBG 的初始了解以及配置了最适合自己的界面布局后,我们来学习 WinDBG 的基本常用命令。WinDBG 内置的命令数量十分庞大,并且考虑到该小节的内容是作为恶意软件开发的前置知识,因此我们不会过于深入。接下来,我们依次介绍以下这些常用命令。
反汇编
在 WinDBG 中,使用命令 u <内存地址> 来检视给定内存地址的汇编代码。例如,我们可以查看 user32.dll 中的 MessageBoxA 函数的汇编实现:
0:000> u user32!messageboxa
USER32!MessageBoxA:
00007ffb`7e9a7a90 4883ec38 sub rsp,38h
00007ffb`7e9a7a94 4533db xor r11d,r11d
00007ffb`7e9a7a97 44391dcaf70300 cmp dword ptr [USER32!gfEMIEnable (00007ffb`7e9e7268)],r11d
00007ffb`7e9a7a9e 742e je USER32!MessageBoxA+0x3e (00007ffb`7e9a7ace)
00007ffb`7e9a7aa0 65488b042530000000 mov rax,qword ptr gs:[30h]
00007ffb`7e9a7aa9 4c8b5048 mov r10,qword ptr [rax+48h]
00007ffb`7e9a7aad 33c0 xor eax,eax
对于内存地址,可以是符号、内存地址、寄存器的形式,只要是合法的内存地址。可以通过 l* 来指定显示的行数。
0:000> u 00007ffb`7e9a7a90 l3
USER32!MessageBoxA:
00007ffb`7e9a7a90 4883ec38 sub rsp,38h
00007ffb`7e9a7a94 4533db xor r11d,r11d
00007ffb`7e9a7a97 44391dcaf70300 cmp dword ptr [USER32!gfEMIEnable (00007ffb`7e9e7268)],r11d
读取内存
我们可以使用命令 d* <内存地址> 来读取给定内存地址,星号可以是不同的数据类型,例如 byte,word,dwordbyte,word,dword 等。我们可以在微软文档 https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/d--da--db--dc--dd--dd--df--dp--dq--du--dw--dw--dyb--dyd--display-memor 中了解更加详细的用法。
0:000> dd rip
00007ffb`80a4cea4 4800ebcc c338c483 cccccccc cccccccc
00007ffb`80a4ceb4 245c8948 74894810 57551824 8d485641
00007ffb`80a4cec4 ff0024ac 8148ffff 000200ec 058b4800
00007ffb`80a4ced4 000bf648 48c43348 00f08589 8b4c0000
00007ffb`80a4cee4 0bc32705 058d4800 0005afe0 8948ff33
00007ffb`80a4cef4 c7502444 16482444 48001800 7024448d
00007ffb`80a4cf04 24448948 f18b4868 602444c7 01000000
00007ffb`80a4cf14 0100be41 89660000 4d70247c 2b74c085
0:000> db rip
00007ffb`80a4cea4 cc eb 00 48 83 c4 38 c3-cc cc cc cc cc cc cc cc ...H..8.........
00007ffb`80a4ceb4 48 89 5c 24 10 48 89 74-24 18 55 57 41 56 48 8d H.\$.H.t$.UWAVH.
00007ffb`80a4cec4 ac 24 00 ff ff ff 48 81-ec 00 02 00 00 48 8b 05 .$....H......H..
00007ffb`80a4ced4 48 f6 0b 00 48 33 c4 48-89 85 f0 00 00 00 4c 8b H...H3.H......L.
00007ffb`80a4cee4 05 27 c3 0b 00 48 8d 05-e0 af 05 00 33 ff 48 89 .'...H......3.H.
00007ffb`80a4cef4 44 24 50 c7 44 24 48 16-00 18 00 48 8d 44 24 70 D$P.D$H....H.D$p
00007ffb`80a4cf04 48 89 44 24 68 48 8b f1-c7 44 24 60 00 00 00 01 H.D$hH...D$`....
00007ffb`80a4cf14 41 be 00 01 00 00 66 89-7c 24 70 4d 85 c0 74 2b A.....f.|$pM..t+
0:000> dw rip
00007ffb`80a4cea4 ebcc 4800 c483 c338 cccc cccc cccc cccc
00007ffb`80a4ceb4 8948 245c 4810 7489 1824 5755 5641 8d48
00007ffb`80a4cec4 24ac ff00 ffff 8148 00ec 0002 4800 058b
00007ffb`80a4ced4 f648 000b 3348 48c4 8589 00f0 0000 8b4c
00007ffb`80a4cee4 2705 0bc3 4800 058d afe0 0005 ff33 8948
00007ffb`80a4cef4 2444 c750 2444 1648 1800 4800 448d 7024
00007ffb`80a4cf04 8948 2444 4868 f18b 44c7 6024 0000 0100
00007ffb`80a4cf14 be41 0100 0000 8966 247c 4d70 c085 2b74
读取结构体
修改和写入内存
搜索内存空间
检视寄存器
计算器
列举模块
调用栈
TEB
内存状态
软件断点
硬件断点
单步执行
t
p
执行至
ph 和 th
pa 和 ta