您的位置:首页 > 移动开发

gdiplus函数调用错误导致程序Crash分析

2011-05-19 13:50 423 查看
前几天看一个Crash的问题。功能是Word上的一个插件,点击上面的一个button,cation实现了,但点击结束后,过一会Word就crash了。下面是问题的分析.

 

 

调试问题

 

问题复现

程序crash时,获得如下log.

 
4aea74b2 ??              ???
0:006> kb
ChildEBP RetAddr  Args to Child
WARNING: Frame IP not in any known module. Following frames may be wrong.
0a89ff80 7c80a173 00000011 00000000 8984c9f8 0x4aea74b2
0a89ffb4 7c80b729 00000000 7c80a174 00000011 kernel32!_allmul+0xa
0a89ffec 00000000 4aea7456 00000000 00000000 kernel32!BaseThreadStart+0x37


0x4aea74b2,指令无效。是访问内存溢出导致EIP被修改,还是对应模块从内存中unload出去了?

 

开始调试

上调试器,看看是否有模块被unload了。

0:004> .logopen c:/crash.log
0:004> sxn ld
0:004> sxn ud
0:004> g
ModLoad: 0a490000 0a501000   D:/Program Files/ProductPath/dllistmt.dll
ModLoad: 4ae90000 4b03b000   D:/WINDOWS/WinSxS/x86_Microsoft.Windows.GdiPlus_6595b64144ccf1df_1.0.6002.22509_x-ww_c7dad023/gdiplus.dll
ModLoad: 0a970000 0aaf4000   D:/Program Files/ProductPath/dllocres.dll
ModLoad: 0ab00000 0ab8d000   D:/Program Files/ProductPath/dllWebAPI.dll
ModLoad: 0a540000 0a561000   D:/Program Files/ProductPath/dllhttp.dll
ModLoad: 3e410000 3e4e1000   D:/WINDOWS/system32/WININET.dll
ModLoad: 0a580000 0a589000   D:/WINDOWS/system32/Normaliz.dll
ModLoad: 3eab0000 3eaf5000   D:/WINDOWS/system32/iertutil.dll
Launching spec process to start at spec directory.
/helpModLoad: 5fdd0000 5fe25000   D:/WINDOWS/system32/netapi32.dll
ModLoad: 43ce0000 43e08000   D:/WINDOWS/system32/urlmon.dll
Unload module D:/Program Files/ProductPath/dllistmt.dll at 0a490000
Unload module D:/WINDOWS/WinSxS/x86_Microsoft.Windows.GdiPlus_6595b64144ccf1df_1.0.6002.22509_x-ww_c7dad023/gdiplus.dll at 4ae90000
Unload module D:/Program Files/ProductPath/dllWebAPI.dll at 0ab00000
Unload module D:/Program Files/ProductPath/dllhttp.dll at 0a540000
Unload module D:/WINDOWS/system32/WININET.dll at 3e410000
Unload module D:/WINDOWS/system32/Normaliz.dll at 0a580000
Unload module D:/Program Files/ProductPath/dllocres.dll at 0a970000
(d1c.a18): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=00000000 ecx=00000000 edx=7c92e514 esi=4b0172a4 edi=77d2a340
eip=4aea74c6 esp=0afaff84 ebp=0afaffb4 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010216
4aea74c6 ??              ???


 

 

从L6和L29的内容,说明准备执行unloaded模块GdiPlus中的函数,导致访问违例。

这时我们可以继续跟踪看看这个地址偏移量是什么code,也可以使用Application Verifier看看它能不能给我们更多的信息。

 

 

Application Verifier

 

下面是启动Application Verifier后的调试结果。

