赞
踩
win7 64 os下,360浏览器不定期出现卡死。
使用任务管理器给360se进程下dump。链接: https://pan.baidu.com/s/1Hd-T0T6WEcufjkFKDA6MHg?pwd=z4u5 提取码: z4u5。
windbg打开之,提示
For analysis of this file, run !analyze -v
wow64win!NtUserMessageCall+0xa:
00000000`749bfdaa c3 ret
这是一个32位进程的dmp,欲参看32位进程模式下的数据,须执行
!wow64exts.sw
其实,如果是64位os下给32位进程下dump,可以用C:\windows\syswow64\taskmgr.exe来抓dmp,这样就不用转换了。
输入!locks
看看有没有互相死锁的Critical Section变量。
0:000:x86> !locks
Scanned 9 critical sections
结果是没有。
~*k
浏览一下每个线程。
. 0 Id: 1f9c.7d4 Suspend: 0 Teb: fffdb000 Unfrozen # ChildEBP RetAddr 00 004fea9c 74ae74bb user32!NtUserMessageCall+0x15 01 004feb28 74ae6a8c user32!RealDefWindowProcWorker+0x73 02 004feb48 72fe0b64 user32!RealDefWindowProcW+0x4a 03 004feba4 72fe0b96 uxtheme!_ThemeDefWindowProc+0x197 04 004febc0 74ae729a uxtheme!ThemeDefWindowProcW+0x18 05 004fec08 666cba83 user32!DefWindowProcW+0x68 WARNING: Stack unwind information not available. Following frames may be wrong. 06 004fec60 63940a93 chrome!IsSandboxedProcess+0x440ff3 07 004feca0 6393fe82 chrome!ChromeMain+0x1dd5c3 08 004fed1c 637bd924 chrome!ChromeMain+0x1dc9b2 09 004fed44 637bd877 chrome!ChromeMain+0x5a454 0a 004fed84 74ae62fa chrome!ChromeMain+0x5a3a7 0b 004fedb0 74ae7316 user32!InternalCallWinProc+0x23 0c 004fee28 74ae77c4 user32!UserCallWinProcCheckWow+0xd8 0d 004fee88 74ae788a user32!DispatchMessageWorker+0x3b5 0e 004fee98 63ae3ff1 user32!DispatchMessageW+0xf 0f 004fef38 65dc4979 chrome!ChromeMain+0x380b21 10 004fefc0 63810c84 chrome!CreateQCMServiceForHiya+0x49b89 11 004ff000 637a274a chrome!ChromeMain+0xad7b4 12 004ff024 637a02b3 chrome!ChromeMain+0x3f27a 13 004ff060 6379f5bf chrome!ChromeMain+0x3cde3 14 004ff0b0 63ae26d0 chrome!ChromeMain+0x3c0ef 15 004ff0dc 63ae267e chrome!ChromeMain+0x37f200 16 004ff0ec 63ae265e chrome!ChromeMain+0x37f1ae 17 004ff0f8 637b7413 chrome!ChromeMain+0x37f18e 18 004ff154 637b72c3 chrome!ChromeMain+0x53f43 19 004ff188 63773813 chrome!ChromeMain+0x53df3 1a 004ff1c4 63773508 chrome!ChromeMain+0x10343 1b 004ff210 637733f3 chrome!ChromeMain+0x10038 1c 004ff220 63769ad9 chrome!ChromeMain+0xff23 1d 004ff308 6376965f chrome!ChromeMain+0x6609 1e 004ff34c 63c55015 chrome!ChromeMain+0x618f 1f 004ff3e4 0105d036 chrome!ChromeMain360+0x105 20 004ff490 01053d0a 360se!get_start+0xc016 21 004ff62c 01052d7e 360se!get_start+0x2cea 22 004ffb1c 0111539a 360se!get_start+0x1d5e 23 004ffb68 74d6344d 360se!GetHandleVerifier+0x5d04a 24 004ffb74 76f79802 kernel32!BaseThreadInitThunk+0xe 25 004ffbb4 76f797d5 ntdll_76f40000!__RtlUserThreadStart+0x70 26 004ffbcc 00000000 ntdll_76f40000!_RtlUserThreadStart+0x1b
0号线程,一般是ui处理相关的线程。user32!NtUserMessageCall
可能是进入内核或者是64位进程空间,猜测此例是0号线程进入内核不返回,进而出现ui卡死的现象。切换会64位空间,在看0号线程的rip
0:000:x86> !wow64exts.sw Switched to Host mode 0:000> k # Child-SP RetAddr Call Site 00 00000000`0020da98 00000000`7499aee6 wow64win!NtUserMessageCall+0xa 01 00000000`0020daa0 00000000`749b27ef wow64win!whNT32NtUserMessageCallCB+0x32 02 00000000`0020daf0 00000000`7499b022 wow64win!Wow64DoMessageThunk+0x8b 03 00000000`0020db30 00000000`749ed18f wow64win!whNtUserMessageCall+0x12e 04 00000000`0020dbd0 00000000`74972776 wow64!Wow64SystemServiceEx+0xd7 05 00000000`0020e490 00000000`749ed286 wow64cpu!ServiceNoTurbo+0x2d 06 00000000`0020e550 00000000`749ec69e wow64!RunCpuSimulation+0xa 07 00000000`0020e5a0 00000000`76db43c3 wow64!Wow64LdrpInitialize+0x42a 08 00000000`0020eaf0 00000000`76e19780 ntdll!LdrpInitializeProcess+0x17e3 09 00000000`0020efe0 00000000`76dc371e ntdll! ?? ::FNODOBFM::`string'+0x22790 0a 00000000`0020f050 00000000`00000000 ntdll!LdrInitializeThunk+0xe 0:000> ub rip wow64win!ZwUserGetMessage: 00000000`749bfd90 4c8bd1 mov r10,rcx 00000000`749bfd93 b806100000 mov eax,1006h 00000000`749bfd98 0f05 syscall 00000000`749bfd9a c3 ret 00000000`749bfd9b 0f1f440000 nop dword ptr [rax+rax] wow64win!NtUserMessageCall: 00000000`749bfda0 4c8bd1 mov r10,rcx 00000000`749bfda3 b807100000 mov eax,1007h 00000000`749bfda8 0f05 syscall
这里可以知道0号线程进入了syscall
正在等待返回。
回到32位进程空间,观察一下0号线程的栈回溯,看看是什么api使得它进入内核空间。初看起来是user32!NtUserMessageCall
,但是这里有提示WARNING: Stack unwind information not available. Following frames may be wrong.
。所以还是先准确地回溯栈。
参考此文https://blog.csdn.net/magictong/article/details/28161757。
得到栈回溯如下。
004fea64 004feb28 004fea68 76f5013a ntdll_76f40000!KiUserCallbackDispatcher+0x2e esp=004fea9c 004fea9c 74ae72b9 user32!NtUserMessageCall+0x15 004feaa0 74ae74bb user32!RealDefWindowProcWorker+0x73 004feb28 004feb48 004feb2c 74ae6a8c user32!RealDefWindowProcW+0x4a 004feb48 004feba4 004feb4c 72fe0b64 uxtheme!_ThemeDefWindowProc+0x197 004feba4 004febc0 004feba8 72fe0b96 uxtheme!ThemeDefWindowProcW+0x18 004febc0 004fec08 004febc4 74ae729a user32!DefWindowProcW+0x68 004fec08 004fec60 004fec0c 666cba83 chrome!IsSandboxedProcess+0x440ff3 004fec60 004feca0 004fec64 63940a93 chrome!ChromeMain+0x1dd5c3 004feca0 004fed1c 004feca4 6393fe82 chrome!ChromeMain+0x1dc9b2 004fed1c 004fed44 004fed20 637bd924 chrome!ChromeMain+0x5a454 004fed44 004fed84 004fed48 637bd877 chrome!ChromeMain+0x5a3a7 004fed84 004fedb0 004fed88 74ae62fa user32!InternalCallWinProc+0x23 004fedb0 004fee28 004fedb4 74ae7316 user32!UserCallWinProcCheckWow+0xd8 004fee28 004fee88 004fee2c 74ae77c4 user32!DispatchMessageWorker+0x3b5 004fee88 004fee98 004fee8c 74ae788a user32!DispatchMessageW+0xf 004fee98 004fef38 004fee9c 63ae3ff1 chrome!ChromeMain+0x380b21 004fef38 004fefc0 004fef3c 65dc4979 chrome!CreateQCMServiceForHiya+0x49b89 004fefc0 004ff000 004fefc4 63810c84 chrome!ChromeMain+0xad7b4 004ff000 004ff024 004ff004 637a274a chrome!ChromeMain+0x3f27a 004ff024 004ff060 004ff028 637a02b3 chrome!ChromeMain+0x3cde3 004ff060 004ff0b0 004ff064 6379f5bf chrome!ChromeMain+0x3c0ef 004ff0b0 004ff0dc 004ff0b4 63ae26d0 chrome!ChromeMain+0x37f200 004ff0dc 004ff0ec 004ff0e0 63ae267e chrome!ChromeMain+0x37f1ae 004ff0ec 004ff0f8 004ff0f0 63ae265e chrome!ChromeMain+0x37f18e 004ff0f8 004ff154 004ff0fc 637b7413 chrome!ChromeMain+0x53f43 004ff154 004ff188 004ff158 637b72c3 chrome!ChromeMain+0x53df3 004ff188 004ff1c4 004ff18c 63773813 chrome!ChromeMain+0x10343 004ff1c4 004ff210 004ff1c8 63773508 chrome!ChromeMain+0x10038 004ff210 004ff220 004ff214 637733f3 chrome!ChromeMain+0xff23 004ff220 004ff308 004ff224 63769ad9 chrome!ChromeMain+0x6609 004ff308 004ff34c 004ff30c 6376965f chrome!ChromeMain+0x618f 004ff34c 004ff3e4 004ff350 63c55015 chrome!ChromeMain360+0x105 004ff3e4 004ff490 004ff3e8 0105d036 360se!get_start+0xc016 004ff490 004ff62c 004ff494 01053d0a 360se!get_start+0x2cea 004ff62c 004ffb1c 004ff630 01052d7e 360se!get_start+0x1d5e 004ffb1c 004ffb68 004ffb20 0111539a 360se!GetHandleVerifier+0x5d04a 004ffb68 004ffb74 004ffb6c 74d6344d kernel32!BaseThreadInitThunk+0xe 004ffb74 004ffbb4 004ffb78 76f79802 ntdll_76f40000!__RtlUserThreadStart+0x70 004ffbb4 004ffbcc 004ffbb8 76f797d5 ntdll_76f40000!_RtlUserThreadStart+0x1b
因为esp已经是004fea9c,所以
004fea64 004feb28
004fea68 76f5013a ntdll_76f40000!KiUserCallbackDispatcher+0x2e
这一段不是栈内的数据。
通过ub eip
可以得知最后离开32位空间时正在执行的代码:
0:000:x86> ub eip
user32!DefWindowProcW+0x6e:
74ae72a0 90 nop
74ae72a1 90 nop
74ae72a2 90 nop
74ae72a3 90 nop
user32!NtUserMessageCall:
74ae72a4 b807100000 mov eax,1007h
74ae72a9 b900000000 mov ecx,0
74ae72ae 8d542404 lea edx,[esp+4]
74ae72b2 64ff15c0000000 call dword ptr fs:[0C0h]
ub 74ae6a8c
查看user32!RealDefWindowProcW
调用的是什么:
0:000:x86> ub 74ae6a8c
user32!RealDefWindowProcW+0x14:
74ae6a76 0f8428f10000 je user32!RealDefWindowProcW+0x16 (74af5ba4)
74ae6a7c 6a00 push 0
74ae6a7e ff7514 push dword ptr [ebp+14h]
74ae6a81 ff7510 push dword ptr [ebp+10h]
74ae6a84 51 push ecx
74ae6a85 52 push edx
74ae6a86 50 push eax
74ae6a87 e861ffffff call user32!RealDefWindowProcWorker (74ae69ed)
因此认为是user32!RealDefWindowProcW
调用user32!RealDefWindowProcWorker
,它里头再调用user32!NtUserMessageCall
,随后脱离了32位进程空间。
user32!NtUserMessageCall
的函数声明没有公开,从reactOS的代码ntuser.h
中可以找到
BOOL
NTAPI
NtUserMessageCall(
HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam,
ULONG_PTR ResultInfo,
DWORD dwType, /* FNID_XX types */
BOOL Ansi);
使用ida反汇编user32.dll,在user32!RealDefWindowProcWorker+0x73
即user32.dll+174bb的位置,可以看到:
loc_7DC6748A: ; _DWORD
push [ebp+cchWideChar]
push 29Eh ; _DWORD
push 0 ; _DWORD
push [ebp+Dst] ; _DWORD
push [ebp+arg_C] ; _DWORD
push edx ; _DWORD
push [ebp+hWnd] ; _DWORD
cmp edx, 400h
jnb loc_7DC94952
movzx eax, ds:_MessageTable[edx]
and eax, 3Fh
call ds:_gapfnScSendMessage[eax*4] ; NtUserMessageCall(x,x,x,x,x,x,x) ...
jmp loc_7DC66A56
查看栈顶的数据,可以分析出NtUserMessageCall
的实际参数:
0:000:x86> dps esp 004fea9c 74ae72b9 user32!NtUserMessageCall+0x15 004feaa0 74ae74bb user32!RealDefWindowProcWorker+0x73 004feaa4 000b0586; hWnd 004feaa8 00000112; Msg=WM_SYSCOMMAND 004feaac 0000f020; wParam=SC_MINIMIZE 004feab0 00000000; lParam 004feab4 00000000; ResultInfo 004feab8 0000029e; dwType 004feabc 00000000; Ansi 004feac0 000b0586 004feac4 fffffffe 004feac8 00000112 004feacc 000b0586 004fead0 0000a918 004fead4 00000112 004fead8 74ae692a user32!NtUserQueryWindow+0x15
所以这是对hwnd=b0586窗口发生最小化命令时,出现的卡死。
欲探知这个hwnd的窗口信息,可以使用SDbgExt的插件www.nynaeve.net/?p=94。
下载后,用32位程序的windbg来load之(64位的windbg程序和windbg Preview版本无法load):
0:000> .load D:\windbg\sdbgext\1.09\sdbgext.dll
0:000> !sdbgext.hwnd b0586
Invalid window handle 00000000000b0586
可能因为这是别的进程的窗口,所以从我进程的dmp里查不出这个窗口是什么。只能复现故障再用spy++找出那个窗口。
0号线程里,64位空间里的rsp和32位空间里的esp是不同的
0:000> r rsp
rsp=000000000020da98
0:000> !wow64exts.sw
Switched to 32bit mode
0:000:x86> r esp
esp=004fea9c
user32!NtUserMessageCall:
74ae72a4 b807100000 mov eax,1007h
74ae72a9 b900000000 mov ecx,0
74ae72ae 8d542404 lea edx,[esp+4]
74ae72b2 64ff15c0000000 call dword ptr fs:[0C0h]
call dword ptr fs:[0C0h]
是如何保存32位空间的eip,retaddr;之后如何进入64位空间,如何切换到rip,如何归还eip,如何回到retAddr呢?
自己写一个demo:
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <cstdio>
int main() {
for (int i = 0; i < MAXINT; ++i) {
printf("Press Enter\n");
getchar();
DefWindowProcW(HWND(0x302a2), WM_SYSCOMMAND, SC_MINIMIZE, 0);
}
return 0;
}
302a2
是一个notepad.exe的窗口句柄。
编译为32位程序。放到win7 64OS内执行。这里的syswow64里的user32.dll跟故障环境里的版本有点不一样。
先执行这个exe,再用windbg64位(不要用32位的windbg否则不能在32,64之间切换)的来attach。
先断点user32!DefWindowProcW
并继续执行:
bp user32!DefWindowProcW;g
。
待断点触发后,再断点769772b2
就是call那句:
USER32!NtUserMessageCall:
769772a4 b807100000 mov eax,1007h
769772a9 b900000000 mov ecx,0
769772ae 8d542404 lea edx,[esp+4]
769772b2 64ff15c0000000 call dword ptr fs:[0C0h]
769772b9 83c404 add esp,4
769772bc c21c00 ret 1Ch
可以看到:
769772b2 64ff15c0000000 call dword ptr fs:[0C0h] fs:0053:000000c0=00000000 0:000:x86> bd 1 0:000:x86> t wow64cpu!X86SwitchTo64BitMode: 73662320 ea1e2766733300 jmp 0033:7366271E 0:000:x86> t wow64cpu!CpupReturnFromSimulatedCode: 00000000`7366271e 67448b0424 mov r8d,dword ptr [esp] ds:00000000`0043f770=769772b9 0:000> p wow64cpu!CpupReturnFromSimulatedCode+0x5: 00000000`73662723 458985bc000000 mov dword ptr [r13+0BCh],r8d ds:00000000`001ffddc=7697692a 0:000> p wow64cpu!CpupReturnFromSimulatedCode+0xc: 00000000`7366272a 4189a5c8000000 mov dword ptr [r13+0C8h],esp ds:00000000`001ffde8=0043f7f8 0:000> p wow64cpu!CpupReturnFromSimulatedCode+0x13: 00000000`73662731 498ba42480140000 mov rsp,qword ptr [r12+1480h] ds:00000000`7efdc480=00000000001feb70 0:000> dds r13+bc *** ERROR: Symbol file could not be found. Defaulted to export symbols for VCRUNTIME140.dll - 00000000`001ffddc 769772b9 USER32!NtUserMessageCall+0x15 00000000`001ffde0 00000023 00000000`001ffde4 00000246 00000000`001ffde8 0043f770
当执行完73662320 ea1e2766733300 jmp 0033:7366271E
时,进程空间变为64位了。
接下来就是把RetAddr=dword ptr[esp]存入r8d。然后又把r8d存入dword ptr [r13+0BCh]。esp被存入dword ptr [r13+0C8h]。这里的r13估计就是64位进程空间的代码里用于存放32位空间寄存器和RetAddr的一个标识位。
一路f11按下去,可以看到:
wow64win!NtUserMessageCall:
00000000`736afda0 4c8bd1 mov r10,rcx
0:000> p
wow64win!ZwUserMessageCall+0x3:
00000000`736afda3 b807100000 mov eax,1007h
0:000> p
wow64win!ZwUserMessageCall+0x8:
00000000`736afda8 0f05 syscall
这里就是这个线程真正进入内核的地方。
再一路f11按下去,可以看到:
wow64cpu!CpuSimulate+0x141:
00000000`736626f1 4989a42480140000 mov qword ptr [r12+1480h],rsp ds:00000000`7efdc480=0000000000000000
wow64cpu!CpuSimulate+0x149:
00000000`736626f9 41c7460423000000 mov dword ptr [r14+4],23h ds:00000000`001febe4=00000023
00000000`73662701 41b82b000000 mov r8d,2Bh
00000000`73662707 418ed0 mov ss,r8w
00000000`73662711 458b8dbc000000 mov r9d,dword ptr [r13+0BCh] ds:00000000`001ffddc=769772b9
00000000`73662718 45890e mov dword ptr [r14],r9d
00000000`7366271b 41ff2e jmp fword ptr [r14]
又是r13+0bc,dword ptr[r13+0bch]里面的就是RetAddr,这里jmp过去,就回到USER32!NtUserMessageCall
的call的下一句了。
至于esp如何从64位的rsp还原,就不得而知了。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。