合成符号和模块(WinDbg / DbgEng)

本出版物将重点介绍Windows调试引擎(调试器引擎)的综合模块和符号。 也就是说,关于可以人为地添加到调试器以为存储器地址着色的实体。



合成模块


通常,作为调试器引擎一部分的模块是虚拟内存的某个连续区域:模块的基地址(投影文件的第一个字节的地址)及其大小。 对于每种调试类型(实时调试/转储分析,用户模式/内核模式等),调试器都有其自己的机制来读取并保持已加载模块列表的最新状态。 强制更新已加载模块列表的最简单方法是使用带有/ s选项的.reload命令


例如,如果我们谈论用户模式进程的实时调试,那么将根据加载程序的三个主要列表来构建已加载模块的列表:模块加载顺序列表(InLoadOrderModuleList),内存中模块列表(InMemoryOrderModuleList)和初始化优先级列表(InInitializationOrderModuleList)。 一方面, 几乎没有什么可以阻止我们获取任意数据(例如,从磁盘上的PE文件获取)并将其标记在内存中以便用自己的双手执行。 另一方面,很早就知道从上述三个加载程序列表(隐藏模块)中删除以常规方式加载的DLL的技术。


在这两种情况下,调试此情况时,标记隐藏模块的区域都是有用的。 为此,适合使用合成模块。 作为一个实际实验,您可以简单地使WinDbg使用相同的.reload命令但使用/ u选项从其内部列表中删除已加载的模块。


此外,在列出的清单中,我将使用用户模式进程(notepad.exe)的常规调试方法。 首先,记住ntdll模块的基地址(0x07ffa8a160000)并计算其大小(0x01e1000):


0:007> lm m ntdll Browse full module list start end module name 00007ffa`8a160000 00007ffa`8a341000 ntdll 0:007> ? 00007ffa`8a341000-00007ffa`8a160000 Evaluate expression: 1970176 = 00000000`001e1000 

考虑一个来自ntdll模块简单RtlFreeSid函数的列表,该函数从该模块调用RtlFreeHeap函数,同时记住RtlFreeSid和RtlFreeHeap字符(0x7ffa8a1cbc20和0x7ffa8a176df0)的地址:


 0:007> uf ntdll!RtlFreeSid ntdll!RtlFreeSid: 00007ffa`8a1cbc20 4053 push rbx 00007ffa`8a1cbc22 4883ec20 sub rsp,20h 00007ffa`8a1cbc26 488bd9 mov rbx,rcx 00007ffa`8a1cbc29 33d2 xor edx,edx 00007ffa`8a1cbc2b 65488b0c2560000000 mov rcx,qword ptr gs:[60h] 00007ffa`8a1cbc34 4c8bc3 mov r8,rbx 00007ffa`8a1cbc37 488b4930 mov rcx,qword ptr [rcx+30h] 00007ffa`8a1cbc3b e8b0b1faff call ntdll!RtlFreeHeap (00007ffa`8a176df0) 00007ffa`8a1cbc40 33c9 xor ecx,ecx 00007ffa`8a1cbc42 85c0 test eax,eax 00007ffa`8a1cbc44 480f45d9 cmovne rbx,rcx 00007ffa`8a1cbc48 488bc3 mov rax,rbx 00007ffa`8a1cbc4b 4883c420 add rsp,20h 00007ffa`8a1cbc4f 5b pop rbx 00007ffa`8a1cbc50 c3 ret 

另外,根据清单,很容易计算ntdll!RtlFreeSid函数的大小:


 0:007> ? 00007ffa`8a1cbc51 - 0x07ffa8a1cbc20 Evaluate expression: 49 = 00000000`00000031 

ntdll!RtlFreeHeap函数的大小对于我们的实验并不重要,因此为简单起见,您可以将其等于一个字节。


现在模拟隐藏ntdll模块:


 0:007> .reload /u ntdll Unloaded ntdll 

在调试器中使用ntdll!RtlFreeSid函数的地址进行此​​工作的字段的信息并不那么丰富(并且您已经可以仅通过虚拟地址访问该函数的开头):


 0:007> uf 00007ffa`8a1cbc20 00007ffa`8a1cbc20 4053 push rbx 00007ffa`8a1cbc22 4883ec20 sub rsp,20h 00007ffa`8a1cbc26 488bd9 mov rbx,rcx 00007ffa`8a1cbc29 33d2 xor edx,edx 00007ffa`8a1cbc2b 65488b0c2560000000 mov rcx,qword ptr gs:[60h] 00007ffa`8a1cbc34 4c8bc3 mov r8,rbx 00007ffa`8a1cbc37 488b4930 mov rcx,qword ptr [rcx+30h] 00007ffa`8a1cbc3b e8b0b1faff call 00007ffa`8a176df0 00007ffa`8a1cbc40 33c9 xor ecx,ecx 00007ffa`8a1cbc42 85c0 test eax,eax 00007ffa`8a1cbc44 480f45d9 cmovne rbx,rcx 00007ffa`8a1cbc48 488bc3 mov rax,rbx 00007ffa`8a1cbc4b 4883c420 add rsp,20h 00007ffa`8a1cbc4f 5b pop rbx 00007ffa`8a1cbc50 c3 ret 

