标题: TTD调试进阶之ttd-bindings 创建: 2022-07-27 16:20 更新: 2022-08-09 08:51 链接: https://scz.617.cn/windows/202207271620.txt windbgx或WinDbg Preview是一个东西,但与windbg不是一回事。TTD与windbgx相关, 与windbg无关。windbgx从Microsoft Store安装,windbg有独立安装包。 关于TTD历史,参看 《TTD历史回顾》 https://scz.617.cn/windows/202207191506.txt TTD录制的.run文件格式未公开,之前只能在WinDbg Preview中操作.run,很难对之 进行脚本操作,比如,想对position进行大小比较,很费劲。 Bindings for Microsoft WinDBG TTD https://github.com/commial/ttd-bindings 上文作者对TTDReplay.dll进行逆向工程,逆出一套编程解析处理.run文件的API。没 有完整逆向,但基本功能都有,比windbgx的GUI还多出一些功能。 C++ bindings功能较多,Python bindings功能弱一些,后者无法设置 CallRetCallback、MemCallback。此外,pyTTD.pyd用的是Python 3.10,为了在 Python 3.9中用,必须自己编译出pyTTD.pyd。 作者提供了.cpp、py的使用示例,对于逆向工程人员来说,浅显易懂,可以照猫画虎 实现自定义功能。 TTD::ReplayEngine ttdengine = TTD::ReplayEngine(); ttdengine.Initialize( trace_path ); 打开.run文件,初始化TTD引擎 TTD::Cursor ttdcursor = ttdengine.NewCursor(); ttdcursor.SetPosition( position.Major, position.Minor ); 顾名思义,ttdcursor相当于!tt,可以来回切换position 目前支持两种回调函数,一种是call、ret指令触发,另一种就是内存断点。此处的 内存断点是TTD的特有支持,不是用硬件断点那套,支持任意长度的内存断点,支持 读、写、执行。 -------------------------------------------------------------------------- MemBreakpoint.addr = ImageBbase; MemBreakpoint.size = imageSize; MemBreakpoint.flags = TTD::BP_FLAGS::EXEC; ttdcursor.AddMemoryWatchpoint( &MemBreakpoint ); ttdcursor.SetMemoryWatchpointCallback( ( TTD::PROC_MemCallback )MemCallback, 0 ); static bool MemCallback ( unsigned __int64 callback_value, TTD::TTD_Replay_MemoryWatchpointResult *mem, struct TTD::TTD_Replay_IThreadView *thread_info ) { g_mutex.lock(); g_hit_set.insert( mem->addr ); g_mutex.unlock(); return FALSE; } -------------------------------------------------------------------------- 这是example_cov中的MemCallback,比较简单,只记录了mem->addr。 TTD::TTD_Replay_ICursorView_ReplayResult replay_ret; ttdcursor.ReplayForward( &replay_ret, &end, -1 ); ReplayForward第二形参指定end position;第三形参-1对应stepCount,若为1,就 是单步执行。 无论单步、非单步,之前设置的回调函数都会命中,可以在回调函数中做很多动作, 相当于交互式调试时条件断点命中后执行的命令。由于可以C++编程,肯定比断点命 令强大太多。 TTD不支持wt,会报错 Operation not supported in current debug session 参看example_calltree,可以自己实现wt的效果。要求如下DLL就位 dbghelp.dll TTDReplay.dll TTDReplayCPU.dll example_calltree的输出类似这种 [1] -> Calculator+0x23c080 (ABE36:C6) | [2] -> Calculator+0x230450 (ABE36:E8) | | [3] -> ucrtbase!calloc (ABE36:F5) | | [3] <- ucrtbase!_calloc_base+0x61 (ABE39:87) ret=0x2ca4c833c80 | [2] <- Calculator+0x230484 (ABE39:8B) ret=0x2ca4c833c80 | [2] -> ucrtbase!free (ABE39:EA) | | [3] -> ntdll!RtlFreeHeap (ABE39:F4) | | [3] <- ntdll!RtlFreeHeap+0x66 (ABE3C:42) ret=0x1 | [2] <- ucrtbase!_free_base+0x27 (ABE3C:46) ret=0x1 [1] <- Calculator+0x23c292 (ABE3C:4B) ret=0x1 受限于UWP的Mitigation Policies,DynamoRIO缺省无法录制Win10 Calculator。理 论上可以改DynamoRIO,使用反射式DLL加载,而非LoadLibrary,使之支持UWP,但我 懒得搞。 参看example_cov,可以对UWP生成为Lighthouse所支持的.log文件,从而对UWP进行 Coverage Diff。即使不考虑UWP情形,对普通进程而言,DynamoRIO录制也不如TTD录 制+example_cov截取,后者可以精确指定position范围,从而让Coverage Diff更有 意义。 example_cov的输出类似这种 Calculator+0xacb0 Calculator+0xacb3 Calculator+0xacb7 Calculator+0xacbb 参看example_tenet,生成为Tenet所支持.trace文件。用了一下Tenet,感觉意义不 大。 example_tenet的输出类似这种 Rsp=0xd695bfd998,Rip=0x7ff6b3fdc080,mw=0xd695bfd998:9d2efdb3f67f0000 Rip=0x7ff6b3fdc085,mw=0xd695bfd9a8:70dabf95d6000000 mw表示发生内存写操作,等号后面是内存地址,冒号后面是16进制字节流。 example_tenet对小范围的position有效,对全范围使用时有坑,出现漏指令的现象; 可能与单步执行有关,可能与多线程有关,总之,极不靠谱。 假设用TTD录制Win10 Calculator的乘法运算,想找出乘法运算的汇编指令。用常规 TTD反向执行找出来过,参看 《MSDN系列(46)--WinDbg Preview TTD入门》 https://scz.617.cn/windows/202201251528.txt 之前对Win7 calc试过Coverage Diff,Win7 calc有符号,它的算术运算逻辑比较独 立,用Coverage Diff很快就定位到乘法运算。参看 《MSDN系列(47)--Lighthouse/DynamoRIO/Coverage Diff入门》 https://scz.617.cn/windows/202202101230.txt 对Win10 Calculator测试Coverage Diff,没有符号,它的算术运算逻辑不那么独立, 乘法运算位于某个公共函数中,该公共函数被频繁调用,很难制造a.log不覆盖该公 共函数、b.log覆盖该公共函数的效果,处理Win7 calc的套路在此不适用。 有ttd-bindings之后,找出乘法运算多了新思路。合理推测应有"imul rega,regb", 事先不知道哪两个寄存器,只是逻辑推断可能出现这样的场景。编程遍历"!tt 0"与 "!tt 100"之间的代码,当某个通用寄存器值为a、另一个通用寄存器值为b时,记录 此时的rip、position。若特征值a、b足够特异,log文件中的记录条数不会太多,人 工检视即可定位乘法运算。 TTD调试支持TTD.Memory的用法,bluerust提出过另一种需求,想将一段position范 围内指定寄存器变迁史列出来,比如这种效果 rip=0x7ff6b3fd2e98 (ABE36:C5) rax=0x41414141 rip=0x7ff6b3fd0457 (ABE36:EA) rax=0x3 rip=0x7ff6b3fd045f (ABE36:ED) rax=0xc rip=0x7ff6b3fd047b (ABE39:88) rax=0x2ca4c833c80 rip=0x7ff6b3fdc1ba (ABE39:9F) rax=0x41414141 rip=0x7ff6b3fdc1d5 (ABE39:A8) rax=0x51201314 rip=0x7ff6b3fdc1dc (ABE39:AA) rax=0x14add2aaaa10ec14 这事用windbg native script可能够呛,后者没法比较position。jscript应该能做 到,jscript功能还是很强的。用ttd-bindings会很简单,不妨就以此需求练手。 可以对着ttd-bindings的源码自己逆一下TTDReplay.dll,或许有新发现。现阶段推 荐用C++ bindings,Python bindings实在太弱了。 本文向逆向工程人员推荐ttd-bindings,有用,好用,值得用,将TTD利用方式推进 到新高度。