Launching spec process to start at spec directory.
/help
===========================================================
VERIFIER STOP 00000201: pid 0xFAC: unloading dll containing active critical section
4B0172D4 : Critical section address
002EB2F4 : Initialization stack trace. Use dds to dump it if non-NULL.
0145740C : DLL name address (use `du ADDRESS' to dump if not null)
4AE90000 : DLL base address
===========================================================
(fac.fb8): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=002eb2f4 ecx=7c93f6a7 edx=0012ef04 esi=00000201 edi=4b0172d4
eip=7c92120e esp=0012f138 ebp=0012f14c iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!DbgBreakPoint:
7c92120e cc              int     3
0:000> !cs -s 4B0172D4
-----------------------------------------
Critical section   = 0x4b0172d4 (gdiplus!GpMallocTrackingCriticalSection::critSec+0x0)
DebugInfo          = 0x02643b80
NOT LOCKED
LockSemaphore      = 0x0
SpinCount          = 0x00000000
Stack trace for DebugInfo = 0x02643b80:
0x7c9315bb: ntdll!RtlInitializeCriticalSectionAndSpinCount+0xC9
0x7c931664: ntdll!RtlInitializeCriticalSection+0xF
0x7c809f9f: kernel32!InitializeCriticalSection+0xE
0x4aea984d: gdiplus!GpMallocTrackingCriticalSection::InitializeCriticalSection+0x2C
0x4aea90ee: gdiplus!InternalGdiplusStartup+0x3C
0x4aea7633: gdiplus!GdiplusStartup+0x4B
0x0c974948: <Unloaded_mg.dll>+0xC974947
0:000> du 145740C
0145740c  "gdiplus.dll"


上面的调试告诉我们,一个Critical section在程序unloading gdiplus模块时没有释放。函数调用指令地址为0x0c974948,说明是模块dllistmt.dll启动了这个函数。但从那里启动的,我们看不出来。

 

寻找entire stack trace

给gdiplus!GdiplusStartup下断点,看看整个函数调用堆栈。

0:004> bu gdiplus!GdiplusStartup "kv100"
0:004> g
ChildEBP RetAddr  Args to Child
0012eb1c 0cdf4948 0ce15194 0012eb40 00000000 gdiplus!GdiplusStartup (FPO: [3,0,0])
WARNING: Stack unwind information not available. Following frames may be wrong.
00000000 00000000 00000000 00000000 00000000 dllistmt!DllUnregisterServer+0x109a8
eax=00000000 ebx=0cdb0000 ecx=0012eb40 edx=785bbad0 esi=0ce1580c edi=0ce037dc
eip=4aea75e9 esp=0012eb20 ebp=00000000 iopl=0         nv up ei pl nz ac pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000216
gdiplus!GdiplusStartup:
4aea75e9 8bff            mov     edi,edi


断点如期命中,但没看到期望中的完整函数堆栈。

去official的build package中找不到dllistmt模块的pdb文件。尝试恢复stack trace第一次失败。

找map文件,没有。尝试恢复stack trace第二次失败。

手动build一个新的dllistmt,同时获得pdb文件,替换到程序中,问题不能复现了。尝试恢复stack trace第三次失败。

 

虽然上面提示Following frames may be wrong,但我们看到ebp=0就闪出一个念头,是否是stack overflow导致EIP被改写,而使程序稀里糊涂执行了这条指令?

 

stack overflow?

看dllistmt模块的code,看到dllmain第一个函数调用是DisableThreadLibraryCalls,下断点再次运行。

0:004> bp kernel32!DisableThreadLibraryCalls "kv100"
0:004> g
ChildEBP RetAddr  Args to Child
0012eb24 0ce2487f 0cde0000 00000000 0012eb90 kernel32!DisableThreadLibraryCalls (FPO: [1,0,0])
WARNING: Stack unwind information not available. Following frames may be wrong.
0012eb90 0ce297a0 0cde0000 0012ebbc 7c92118a dllistmt!DllUnregisterServer+0x108df
0012eb9c 7c92118a 0cde0000 00000001 00000000 dllistmt!DllUnregisterServer+0x15800
0012ebbc 7c93d98a 0ce29782 0cde0000 00000001
4000
ntdll!LdrpCallInitRoutine+0x14
0012ecc4 7c935be3 00000000 c0150008 00000000 ntdll!LdrpRunInitializeRoutines+0x344 (FPO: [Non-Fpo])
0012ef70 7c935d45 00000000 012abc60 0012f264 ntdll!LdrpLoadDll+0x3e5 (FPO: [Non-Fpo])
0012f218 7c801bbd 012abc60 0012f264 0012f244 ntdll!LdrLoadDll+0x230 (FPO: [Non-Fpo])
0012f280 7c80aefc 048d4d88 00000000 00000000 kernel32!LoadLibraryExW+0x18e (FPO: [Non-Fpo])
0012f294 04244b93 048d4d88 96e16ac7 0012f328 kernel32!LoadLibraryW+0x11 (FPO: [1,0,0])
0012f2c4 042449e2 96e16af7 0012f328 00000000 dllms+0x4b93
0012f2f4 0427ae3b 96e16b43 043c9298 04286974 dllms+0x49e2
0012f340 0427a538 96e16b7f 04286974 026f0588 dllms!DllUnregisterServer+0x1874b
0012f37c 77105cd9 043c9298 0370a9c8 0012f5f0 dllms!DllUnregisterServer+0x17e48
0012f39c 0426bd97 0012f3ec 00000000 00000004 OLEAUT32!DispCallFunc+0x16a
0012f42c 0426af94 0427a4e0 00000000 0429a874 dllms!DllUnregisterServer+0x96a7
0012f514 314d9524 043c9380 00000001 30df3040 dllms!DllUnregisterServer+0x88a4
0012f558 3101cb81 00000001 0012f5d0 00000000 mso!Ordinal4725+0x1a0
0012f588 3104329c 30df2f60 3151d05d 00000001 mso!Ordinal1549+0x31504
0012f5f4 30d4f357 0012f660 00000000 01267b70 mso!Ordinal1549+0x57c1f
0012f648 30d347e2 00000000 0012f670 00000001 mso!Ordinal5959+0x3b7
0012f6ac 30d3fbf0 00000003 00000001 01266f78 mso!Ordinal3740+0xb82
0012f720 30d4c627 01266f78 00000003 00000088 mso!Ordinal4343+0xbe
0012f804 30d30285 00000003 006b0460 00000202 mso!Ordinal260+0x240
0012f838 7c92e473 0012f848 00000030 00030000 mso!Ordinal3687+0x15d
0012f874 77d193e9 43b4d6fc 0012f8ac 77d18734 ntdll!KiUserCallbackDispatcher+0x13 (FPO: [0,0,0])
0012f880 77d18734 006b0460 00000202 00000000 USER32!NtUserPeekMessage+0xc
0012f8ac 77d18816 30ce87f4 006b0460 00000202 USER32!InternalCallWinProc+0x28
0012f914 77d189cd 00000000 30ce87f4 006b0460 USER32!UserCallWinProcCheckWow+0x150 (FPO: [Non-Fpo])
0012f974 77d196c7 30a84f40 00000001 30a84f40 USER32!DispatchMessageWorker+0x306 (FPO: [Non-Fpo])
0012f984 300664c1 30a84f40 00000000 30065cc5 USER32!DispatchMessageA+0xf (FPO: [1,0,0])
0012f990 30065cc5 30a84f40 01cf0092 01cf0008 WINWORD+0x664c1
00000000 00000000 00000000 00000000 00000000 WINWORD+0x65cc5
eax=00000001 ebx=0cde0000 ecx=0ce4589c edx=00000000 esi=00000001 edi=00000000
eip=7c811336 esp=0012eb28 ebp=0012eb90 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
kernel32!DisableThreadLibraryCalls:
7c811336 8bff            mov     edi,edi
0:000> ub dllistmt!DllUnregisterServer+0x108df
dllistmt!DllUnregisterServer+0x108c8:
0ce24868 83f801          cmp     eax,1
0ce2486b 0f85e6000000    jne     dllistmt!DllUnregisterServer+0x109b7 (0ce24957)
0ce24871 53              push    ebx
0ce24872 8b5c241c        mov     ebx,dword ptr [esp+1Ch]
0ce24876 55              push    ebp
0ce24877 57              push    edi
0ce24878 53              push    ebx
0ce24879 ff1558f1e20c    call    dword ptr [dllistmt!DllUnregisterServer+0x1b1b8 (0ce2f158)]
0:000> ln poi(0ce2f158)
(7c811336)   kernel32!DisableThreadLibraryCalls   |  (7c81135b)   kernel32!LdrDisableThreadCalloutsForDll
Exact matches:
kernel32!DisableThreadLibraryCalls = <no type information>
0:000> u
dllistmt!DllUnregisterServer+0x108df:
0ce2487f 891d8056e40c    mov     dword ptr [dllistmt!DllUnregisterServer+0x316e0 (0ce45680)],ebx
0ce24885 e8c6010000      call    dllistmt!DllUnregisterServer+0x10ab0 (0ce24a50)
0ce2488a 33ed            xor     ebp,ebp
0ce2488c f6052458e40c01  test    byte ptr [dllistmt!DllUnregisterServer+0x31884 (0ce45824)],1
0ce24893 7539            jne     dllistmt!DllUnregisterServer+0x1092e (0ce248ce)
0ce24895 830d2458e40c01  or      dword ptr [dllistmt!DllUnregisterServer+0x31884 (0ce45824)],1
0ce2489c bfb428e30c      mov     edi,offset dllistmt!DllUnregisterServer+0x1e914 (0ce328b4)
0ce248a1 be1858e40c      mov     esi,offset dllistmt!DllUnregisterServer+0x31878 (0ce45818)
  

从上面的分析,我们可知dllistmt!DllUnregisterServer+0x108df这是在执行dllistmt的dllmain函数,但这个地方编译器生成了一个奇怪的指令,把ebp设为0了,这样在没有pdb的情况下stack就不能正确地恢复了(这里尝试了汇编代码的单步跟踪,最后是运行到了gdiplus!GdiplusStartup。不过,这个过程比较枯燥,这里就忽略了)。调试时发现,这个简单的动作加载了8~9 dll,还启动了几个线程,感觉里面的初始化动作很复杂。怀疑里面一个模拟COM进程内通讯的模块有问题,化时间看了部分code,做了些调试,也没有发现根本问题(dllistmt模块里,有一个地方调用GdiplusStartup,但这个button的action根本就不执行到这段code)。

 

第一次迷茫

走不下去了,困惑中想到GdiplusStartup是干什么的,为什么需要Critical section,而且一直不释放它?

上google找,打开URL http://msdn.microsoft.com/en-us/library/ms534077(v=vs.85).aspx获得如下信息。

The GdiplusStartup function initializes Windows GDI+. Call GdiplusStartup before making any other GDI+ calls, and call GdiplusShutdown when you have finished using GDI+.
Status GdiplusStartup(
__out  ULONG_PTR token *token,
__in   const GdiplusStartupInput *input,
__out  GdiplusStartupOutput *output
);
input [in]
GdiplusStartupInput
Pointer to a GdiplusStartupInput structure that contains the GDI+ version, a pointer to a debug callback function, a Boolean value that specifies whether to suppress the background thread, and a Boolean value that specifies whether to suppress external image codecs.


 原来初始化gdiplus需要调用GdiplusStartup,关闭gdiplus需要调用GdiplusShutdown。这个Critical section是在初始化的时候产生的,那可能是调用GdiplusShutdown时释放它,但这里却说没释放它,奇怪。对GdiplusShutdown下断点,再次调试,结果到异常发生了,这个断点也没有命中。

 

第二次迷茫

很是困惑,再次停下来分析。

1。action开始时,dllms模块动态加载dllistmt模块,dllistmt静态加载gdiplus模块。

2。action结束时,dllms模块动态unload dllistmt模块,因为这个时候gdiplus模块的引用计数为1,gdiplus这个时候也被自动unload出系统。

3。整个过程中,dllistmt没显示调用gdiplus的code,可gdiplus模块的初始化函数被调用了,对应的析构函数却没被调用,这导致6号线程发生了异常。等等,6号线程,难道就是上面说的gdiplus的后台线程?

 

大胆的假设

dllms在加载dllistmt模块时,一个我们现在还不清楚系统调用,激活了gdiplus,调用了GdiplusStartup。这个函数又创建了gdiplus的后台线程。当程序unload dllistmt模块,gdiplus模块也被unload,但没有机会调用GdiplusShutdown,所以gdiplus的后台线程还在,这样当系统切换到这个线程,因调用无效内存(gpiplus原来再系统中的代码段)指令而产生访问违例。这个分析跟当前的现象很吻合。

 

验证假设

 
0:004> bl
1 eu 0001 (0001) (gdiplus!GdiplusShutdown) "kb100"
2 eu 0001 (0001) (gdiplus!GdiplusStartup) "kb100"
0:004> g
ChildEBP RetAddr Args to Child
0012eb1c 0a394948 0a3b5194 0012eb40 00000000 gdiplus!GdiplusStartup
WARNING: Stack unwind information not available. Following frames may be wrong.
00000000 00000000 00000000 00000000 00000000 dllistmt!DllUnregisterServer+0x109a8
eax=00000000 ebx=0a350000 ecx=0012eb40 edx=785bbad0 esi=0a3b580c edi=0a3a37dc
eip=4aea75e9 esp=0012eb20 ebp=00000000 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216
gdiplus!GdiplusStartup:
4aea75e9 8bff mov edi,edi
0:000> ~0 bp kernel32!createthread "kb"
0:000> g
ChildEBP RetAddr Args to Child
0012eaa8 4aea7f66 00000000 00000000 4aea7456 kernel32!CreateThread
0012ead0 4aea95c8 37b340d1 4b0172bc 0012eb40 gdiplus!BackgroundThreadStartup+0x4e
0012eb04 4aea7633 0012eb40 0a3a37dc 0a3b580c gdiplus!InternalGdiplusStartup+0x56d
0012eb1c 0a394948 0a3b5194 0012eb40 00000000 gdiplus!GdiplusStartup+0x4b
WARNING: Stack unwind information not available. Following frames may be wrong.
00000000 00000000 00000000 00000000 00000000 dllistmt!DllUnregisterServer+0x109a8
eax=00000000 ebx=00000594 ecx=7ffde000 edx=4b0172a4 esi=4b0172a4 edi=00000000
eip=7c8106d7 esp=0012eaac ebp=0012eb04 iopl=0 nv up ei ng nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000296
kernel32!CreateThread:
7c8106d7 8bff mov edi,edi
0:000> dd 0012eaa8 L8
0012eaa8 00000001 4aea7f66 00000000 00000000
0012eab8 4aea7456 00000000 00000000 4b017048
0:000> ln 4aea7456
(4aea7456) gdiplus!BackgroundThreadProc | (4aea74f0) gdiplus!EpScanEngine::`vftable'
Exact matches:
gdiplus!BackgroundThreadProc = <no type information>
0:000> dd 4b017048 L1
4b017048 00000000
0:000> gu
eax=00000584 ebx=00000594 ecx=7c8106a3 edx=7c92e514 esi=4b0172a4 edi=00000000
eip=4aea7f66 esp=0012eac8 ebp=0012eb04 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
gdiplus!BackgroundThreadStartup+0x4e:
4aea7f66 3bc7 cmp eax,edi
0:000> dd 4b017048 L1
4b017048 0000063c
0:000> ~0 bp kernel32!freelibrary
0:000> g
Launching spec process to start at spec directory.
/help
Breakpoint 3 hit
eax=00000000 ebx=00000000 ecx=0012d824 edx=7c92e514 esi=77da0000 edi=00000001
eip=7c80ac7e esp=0012d834 ebp=0012e260 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
kernel32!FreeLibrary:
7c80ac7e 8bff mov edi,edi
0:000> ~
. 0 Id: bc0.cac Suspend: 1 Teb: 7ffde000 Unfrozen
1 Id: bc0.ed4 Suspend: 1 Teb: 7ffdd000 Unfrozen
2 Id: bc0.d80 Suspend: 1 Teb: 7ffdc000 Unfrozen
3 Id: bc0.960 Suspend: 1 Teb: 7ffdb000 Unfrozen
4 Id: bc0.e4 Suspend: 1 Teb: 7ffda000 Unfrozen
5 Id: bc0.63c Suspend: 1 Teb: 7ffd9000 Unfrozen
6 Id: bc0.d88 Suspend: 1 Teb: 7ffd8000 Unfrozen
0:000> ~5 s
eax=00000001 ebx=0ac9fe7c ecx=00000000 edx=00000002 esi=00000000 edi=7ffdf000
eip=7c92e514 esp=0ac9fe54 ebp=0ac9fef0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!KiFastSystemCallRet:
7c92e514 c3 ret
0:005> kb
ChildEBP RetAddr Args to Child
0ac9fe50 7c92df4a 7c809590 00000002 0ac9fe7c ntdll!KiFastSystemCallRet
0ac9fe54 7c809590 00000002 0ac9fe7c 00000001 ntdll!ZwWaitForMultipleObjects+0xc
0ac9fef0 77d195f9 00000002 0ac9ff18 00000000 kernel32!WaitForMultipleObjectsEx+0x12c
0ac9ff4c 77d196a8 00000001 0ac9ffac ffffffff USER32!RealMsgWaitForMultipleObjectsEx+0x13e
0ac9ff68 4aea74b2 00000001 0ac9ffac 00000000 USER32!MsgWaitForMultipleObjects+0x1f
0ac9ffb4 7c80b729 00000000 00000000 00000011 gdiplus!BackgroundThreadProc+0x59
0ac9ffec 00000000 4aea7456 00000000 00000000 kernel32!BaseThreadStart+0x37
0:005> ln 4aea74b2
(4aea7456) gdiplus!BackgroundThreadProc+0x59 | (4aea74f0) gdiplus!EpScanEngine::`vftable'
0:000> g
*** ERROR: Symbol file could not be found. Defaulted to export symbols for D:/Program Files/ProductPath/dllWebAPI.dll -
Unload module D:/Program Files/ProductPath/dllistmt.dll at 0a350000
Unload module D:/WINDOWS/WinSxS/x86_Microsoft.Windows.GdiPlus_6595b64144ccf1df_1.0.6002.22509_x-ww_c7dad023/gdiplus.dll at 4ae90000
Unload module D:/Program Files/ProductPath/dllWebAPI.dll at 0a7f0000
Unload module D:/Program Files/ProductPath/dllhttp.dll at 0a400000
Unload module D:/WINDOWS/system32/WININET.dll at 3e410000
Unload module D:/WINDOWS/system32/Normaliz.dll at 0a440000
Unload module D:/Program Files/ProductPath/dllocres.dll at 0a660000
ModLoad: 37320000 37341000 D:/PROGRA~1/COMMON~1/MICROS~1/SMARTT~1/FNAME.DLL
ModLoad: 74be0000 74c0c000 D:/WINDOWS/system32/OLEACC.dll
ModLoad: 75ff0000 76055000 D:/WINDOWS/system32/MSVCP60.dll
ModLoad: 374b0000 374b9000 D:/PROGRA~1/COMMON~1/MICROS~1/SMARTT~1/2052/stintl.dll
(bc0.498): Break instruction exception - code 80000003 (first chance)
eax=7ffdf000 ebx=00000001 ecx=00000002 edx=00000003 esi=00000004 edi=00000005
eip=7c92120e esp=01cfffcc ebp=01cffff4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000246
ntdll!DbgBreakPoint:
7c92120e cc int 3
Missing image name, possible paged-out or corrupt data.
Missing image name, possible paged-out or corrupt data.
Missing image name, possible paged-out or corrupt data.
0:002> ~5 s
eax=00000001 ebx=0ac9fe7c ecx=00000000 edx=7c92e514 esi=00000000 edi=7ffdf000
eip=7c92e514 esp=0ac9fe54 ebp=0ac9fef0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ntdll!KiFastSystemCallRet:
7c92e514 c3 ret
0:005> kb
ChildEBP RetAddr Args to Child
0ac9fe50 7c92df4a 7c809590 00000002 0ac9fe7c ntdll!KiFastSystemCallRet
0ac9fe54 7c809590 00000002 0ac9fe7c 00000001 ntdll!ZwWaitForMultipleObjects+0xc
0ac9fef0 77d195f9 00000002 0ac9ff18 00000000 kernel32!WaitForMultipleObjectsEx+0x12c
0ac9ff4c 77d196a8 00000001 0ac9ffac ffffffff USER32!RealMsgWaitForMultipleObjectsEx+0x13e
0ac9ff68 4aea74b2 00000001 0ac9ffac 00000000 USER32!MsgWaitForMultipleObjects+0x1f
WARNING: Frame IP not in any known module. Following frames may be wrong.
0ac9ffb4 7c80b729 00000000 00000000 00000011 0x4aea74b2
0ac9ffec 00000000 4aea7456 00000000 00000000 kernel32!BaseThreadStart+0x37
0:005> !address 0x4aea74b2
43e08000 : 43e08000 - 16fb8000
Type 00000000
Protect 00000001 PAGE_NOACCESS
State 00010000 MEM_FREE
Usage RegionUsageFree

0:005> g
(bc0.63c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000001 ebx=00000000 ecx=0ac9ff00 edx=7c92e514 esi=4b0172a4 edi=77d2a340
eip=4aea74b2 esp=0ac9ff84 ebp=0ac9ffb4 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
4aea74b2 ??              ???
0:005> kb
ChildEBP RetAddr  Args to Child
WARNING: Frame IP not in any known module. Following frames may be wrong.
0ac9ff80 00000000 00000011 00000000 00090858 0x4aea74b2
  

调试结果跟分析的一样,gdiplus启动了线程gdiplus!BackgroundThreadProc。这样当gdiplus模块unload出去后,这个线程在执行BackgroundThreadProc函数时就产生访问违例而crash了。

 

 

解决办法

由于不是太清楚具体的code逻辑,就建议维护code的人员在load dllistmt模块时,显式的调用GdiplusStartup,在unload时再显式的调用GdiplusShutdown来归避这个问题。

 

 

经验与教训

1。如果dllistmt模块有pdb文件,那就可以立刻定位哪里调用了GdiplusStartup,而发现问题的root cause。

2。如果一开始就熟悉gdiplus的调用规则,知道GdiplusStartup和GdiplusShutdown需要配对。同时GdiplusStartup可能会创建后台线程,那就不用等到google后,才对特殊的规则做相应的调试。丰富的背景知识是调试的基础啊。

3。其实在遇到critical section错误的时候,如果看一下所有的线程stack也可能早的想到这个假设,但当时好像注意这个东西。有时,成功看起来是离我们如此的接近。

 

 

 

 

 

 

 

 

 

 

 

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息