调试运行在Wow64子系统下的程序----x64版windbg调试win32程序

本文介绍在Windbg中调试Win32程序时,如何使用.wow64exts扩展命令来揭示隐藏在Wow64子系统中的调用栈信息,使调试更加直观。

    大家有没有遇到过这种情况:程序崩溃了,windbg分析线程堆栈时,输出了一堆乱七八糟的调用栈。更烦心的是,这堆调用栈中根本找不到跟自己程序相关的信息。来看一个类似的场景:运行C:\Windows\SysWOW64\calc.exe (win32版的calc.exe),windbg(x64 bit)附加到calc.exe:

0:003> ~*kb
   0  Id: 2718.1c98 Suspend: 1 Teb: 00000000`7efdb000 Unfrozen
 # RetAddr           : Args to Child                                                           : Call Site
00 00000000`741fae90 : 00000000`00000000 00000000`7420342a 00000000`00000000 00000000`0005138e : wow64win!ZwUserGetMessage+0xa
01 00000000`7424d18f : 00000000`0017ef6c 00000000`7efdb000 00000000`7efdd000 00000000`741faef4 : wow64win!whNtUserGetMessage+0x30
02 00000000`741d2776 : 00000000`74b378d7 00000000`74240023 00000000`00200246 00000000`0017ef64 : wow64!Wow64SystemServiceEx+0xd7
03 00000000`7424d286 : 00000000`00000000 00000000`741d1920 00000000`770f3128 00000000`7712c4f1 : wow64cpu!ServiceNoTurbo+0x2d
04 00000000`7424c69e : 00000000`00000000 00000000`00000000 00000000`74244b10 00000000`7ffe0030 : wow64!RunCpuSimulation+0xa
05 00000000`771243c3 : 00000000`002c3b20 00000000`00000000 00000000`77222e70 00000000`770f7550 : wow64!Wow64LdrpInitialize+0x42a

   1  Id: 2718.4d8 Suspend: 1 Teb: 00000000`7efd8000 Unfrozen
 # RetAddr           : Args to Child                                                           : Call Site
