系统: windows xp sp3
崩溃软件: 公司内部某文档安全软件
调试工具: windbg
反汇编工具: ida pro 5.5
介绍: 该文档安全软件是由公司内部vpn工具启动,当在vpn工具输入用户密码连接网络后,vpn工具会自动把文档安全软件启动, 但每次启动都会coredump. 由于电脑用了windbg作为实时调试器,每次启动windbg都会弹出来, 显示coredump位置
分析过程:
coredump信息
(50c4.50d0): Access violation - code c0000005 (!!! second chance !!!)
eax=00000000 ebx=01aa5c62 ecx=00000000 edx=7efefeff esi=01aa4e34 edi=0012dbe0
eip=10009ffb esp=0012d980 ebp=0012dae0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
*** WARNING: Unable to verify checksum for C:\Program Files\MarkAny\Document SAFER\MAAgtClient.DLL
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\MarkAny\Document SAFER\MAAgtClient.DLL -
MAAgtClient!CDRMLogin::SetDocHeaderByServerAcl+0x54a9:
10009ffb 8b01 mov eax,dword ptr [ecx] ds:0023:00000000=????????
由上面可以看coredump是由于往空地址里取值.
由
*** ERROR: Symbol file could not be found. Defaulted to export symbols for
C:\Program Files\MarkAny\Document SAFER\MAAgtClient.DLL -
可知, coredump地址是位于MAAgtClient.DLL
用IDA打开MAAgtClient.DLL, 查看一下coredump地址的汇编, 看一下ecx是由哪里得到的.
.text:10009FD1 loc_10009FD1: ; CODE XREF: _strcpy+5 j
.text:10009FD1 ; _strcat+52 j ...
.text:10009FD1 mov
ecx, [esp+4+Source]
.text:10009FD5 test ecx, 3
.text:10009FDB jz short loc_10009FF6
.text:10009FDD
.text:10009FDD loc_10009FDD: ; CODE XREF: _strcat+7D j
.text:10009FDD mov dl, [ecx]
.text:10009FDF inc ecx
.text:10009FE0 test dl, dl
.text:10009FE2 jz short loc_1000A048
.text:10009FE4 mov [edi], dl
.text:10009FE6 inc edi
.text:10009FE7 test ecx, 3
.text:10009FED jnz short loc_10009FDD
.text:10009FEF jmp short loc_10009FF6
.text:10009FF1 ; ---------------------------------------------------------------------------
.text:10009FF1
.text:10009FF1 loc_10009FF1: ; CODE XREF: _strcat+9E j
.text:10009FF1 ; _strcat+B8 j
.text:10009FF1 mov [edi], edx
.text:10009FF3 add edi, 4
.text:10009FF6
.text:10009FF6 loc_10009FF6: ; CODE XREF: _strcat+6B j
.text:10009FF6 ; _strcat+7F j
.text:10009FF6 mov edx, 7EFEFEFFh
.text:10009FFB mov eax, [ecx]
.text:10009FFD add edx, eax
.text:10009FFF xor eax, 0FFFFFFFFh
.text:1000A002 xor eax, edx
.text:1000A004 mov edx, [ecx]
.text:1000A006 add ecx, 4
.text:1000A009 test eax, 81010100h
.text:1000A00E jz short loc_10009FF1
.text:1000A010 test dl, dl
.text:1000A012 jz short loc_1000A048
.text:1000A014 test dh, dh
.text:1000A016 jz short loc_1000A03F
.text:1000A018 test edx, 0FF0000h
.text:1000A01E jz short loc_1000A032
.text:1000A020 test edx, 0FF000000h
.text:1000A026 jz short loc_1000A02A
.text:1000A028 jmp short loc_10009FF1
从上面看,主要是这一行
.text:10009FD1 mov ecx, [esp+4+Source]
给ecx赋值的.
Source这个是指代什么的数据呢? 在IDA中, 应该是这个函数开初就有定义.
由
.text:10009FD1 loc_10009FD1: ; CODE XREF: _strcpy+5 j
.text:10009FD1 ; _strcat+52 j ...
可以看到
.text:10009FD1
是由_strcat, _strcpy来引用
先看一下_strcat的开始
.text:10009F70 ; char *__cdecl strcat(char *Dest, const char *Source)
.text:10009F70 _strcat proc near ; CODE XREF: sub_1000F800+E6 p
.text:10009F70 ; sub_1000F800+F7 p ...
.text:10009F70
.text:10009F70 Dest = dword ptr 4
.text:10009F70 Source = dword ptr 8
和_strcpy的开始
.text:10009F60 ; char *__cdecl strcpy(char *Dest, const char *Source)
.text:10009F60 _strcpy proc near ; CODE XREF: CDRMLogin::GetUserID(char *)+3C p
.text:10009F60 ; CDRMLogin::GetLocalLoginPW(char *)+51 p ...
.text:10009F60
.text:10009F60 Dest = dword ptr 4
.text:10009F60 Source = dword ptr 8
可以看到, Source是指向_strcat, _strcpy的第二个参数源字符串的地址. 当这个地址为空,就出现coredump. 那么这个空地址是从哪里传进来的? 还有, coredump的函数是_strcat还是_strcpy?
看一下堆栈:
0:000> kb
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
0012dae0 0040bd5f 0012dbe0 0012eb4b 01aa5c56 MAAgtClient!CDRMLogin::SetDocHeaderByServerAcl+0x54a9
0012daf8 7c980d45 01aa4c14 01aa6038 01aa5c62 MADRMAgent!CAclCCF::~CAclCCF+0x2a1f
0012db54 7c937d3b 0012dc16 00000524 7c920000 ntdll!RtlWalkFrameChain+0xb3
0012dba4 7c937c02 7c920000 0012dc16 0012dc10 ntdll!LdrGetProcedureAddress+0x4b
0012dc74 7c92dfba 7c802277 7c92d34a 00000086 ntdll!RtlCompareUnicodeString+0x412
00000000 00000000 00000000 00000000 00000000 ntdll!NtWriteVirtualMemory+0xc
根据x86的调用约定, 0040bd5f是上一层的调用者返回地址. 详情请见<coredump问题原理探究>第三章.
先看一下0040bd5f是位于哪个模块
0:000> lm
start end module name
00390000 003e1000 cipher (deferred)
003f0000 003f9000 Normaliz (deferred)
00400000 0053b000 MADRMAgent (export symbols) C:\Program Files\MarkAny\Document SAFER\MADRMAgent.exe
01540000 0157f000 Encryption (deferred)
01fe0000 0202a000 VrvHook (deferred)
020e0000 02108000 ImageSAFERFilter (deferred)
10000000 10029000 MAAgtClient C (export symbols) C:\Program Files\MarkAny\Document SAFER\MAAgtClient.DLL
13000000 1308f000 libdb41 (deferred)
1f840000 1f857000 odbcint (deferred)
3e410000 3e4f7000 WININET (deferred)
3eab0000 3ec9c000 iertutil (deferred)
43ce0000 43e14000 urlmon (deferred)
5adc0000 5adf7000 uxtheme (deferred)
5fdd0000 5fe25000 NETAPI32 (deferred)
60fd0000 61025000 hnetcfg (deferred)
61be0000 61bed000 MFC42LOC (deferred)
62c20000 62c29000 LPK (deferred)
68000000 68036000 rsaenh (deferred)
719c0000 719fe000 mswsock (deferred)
71a00000 71a08000 wshtcpip (deferred)
71a10000 71a18000 WS2HELP (deferred)
71a20000 71a37000 WS2_32 (deferred)
71a40000 71a4b000 WSOCK32 (deferred)
72240000 72245000 sensapi (deferred)
72f70000 72f96000 WINSPOOL (deferred)
73540000 7357d000 ODBC32 (deferred)
73640000 7366e000 msctfime (deferred)
73d30000 73e22000 MFC42 (deferred)
73fa0000 7400b000 USP10 (deferred)
74680000 746cc000 MSCTF (deferred)
759d0000 75a7f000 USERENV (deferred)
75ff0000 76055000 MSVCP60 (deferred)
76060000 761b6000 setupapi (deferred)
762f0000 762f5000 MSIMG32 (deferred)
76300000 7631d000 IMM32 (deferred)
76320000 76367000 comdlg32 (deferred)
76760000 7676c000 cryptdll (deferred)
76990000 76ace000 ole32 (deferred)
76b10000 76b3a000 WINMM (deferred)
76bc0000 76bcb000 psapi (deferred)
76d30000 76d48000 iphlpapi (deferred)
76d70000 76d92000 Apphelp (deferred)
76e50000 76e5e000 rtutils (deferred)
76e60000 76e72000 rasman (deferred)
76e80000 76eaf000 TAPI32 (deferred)
76eb0000 76eec000 RASAPI32 (deferred)
76ef0000 76f17000 DNSAPI (deferred)
76f90000 76f96000 rasadhlp (deferred)
770f0000 7717b000 OLEAUT32 (deferred)
77180000 77283000 COMCTL32 (deferred)
77bd0000 77bd8000 VERSION (deferred)
77be0000 77c38000 msvcrt (deferred)
77c40000 77c65000 msv1_0 (deferred)
77d10000 77da0000 USER32 (deferred)
77da0000 77e49000 ADVAPI32 (deferred)
77e50000 77ee3000 RPCRT4 (deferred)
77ef0000 77f39000 GDI32 (deferred)
77f40000 77fb6000 SHLWAPI (deferred)
77fc0000 77fd1000 Secur32 (deferred)
78480000 7850e000 MSVCP90 (deferred)
78520000 785c3000 MSVCR90 (deferred)
7c800000 7c91e000 kernel32 (deferred)
7c920000 7c9b6000 ntdll (export symbols) C:\WINDOWS\system32\ntdll.dll
7d590000 7dd84000 SHELL32 (deferred)
Unloaded modules:
71dd0000 71de5000 msapsspc.dll
78080000 78091000 MSVCRT40.dll
767c0000 767e9000 schannel.dll
765e0000 76675000 CRYPT32.dll
76db0000 76dc2000 MSASN1.dll
757f0000 75805000 digest.dll
72f10000 72f57000 msnsspc.dll
78080000 78091000 MSVCRT40.dll
02260000 0227c000 DSWrapper.dll
02280000 0228b000 MASYSID.dll
可见0040bd5f是位于C:\Program Files\MarkAny\Document SAFER\MADRMAgent.exe
用IDA打开MADRMAgent.exe , 并跳转0040bd5f的附近汇编.
.text:0040BD57 push edx
.text:0040BD58 stosw
.text:0040BD5A call [email protected]@@[email protected] ; CDRMLogin::GetUserID(char *)
.text:0040BD5F lea eax, [esp+102Ch+var_F38]
看来堆栈有一些调用没有显示.那么看一下CDRMLogin::GetUserID是在哪定义
0:000> x *!*GetUserID*
00396300 cipher!CFlexibleHeader::GetUserID (<no parameter info>)
0039bc80 cipher!CCCF::GetUserID (<no parameter info>)
003a1080 cipher!CDRMLogic::GetUserID (<no parameter info>)
10004473 MAAgtClient!CDRMLogin::GetUserID (<no parameter info>)
可见, CDRMLogin::GetUserID是在MAAgtClient.DLL定义
看一下它的汇编:
.text:10004473 ; int __stdcall CDRMLogin__GetUserID(char *Dest)
.text:10004473 public [email protected]@@[email protected]
.text:10004473 [email protected]@@[email protected] proc near ; DATA XREF: .rdata:off_1001AB68 o
.text:10004473
.text:10004473 var_150 = byte ptr -150h
.text:10004473 var_C = dword ptr -0Ch
.text:10004473 var_4 = dword ptr -4
.text:10004473 Dest = dword ptr 8
.text:10004473
.text:10004473 mov eax, offset sub_10014714
.text:10004478 call __EH_prolog
.text:1000447D sub esp, 144h
.text:10004483 lea ecx, [ebp+var_150]
.text:10004489 call ds:[email protected]@[email protected] ; CCCF::CCCF(void)
.text:1000448F and [ebp+var_4], 0
.text:10004493 lea ecx, [ebp+var_150]
.text:10004499 call ds:[email protected]@@QAEHXZ ; CCCF::CheckCCFValidity(void)
.text:1000449F lea ecx, [ebp+var_150]
.text:100044A5 call ds:[email protected]@@QAEPADXZ ; CCCF::GetUserID(void)
.text:100044AB push eax ; Source
.text:100044AC push [ebp+Dest] ; Dest
.text:100044AF call _strcpy
.text:100044B4 or [ebp+var_4], 0FFFFFFFFh
.text:100044B8 pop ecx
.text:100044B9 pop ecx
.text:100044BA lea ecx, [ebp+var_150]
.text:100044C0 call ds:[email protected]@[email protected] ; CCCF::~CCCF(void)
.text:100044C6 mov ecx, [ebp+var_C]
.text:100044C9 mov large fs:0, ecx
.text:100044D0 leave
.text:100044D1 retn 4
.text:100044D1 [email protected]@@[email protected] endp
从
.text:100044A5 call ds:[email protected]@@QAEPADXZ ; CCCF::GetUserID(void)
.text:100044AB push eax ; Source
.text:100044AC push [ebp+Dest] ; Dest
.text:100044AF call _strcpy
可知,coredump的地址是位于_strcpy, 而_strcpy的第二个参数是CCCF::GetUserID的返回值.
从这里可以看到, 这里的代码是没有对CCCF::GetUserID的返回值进行判空处理, 从而导致coredump
至于是否会由于strcpy而导致栈溢出, 就先不管了.