Skip to main content

调试与逆向

在这小节,我们将学习简单的调试以及逆向技能,这对于恶意软件开发领域有这些帮助:理解当下的恶意软件的技术、功能、原理分析恶意软件并且改进,开发漏洞利用以及 Shellcode理解安全产品的原理以规避检测等

我们将主要使用 WinDBG 作为动态调试工具,IDA 作为逆向工具。两者相辅相成,动态与静态相结合。WinDBG 可以在微软商店中获取:

image.png

IDA 可以从 https://hex-rays.com/ida-free/ 下载,拥有专业版自然更好。


WinDBG

调试器是插入在目标应用程序和 CPU 之间的计算机程序,充当类似代理的角色。使用调试器使我们能够查看应用程序的内存和执行流程并与之交互。接下来的课程内容中,我们将与用户模式交互。

CPU以二进制级别处理代码,这对人类来说很难阅读和理解,而汇编语言引入了二进制内容和编程语言之间的一对一映射。尽管汇编语言应该是人类可读的,但它仍然是一种低级语言,并且需要时间来掌握。 操作码是由 CPU 解释为特定指令的二进制序列,这在调试器中显示为十六进制值以及汇编语言的翻译。

接下来,利用 WinDbg,我们将学习如何使用断点来单步执行和控制应用程序的流程。


自定义界面

为了能有一个舒适的调试环境,我们可以对 WinDBG 的界面进行自定义,这样当我们调试应用程序的时候,可以在用户界面中查看到所需的信息,例如内存汇编代码断点信息等。

在导航栏中选中 View,我们可以添加多个窗口,这里面对我们调试过程会十分有帮助的有 WinDBG 命令 (Command)寄存器 (Registers)内存 (Memory)栈 (Stack)汇编代码 (Disassembly)

image.png点中之后,对应的窗口会浮现出来,可以使其与 WinDBG 主窗口相互独立,也可以将其嵌入至 WinDBG 主窗口,我会更推荐将它们嵌入至主窗口。不过因为主窗口空间有限,如果添加的窗口数量过多,也会影响我们对信息的提取效率,诸如模块 (Module)、断点 (Breakpoints) 等窗口我们不一定要添加到主页面,而是通过 WinDBG 的命令来查看相关信息。

下图是个人偏好的一个界面布局,你们可能注意到我添加了 2 个内存窗口,因为我们还想查看 RSP 的状态。

image.png

当我们想要调试一个进程或者应用的时候,可以选择使用 WinDBG 启动目标程序,或者附加到正常运行的进程中。

image.png

附加上之后,进程会自动被设置一个软件断点,对应的汇编指令为 int 3

image.png

我们可以执行 WinDBG 命令 g 来继续执行。

image.png

在进入对 WinDBG 基本命令的学习之前,我们还需要知道调试符号。符号 (Symbol) 文件允许 WinDbg 使用名称而不是地址来引用内部函数结构体全局变量。例如,我们想给 kernelbase.dll 中的 CreateProcessA 函数设置软件断点,我们不需要先得到载入的 kernelbase.dll 中的 CreateProcessA 函数的地址,使用名称即可,命令为 bp kernelbase!CreateProcessA。在后面,我们也可以用符号来查看一些结构体。

符号文件以 .pdb 为拓展名,当应用程序的 pdb 与 PE 文件在同一目录下,符号文件会被自动载入从而识别应用程序中的符号。

image.png

这样,我们可以使用 process_calc!Main 来定位到 process_calc.exe 程序中的 Main 函数。

image.png



伪寄存器

除了 rsp, rip 等处理器寄存器外,在 WinDBG,还支持伪寄存器。WinDbg 中的伪寄存器不是实际的处理器寄存器,而是调试器本身提供的结构,允许我们快速方便地访问某些常用信息,而无需手动计算地址或使用较长的命令序列。我们会经常用到的伪寄存器有 $peb$teb$ip ,分别代表当前进程的 PEB当前线程的 TEB,当前的指令寄存器

0:000> r $peb
$peb=0000005c12c6f000
0:000> r $teb
$teb=0000005c12c70000
0:000> r $ip
$ip=00007ffb80a4cea4



基本命令

有了对 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* <内存地址> 来读取给定内存地址,星号可以是不同的数据类型,例如 byteword,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


读取结构体