00 00000000`741d283e : 00000000`74b3a965 00000000`00000023 00000000`00000246 00000000`0373f30c : wow64cpu!CpupSyscallStub+0x9
01 00000000`7424d286 : 00000000`00000000 00000000`741d1920 00000000`00000000 00000000`00000000 : wow64cpu!WaitForMultipleObjects32+0x3b
02 00000000`7424c69e : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : wow64!RunCpuSimulation+0xa
03 00000000`771898c4 : 00000000`00000000 00000000`7efdf000 00000000`7efd8000 00000000`00000000 : wow64!Wow64LdrpInitialize+0x42a

   2  Id: 2718.2078 Suspend: 1 Teb: 00000000`7efd5000 Unfrozen
 # RetAddr           : Args to Child                                                           : Call Site
00 00000000`741d283e : 00000000`74b38e63 00000000`00000023 00000000`00000246 00000000`03bcf9fc : wow64cpu!CpupSyscallStub+0x9
01 00000000`7424d286 : 00000000`00000000 00000000`741d1920 00000000`00000000 00000000`00000000 : wow64cpu!WaitForMultipleObjects32+0x3b
02 00000000`7424c69e : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : wow64!RunCpuSimulation+0xa
03 00000000`771898c4 : 00000000`00000000 00000000`7efdf000 00000000`7efd5000 00000000`00000000 : wow64!Wow64LdrpInitialize+0x42a

    Windows x64系统为了兼容win32程序,加入了Wow64子系统。Wow子系统Hook win32的系统调用,在Wow64Cpu/Wow64/Wow64win三个dll中实现相应的系统调用功能。所以尽管calc.exe并不显示依赖这些dll,但是在windbg输出的调用栈中仍会找到这些模块信息。

    既然Wow64子系统只是Hook win32程序的API,那么他必然不会修改程序本身的实现,所以calc.exe的调用信息只是被隐藏在Wow64Cpu/Wow64/Wow64win这3个dll的背后。那么如何把他们暴露到阳光下?只要2条windbg命令就能实现:

0:003> .load wow64exts
0:003> !wow64exts.sw
Switched to Guest (WoW) mode

    wow64exts命令扩展随windbg一起发布,用于调试运行在Wow64子系统中的win32程序,默认并不加载,需要使用.load wow64exts加载。加载后通过命令!wow64exts.sw切换模式,切换后就像在x86系统上调试win32程序一样:

0:003:x86> ~*kb

   0  Id: 2718.1c98 Suspend: 1 Teb: 7efdb000 Unfrozen
 # ChildEBP RetAddr  Args to Child              
00 0017ef64 74b3790d 0017f068 00000000 00000000 USER32!NtUserGetMessage+0x15
01 0017ef80 00871cbc 0017f068 00000000 00000000 USER32!GetMessageW+0x33
02 0017fcfc 0088219a 00870000 00000000 00483faf calc!WinMain+0x878
03 0017fd8c 75d5343d 7efde000 0017fdd8 772e9802 calc!_initterm_e+0x1a1
04 0017fd98 772e9802 7efde000 77afdc5b 00000000 KERNEL32!BaseThreadInitThunk+0xe
05 0017fdd8 772e97d5 00882d6c 7efde000 00000000 ntdll_772b0000!__RtlUserThreadStart+0x70
06 0017fdf0 00000000 00882d6c 7efde000 00000000 ntdll_772b0000!_RtlUserThreadStart+0x1b

   1  Id: 2718.4d8 Suspend: 1 Teb: 7efd8000 Unfrozen
 # ChildEBP RetAddr  Args to Child              
00 0373f9ac 76dc171a 00000002 0373f9fc 00000001 ntdll_772b0000!ZwWaitForMultipleObjects+0x15
01 0373fa48 75d519fc 0373f9fc 0373fa70 00000000 KERNELBASE!WaitForMultipleObjectsEx+0x100
02 0373fa90 74b40882 00000002 7efde000 00000000 KERNEL32!WaitForMultipleObjectsExImplementation+0xe0
03 0373fae4 74b40b81 000000f4 0373fb44 ffffffff USER32!RealMsgWaitForMultipleObjectsEx+0x14d
04 0373fb00 71057910 00000001 0373fb44 00000000 USER32!MsgWaitForMultipleObjects+0x1f
05 0373fb4c 7105782f 00000000 00000000 00000000 gdiplus!BackgroundThreadProc+0x59
06 0373fb64 75d5343d 00832218 0373fbb0 772e9802 gdiplus!DllRefCountSafeThreadThunk+0x10
07 0373fb70 772e9802 00832218 74cbda33 00000000 KERNEL32!BaseThreadInitThunk+0xe
08 0373fbb0 772e97d5 7105781f 00832218 00000000 ntdll_772b0000!__RtlUserThreadStart+0x70
09 0373fbc8 00000000 7105781f 00832218 00000000 ntdll_772b0000!_RtlUserThreadStart+0x1b

   2  Id: 2718.2078 Suspend: 1 Teb: 7efd5000 Unfrozen
 # ChildEBP RetAddr  Args to Child              
00 03bcfa8c 72a3a41c 00000001 03bcfaec 00000001 ntdll_772b0000!ZwWaitForMultipleObjects+0x15
01 03bcfb34 75d5343d 00000000 03bcfb80 772e9802 WINMM!timeThread+0x3c
02 03bcfb40 772e9802 00000000 7404da03 00000000 KERNEL32!BaseThreadInitThunk+0xe
03 03bcfb80 772e97d5 72a3a3e0 00000000 00000000 ntdll_772b0000!__RtlUserThreadStart+0x70
04 03bcfb98 00000000 72a3a3e0 00000000 00000000 ntdll_772b0000!_RtlUserThreadStart+0x1b

#  3  Id: 2718.2734 Suspend: 1 Teb: 7ef9d000 Unfrozen
 # ChildEBP RetAddr  Args to Child              
00 0287f8c0 00000000 00000000 00000000 00000000 ntdll_772b0000!RtlUserThreadStart

从这次windbg的输出中可以看到熟悉的模块名字,就连寄存器也以x86架构的形式输出:

0:003:x86> r
eax=771e9390 ebx=00000000 ecx=00000000 edx=00000000 esi=00000000 edi=00000000
eip=772c01e4 esp=0287f8bc ebp=00000000 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
ntdll_772b0000!RtlUserThreadStart:
772c01e4 89442404        mov     dword ptr [esp+4],eax ss:002b:0287f8c0=00000000

最后,再次执行!wow64exts.sw命令,windbg又切换回x64模式,输出的内容跟文章开头一样~

参考:windows Internal 6th

Debugging WOW64

wow64_hook 源码历史更新 --------------------------------------------------------------- 2021/4/16  模块源码1.8.7 更新: 1:重新架构了穿插汇编指令,优化了一些代码和流程 2:在 远程hook64指令_安装()时新增可回调的3个自定义参数值,这些值在回调接口的[寄存器64.自定义值1;2;3]里可获取到该值 3:修复 寻找无效8字节指令地址()中一个重要BUG,此BUG极大可能导致之前本在 远程hook64指令_安装()时即导致目标程序崩溃的现象 本次更新比较重要 建议使用者更新到此本使用............ --------------------------------------------------------------- 2021/4/15  模块源码1.8.6 更新: 1:新增3组函数:X64_取模块代码区起始地址(),X64_取模块入口地址(),X64_取模块代码执行段大小() 2:自定义类型:模块信息64,的成员构成新增改动为 以下,在枚举模块中亦可直接取得 成员 模块基址, 长整数型, , , 模块映像的内存地址 也称为句柄 成员 模块长度, 整数型, , , 整个模块文件长度 成员 模块入口, 长整数型, , , 模块入口函数地址 如 Mian/DllMain 成员 模块代码入口, 长整数型, , , 模块代码执行区起始地址 成员 模块代码长度, 整数型, , , 模块代码执行区的长度 成员 模块名称, 文本型, , , 文件名称 成员 模块路径, 文本型, , , 完整的路径地址 3:新消息接口()远程返回_调用回调子程序()优化了代码严谨性,减少hook目标崩溃的可能性 4:寻找无效8字节指令地址()由之前的全模块查找 改动为 在模块代码执行区查找 5:改写模块实列为 一对多的模式 6:模块实列操作控件的方式由变量改为堆内存,避免引起多线程自身崩溃 7:模块实列 对 recv,recvfrom两个函数的hook方式由原先 在回调内 暂停recv-->recv_call-->恢复recv,的方式改为经过特殊改造的 recv_call,这个call经过特殊处理,在recv回调函数内调用,用来取得真实长度,这个调用会绕过hook位置,所以不会触发 recv回调,详见源码 8:修改了一些已知可能出现的问题 --------------------------------------------------------------- 2021/4/12 模块源码 v1.8.2更新 1:修复 x64_远调用函数()在 易语言 主线程调用时造成消息无法回调,导致易语言主线程窗口卡死的问题。      感谢楼下易友发现的BUG,已经第一时间更新 --------------------------------------------------------------- 2021/4/12 模块源码 v1.8.1更新 1:修复 hook全部卸载时的流程写法的一个错误,由于句柄的提前关闭导致多个hook点卸载不干净的问题 2:改写了消息回调时线程传参的代码优化,优化了其他一些小问题 3:  鉴于很多朋友需要,改写了模块自带实列,对TCP,UDP的两组封包函数做了hook实列写法 4:列子中同样增加对x64_远调用函数()的应用写了几个列子,如使用套接字取得本地或远端IP端口API调用的的应用实列 5:本hook模块不支持非模块内存区hook,如申请的动态分配页等,不是不能支持,只是觉得没有任何意义,对这方面有需求的,自行改写模块源码使用 提醒:hook回调函数中尽量减少耗时代码,时间越长返回越慢,回调中谨慎操作控件,如必须要用到可参考源码中实列写法采用线程操作 --------------------------------------------------------------- 2021/3/1   模块源码v1.6更新: 1:修复  x64_远程调用函数()命令,在没有提供 寄存器 参数时,没有返回值的BUG。 --------------------------------------------------------------- 2021/2/28 模块源码v1.5更新: 一:修复win7 64位系统下枚举模块 出现部分模块长度出现负数的问题,从而导致部分win7用户不能使用 二:强化 远程hook64指令_安装 的稳定性:        1,穿插代码中增加对标志位的保护,避免hook位置长度下一条指令为跳转时产生跳转错乱的问题,强化了hook任意位置的定位        2,因为穿插代码中会调用AP
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值