为了添加一个合成模块,您需要调用IDebugSymbols3 :: AddSyntheticModule程序接口。 据我所知,根本没有内置命令可以进行此调用。 Mikhail I. Izmestev提出了类似的机制-相同的.reload ,但具有名称,地址和大小参数:“ Module = Address,Size”(并且可能在合成模块上实现)。


.reload ntdll = 00007ff8`470e1000,001e1000
 0:007> .reload ntdll=00007ff8`470e1000,001e1000 *** WARNING: Unable to verify timestamp for ntdll *** ERROR: Module load completed but symbols could not be loaded for ntdll 0:007> uf 00007ff8`4714bc20 ntdll+0x6ac20: 00007ff8`4714bc20 4053 push rbx 00007ff8`4714bc22 4883ec20 sub rsp,20h 00007ff8`4714bc26 488bd9 mov rbx,rcx 00007ff8`4714bc29 33d2 xor edx,edx 00007ff8`4714bc2b 65488b0c2560000000 mov rcx,qword ptr gs:[60h] 00007ff8`4714bc34 4c8bc3 mov r8,rbx 00007ff8`4714bc37 488b4930 mov rcx,qword ptr [rcx+30h] 00007ff8`4714bc3b e8b0b1faff call ntdll+0x15df0 (00007ff8`470f6df0) 00007ff8`4714bc40 33c9 xor ecx,ecx 00007ff8`4714bc42 85c0 test eax,eax 00007ff8`4714bc44 480f45d9 cmovne rbx,rcx 00007ff8`4714bc48 488bc3 mov rax,rbx 00007ff8`4714bc4b 4883c420 add rsp,20h 00007ff8`4714bc4f 5b pop rbx 00007ff8`4714bc50 c3 ret 

好吧,我们将使用调试器扩展,在这里pykd可以解救 。 在最新的(撰写本文时)版本0.3.4.3中 ,添加了用于管理合成模块的功能: addSyntheticModule (...)(在我们的示例中是必需的)和removeSyntheticModule (...)。


使用以前保存的基地址和模块大小,我们将有关隐藏的 ntdll模块的信息添加到调试器中(如果发生错误,将引发异常,因此,静默执行是成功的标志,可以忽略打印警告):


 0:007> !py Python 2.7.15 (v2.7.15:ca079a3ea3, Apr 30 2018, 16:30:26) [MSC v.1500 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> addSyntheticModule(0x07ffa8a160000, 0x01e1000, 'ntdll') *** WARNING: Unable to verify timestamp for ntdll >>> exit() 

现在,反汇编程序列表已变得更加有用:


 0:007> uf 00007ffa`8a1cbc20 ntdll+0x6bc20: 00007ffa`8a1cbc20 4053 push rbx 00007ffa`8a1cbc22 4883ec20 sub rsp,20h 00007ffa`8a1cbc26 488bd9 mov rbx,rcx 00007ffa`8a1cbc29 33d2 xor edx,edx 00007ffa`8a1cbc2b 65488b0c2560000000 mov rcx,qword ptr gs:[60h] 00007ffa`8a1cbc34 4c8bc3 mov r8,rbx 00007ffa`8a1cbc37 488b4930 mov rcx,qword ptr [rcx+30h] 00007ffa`8a1cbc3b e8b0b1faff call ntdll+0x16df0 (00007ffa`8a176df0) 00007ffa`8a1cbc40 33c9 xor ecx,ecx 00007ffa`8a1cbc42 85c0 test eax,eax 00007ffa`8a1cbc44 480f45d9 cmovne rbx,rcx 00007ffa`8a1cbc48 488bc3 mov rax,rbx 00007ffa`8a1cbc4b 4883c420 add rsp,20h 00007ffa`8a1cbc4f 5b pop rbx 00007ffa`8a1cbc50 c3 ret 

更确切地说,我们看到ntdll模块中的函数调用了同一模块中的另一个函数。


合成符号


在添加一个综合模块后,Listing变得内容更多,但仍不如原始模块那么雄辩。 它缺少字符 (在我们的例子中是RtlFreeSid和RtlFreeHeap函数的名称)。 为了解决这个问题,需要再次调用,但是调用的是不同的程序接口-IDebugSymbols3 :: AddSyntheticSymbol 。 再次派克 准备帮助我们:


 0:007> !py Python 2.7.15 (v2.7.15:ca079a3ea3, Apr 30 2018, 16:30:26) [MSC v.1500 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> addSyntheticSymbol(0x07ffa8a1cbc20, 0x31, 'RtlFreeSid') <pykd.syntheticSymbol object at 0x0000014D47699518> >>> addSyntheticSymbol(0x07ffa8a176df0, 1, 'RtlFreeHeap') <pykd.syntheticSymbol object at 0x0000014D476994A8> >>> exit() 

结果与使用ntdll pdb文件中的符号时的结果非常相似:


 0:007> uf 00007ffa`8a1cbc20 ntdll!RtlFreeSid: 00007ffa`8a1cbc20 4053 push rbx 00007ffa`8a1cbc22 4883ec20 sub rsp,20h 00007ffa`8a1cbc26 488bd9 mov rbx,rcx 00007ffa`8a1cbc29 33d2 xor edx,edx 00007ffa`8a1cbc2b 65488b0c2560000000 mov rcx,qword ptr gs:[60h] 00007ffa`8a1cbc34 4c8bc3 mov r8,rbx 00007ffa`8a1cbc37 488b4930 mov rcx,qword ptr [rcx+30h] 00007ffa`8a1cbc3b e8b0b1faff call ntdll!RtlFreeHeap (00007ffa`8a176df0) 00007ffa`8a1cbc40 33c9 xor ecx,ecx 00007ffa`8a1cbc42 85c0 test eax,eax 00007ffa`8a1cbc44 480f45d9 cmovne rbx,rcx 00007ffa`8a1cbc48 488bc3 mov rax,rbx 00007ffa`8a1cbc4b 4883c420 add rsp,20h 00007ffa`8a1cbc4f 5b pop rbx 00007ffa`8a1cbc50 c3 ret 

综合实体的特征


值得注意的是,在任何现有模块中都可以创建合成符号,而不仅仅是在合成模块中。 也就是说,您既可以将符号添加到具有可访问pdb-files的模块中,也可以添加到我们没有包含调试信息的文件的模块中。 但是您不能在模块外部创建字符。 也就是说,要在现有模块的边界之外标记任意内存地址,必须首先创建一个信封式合成模块,然后(如果需要,因为模块名称可能足以着色 ),在其中创建一个合成符号。


还值得一提的是,可以通过不小心移动手来删除合成模块和符号:


  • 重新加载模块的调试符号会删除该模块的所有综合符号;
  • 重新加载所有模块将删除所有合成模块。

!synexts


尽管在大多数情况下使用pykd都很方便(尤其是考虑到bootstarpper的存在),但是有时您可能会陷入必须花费大量精力才能使用pykd的情况。 例如,我曾经需要从运行Windows XP的主机上进行调试。 事实证明,pykd已经有一段时间不支持XP了,我需要合成字符和模块。 在我看来,对于该任务,组装一个单独的小型扩展程序比解决对pykd的完整XP支持要容易得多,该扩展程序可以解决范围广泛的必需任务。 结果,创建了一个单独的项目!Synexts


这是一个简单的扩展,对用户有两个导出:


  • !synexts.addsymbol-在任何现有模块中创建合成符号;
  • !synexts.addmodule-在调试引擎的内部列表中创建综合模块。

为了演示,我们模拟再次隐藏ntdll模块:


 0:007> .reload /u ntdll Unloaded ntdll 0:007> uf 00007ffa`8a1cbc20 00007ffa`8a1cbc20 4053 push rbx 00007ffa`8a1cbc22 4883ec20 sub rsp,20h 00007ffa`8a1cbc26 488bd9 mov rbx,rcx 00007ffa`8a1cbc29 33d2 xor edx,edx 00007ffa`8a1cbc2b 65488b0c2560000000 mov rcx,qword ptr gs:[60h] 00007ffa`8a1cbc34 4c8bc3 mov r8,rbx 00007ffa`8a1cbc37 488b4930 mov rcx,qword ptr [rcx+30h] 00007ffa`8a1cbc3b e8b0b1faff call 00007ffa`8a176df0 00007ffa`8a1cbc40 33c9 xor ecx,ecx 00007ffa`8a1cbc42 85c0 test eax,eax 00007ffa`8a1cbc44 480f45d9 cmovne rbx,rcx 00007ffa`8a1cbc48 488bc3 mov rax,rbx 00007ffa`8a1cbc4b 4883c420 add rsp,20h 00007ffa`8a1cbc4f 5b pop rbx 00007ffa`8a1cbc50 c3 ret 

同样,我们以合成实体的形式恢复了有关模块和符号的知识,但是已经使用了!Synexts扩展:


 0:007> !synexts.addmodule ntdll C:\Windows\System32\ntdll.dll 00007ffa`8a160000 0x01e1000 *** WARNING: Unable to verify timestamp for C:\Windows\System32\ntdll.dll Synthetic module successfully added 0:007> !synexts.addsymbol RtlFreeSid 00007ffa`8a1cbc20 31 Synthetic symbol successfully added 0:007> !synexts.addsymbol RtlFreeHeap 00007ffa`8a176df0 1 Synthetic symbol successfully added 

假定您已经将已编译的synexts.dll库 (对应于WinDbg使用的位深度)复制到调试器的winext目录中:


 0:007> .chain Extension DLL search Path: C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\WINXP;C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\winext;C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\winext\arcade;C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\pri;C:\Program Files (x86)\Windows Kits\10\Debuggers\x64;<…> Extension DLL chain: pykd.pyd: image 0.3.4.3, API 1.0.0, built Thu Jan 10 19:56:25 2019 [path: C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\winext\pykd.pyd] synexts: API 1.0.0, built Fri Jan 18 17:38:17 2019 [path: C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\winext\synexts.dll] <…> 

再一次,我们看到了将合成字符添加到合成模块的结果:


 0:007> uf 00007ffa`8a1cbc20 ntdll!RtlFreeSid: 00007ffa`8a1cbc20 4053 push rbx 00007ffa`8a1cbc22 4883ec20 sub rsp,20h 00007ffa`8a1cbc26 488bd9 mov rbx,rcx 00007ffa`8a1cbc29 33d2 xor edx,edx 00007ffa`8a1cbc2b 65488b0c2560000000 mov rcx,qword ptr gs:[60h] 00007ffa`8a1cbc34 4c8bc3 mov r8,rbx 00007ffa`8a1cbc37 488b4930 mov rcx,qword ptr [rcx+30h] 00007ffa`8a1cbc3b e8b0b1faff call ntdll!RtlFreeHeap (00007ffa`8a176df0) 00007ffa`8a1cbc40 33c9 xor ecx,ecx 00007ffa`8a1cbc42 85c0 test eax,eax 00007ffa`8a1cbc44 480f45d9 cmovne rbx,rcx 00007ffa`8a1cbc48 488bc3 mov rax,rbx 00007ffa`8a1cbc4b 4883c420 add rsp,20h 00007ffa`8a1cbc4f 5b pop rbx 00007ffa`8a1cbc50 c3 ret 

Source: https://habr.com/ru/post/zh-CN436644/


All Articles