我们可以使用 dt <结构体名称> 命令来显示结构体的结构,当然了,读取结构体需要符号文件的载入。在之前的小节,我们讲了 PEB 和 TEB,那么我们在 WinDBG 中查看一下 PEB 的结构吧。其中,使用 -r 选项可以递归地展示成员,因为结构体的成员也可能是结构体。

0:000> dt ntdll!_peb
   +0x000 InheritedAddressSpace : UChar
   +0x001 ReadImageFileExecOptions : UChar
   +0x002 BeingDebugged    : UChar
   +0x003 BitField         : UChar
   +0x003 ImageUsesLargePages : Pos 0, 1 Bit
   +0x003 IsProtectedProcess : Pos 1, 1 Bit
   +0x003 IsImageDynamicallyRelocated : Pos 2, 1 Bit
   +0x003 SkipPatchingUser32Forwarders : Pos 3, 1 Bit
   +0x003 IsPackagedProcess : Pos 4, 1 Bit
   +0x003 IsAppContainer   : Pos 5, 1 Bit
   +0x003 IsProtectedProcessLight : Pos 6, 1 Bit
   +0x003 IsLongPathAwareProcess : Pos 7, 1 Bit
   +0x004 Padding0         : [4] UChar
   +0x008 Mutant           : Ptr64 Void
   +0x010 ImageBaseAddress : Ptr64 Void
   +0x018 Ldr              : Ptr64 _PEB_LDR_DATA
   +0x020 ProcessParameters : Ptr64 _RTL_USER_PROCESS_PARAMETERS
............
   +0x7ac CloudFileDiagFlags : Uint4B
   +0x7b0 PlaceholderCompatibilityMode : Char
   +0x7b1 PlaceholderCompatibilityModeReserved : [7] Char
   +0x7b8 LeapSecondData   : Ptr64 _LEAP_SECOND_DATA
   +0x7c0 LeapSecondFlags  : Uint4B
   +0x7c0 SixtySecondEnabled : Pos 0, 1 Bit
   +0x7c0 Reserved         : Pos 1, 31 Bits
   +0x7c4 NtGlobalFlag2    : Uint4B
   +0x7c8 ExtendedFeatureDisableMask : Uint8B
0:000> dt -r ntdll!_peb
   +0x000 InheritedAddressSpace : UChar
   +0x001 ReadImageFileExecOptions : UChar
   +0x002 BeingDebugged    : UChar
   +0x003 BitField         : UChar
   +0x003 ImageUsesLargePages : Pos 0, 1 Bit
   +0x003 IsProtectedProcess : Pos 1, 1 Bit
   +0x003 IsImageDynamicallyRelocated : Pos 2, 1 Bit
   +0x003 SkipPatchingUser32Forwarders : Pos 3, 1 Bit
   +0x003 IsPackagedProcess : Pos 4, 1 Bit
   +0x003 IsAppContainer   : Pos 5, 1 Bit
   +0x003 IsProtectedProcessLight : Pos 6, 1 Bit
   +0x003 IsLongPathAwareProcess : Pos 7, 1 Bit
   +0x004 Padding0         : [4] UChar
   +0x008 Mutant           : Ptr64 Void
   +0x010 ImageBaseAddress : Ptr64 Void
   +0x018 Ldr              : Ptr64 _PEB_LDR_DATA
      +0x000 Length           : Uint4B
      +0x004 Initialized      : UChar
      +0x008 SsHandle         : Ptr64 Void
      +0x010 InLoadOrderModuleList : _LIST_ENTRY
         +0x000 Flink            : Ptr64 _LIST_ENTRY
         +0x008 Blink            : Ptr64 _LIST_ENTRY
      +0x020 InMemoryOrderModuleList : _LIST_ENTRY
         +0x000 Flink            : Ptr64 _LIST_ENTRY
         +0x008 Blink            : Ptr64 _LIST_ENTRY
      +0x030 InInitializationOrderModuleList : _LIST_ENTRY
         +0x000 Flink            : Ptr64 _LIST_ENTRY
         +0x008 Blink            : Ptr64 _LIST_ENTRY
      +0x040 EntryInProgress  : Ptr64 Void
      +0x048 ShutdownInProgress : UChar
      +0x050 ShutdownThreadId : Ptr64 Void
............
   +0x34e OemCodePage      : Uint2B
   +0x350 UseCaseMapping   : Uint2B
   +0x352 UnusedNlsField   : Uint2B
   +0x358 WerRegistrationData : Ptr64 Void
   +0x360 WerShipAssertPtr : Ptr64 Void
   +0x368 EcCodeBitMap     : Ptr64 Void
   +0x370 pImageHeaderHash : Ptr64 Void
   +0x378 TracingFlags     : Uint4B
   +0x378 HeapTracingEnabled : Pos 0, 1 Bit
   +0x378 CritSecTracingEnabled : Pos 1, 1 Bit
   +0x378 LibLoaderTracingEnabled : Pos 2, 1 Bit
   +0x378 SpareTracingBits : Pos 3, 29 Bits
   +0x37c Padding6         : [4] UChar
   +0x380 CsrServerReadOnlySharedMemoryBase : Uint8B
   +0x388 TppWorkerpListLock : Uint8B
   +0x390 TppWorkerpList   : _LIST_ENTRY
      +0x000 Flink            : Ptr64 _LIST_ENTRY
         +0x000 Flink            : Ptr64 _LIST_ENTRY
         +0x008 Blink            : Ptr64 _LIST_ENTRY
      +0x008 Blink            : Ptr64 _LIST_ENTRY
         +0x000 Flink            : Ptr64 _LIST_ENTRY
         +0x008 Blink            : Ptr64 _LIST_ENTRY
   +0x3a0 WaitOnAddressHashTable : [128] Ptr64 Void
   +0x7a0 TelemetryCoverageHeader : Ptr64 Void
   +0x7a8 CloudFileFlags   : Uint4B
   +0x7ac CloudFileDiagFlags : Uint4B
   +0x7b0 PlaceholderCompatibilityMode : Char
   +0x7b1 PlaceholderCompatibilityModeReserved : [7] Char
   +0x7b8 LeapSecondData   : Ptr64 _LEAP_SECOND_DATA
      +0x000 Enabled          : UChar
      +0x004 Count            : Uint4B
      +0x008 Data             : [1] _LARGE_INTEGER
         +0x000 LowPart          : Uint4B
         +0x004 HighPart         : Int4B
         +0x000 u                : <unnamed-tag>
         +0x000 QuadPart         : Int8B
   +0x7c0 LeapSecondFlags  : Uint4B
   +0x7c0 SixtySecondEnabled : Pos 0, 1 Bit
   +0x7c0 Reserved         : Pos 1, 31 Bits
   +0x7c4 NtGlobalFlag2    : Uint4B
   +0x7c8 ExtendedFeatureDisableMask : Uint8B

我们还可以使用 WinDBG 检视给定内存区域的结构体数据、查看结构体中的成员、显示结构体的尺寸。

0:000> dt ntdll!_peb @$peb
   +0x000 InheritedAddressSpace : 0 ''
   +0x001 ReadImageFileExecOptions : 0 ''
   +0x002 BeingDebugged    : 0x1 ''
   +0x003 BitField         : 0x4 ''
   +0x003 ImageUsesLargePages : 0y0
   +0x003 IsProtectedProcess : 0y0
   +0x003 IsImageDynamicallyRelocated : 0y1
   +0x003 SkipPatchingUser32Forwarders : 0y0
   +0x003 IsPackagedProcess : 0y0
   +0x003 IsAppContainer   : 0y0
   +0x003 IsProtectedProcessLight : 0y0
   +0x003 IsLongPathAwareProcess : 0y0
   +0x004 Padding0         : [4]  ""
   +0x008 Mutant           : 0xffffffff`ffffffff Void
   +0x010 ImageBaseAddress : 0x00007ff6`4ba90000 Void
   +0x018 Ldr              : 0x00007ffb`80af4380 _PEB_LDR_DATA
   +0x020 ProcessParameters : 0x00000200`bdb86990 _RTL_USER_PROCESS_PARAMETERS
............
   +0x7c4 NtGlobalFlag2    : 0
   +0x7c8 ExtendedFeatureDisableMask : 0
0:000> dt ntdll!_peb @$peb ProcessParameters
   +0x020 ProcessParameters : 0x00000200`bdb86990 _RTL_USER_PROCESS_PARAMETERS
0:000> ?? sizeof(ntdll!_PEB)
unsigned int64 0x7d0



修改和写入内存


搜索内存空间


检视寄存器


计算器


列举模块


调用栈


TEB


内存状态


软件断点


硬件断点


单步执行

t

p

执行至

ph 和 th

pa 和 ta



解析 PE



IDA