|
| HOME > ソフテックだより > 第67号(2008年6月4日発行) 技術レポート「Windowsネイティブプログラムのエラー解析」 |
|
| ソフテックだより 第67号(2008年6月4日発行) 技術レポート 「Windowsネイティブプログラムのエラー解析」 |
Windowsプログラム(C/C++)の厄介な問題のひとつに、アプリケーションエラーがあります。
最近では.NET Framework上で動作するマネージドプログラム(※1)のプログラムが増えてきているため、アプリケーションエラーが発生する可能性は低いと思いますが、まだまだ現役のネイティブプログラム(※2)は多いため、避けては通れない問題です。
そんな中、先日、私にアプリケーションエラーの調査依頼があり、担当することになりました。しかし、数年振りにアプリケーションエラーの解析をしてみると忘れていることばかりで、いろいろ思い出しながらの作業でした。今回はその時にどのような手順で進めていったかをまとめてみたいと思います。
解析には、いろいろな前準備も必要ですので、以下の順で説明したいと思います。
1.〜2.までは解析を進める上で必要な前準備となり、3.〜5.までが実際のアプリケーションエラー発生〜解析となります。
また、今回使用するプログラムのソースは以下のものを用います。
|
#include "stdafx.h" int FuncA(char* buf1, char* buf2, int size) { buf1 += 100000000; memcpy(buf1, buf2, size); return 1; } int _tmain(int argc, _TCHAR* argv[]) { char x[10]; char* y;
int z = FuncA(x, y, 10); return 0; |
今回対象とするプログラムは、VC++(Visual Studio C++ 2005)で作成したWin32コンソールアプリケーションです。exe名はCrashとしています。
ソースを見れば、この時点でもうおかしなプログラムであることは明らかですが、今回はこのソースを使っていきます。
※また、分かりやすさのため、コンパイルの最適化は無効としています。
![]() |
|---|
![]() |
|---|
![]() |
|---|
![]() |
|---|
|
アプリケーション例外が発生しました: アプリケーション: C:\Crash\release\Crash.exe (pid=4976) 発生時間: 2008/06/02 @ 18:10:58.437 例外番号: c0000005 (アクセス違反)
*----> システム情報 <----* コンピュータ名: **** ユーザー名: **** ターミナル セッション Id: 0 プロセッサの数: 2 プロセッサの種類: x86 Family 6 Model 15 Stepping 2 Windows バージョン: 5.1 現在のビルド: 2600 Service Pack: 2 現在のタイプ: Multiprocessor Free 登録されている会社名: SOFTECH 登録されている所有者: **** ・・・・・・・・・・・・・・・以下省略・・・・・・・・・・・・・・・・ |
|
ファンクション: MSVCR80!memcpy 7814514c 89448fe4 mov [edi+ecx*4-0x1c],eax 78145150 8b448ee8 mov eax,[esi+ecx*4-0x18] 78145154 89448fe8 mov [edi+ecx*4-0x18],eax 78145158 8b448eec mov eax,[esi+ecx*4-0x14] 7814515c 89448fec mov [edi+ecx*4-0x14],eax 78145160 8b448ef0 mov eax,[esi+ecx*4-0x10] 78145164 89448ff0 mov [edi+ecx*4-0x10],eax 78145168 8b448ef4 mov eax,[esi+ecx*4-0xc] 7814516c 89448ff4 mov [edi+ecx*4-0xc],eax 78145170 8b448ef8 mov eax,[esi+ecx*4-0x8] フォールト ->78145174 89448ff8 mov [edi+ecx*4-0x8],eax ds:0023:0608e068=???????? 78145178 8b448efc mov eax,[esi+ecx*4-0x4] 7814517c 89448ffc mov [edi+ecx*4-0x4],eax 78145180 8d048d00000000 lea eax,[00000000+ecx*4] 78145187 03f0 add esi,eax 78145189 03f8 add edi,eax 7814518b ff249594511478 jmp dword ptr [MSVCR80!memcpy+0x174 (78145194)+edx*4] 78145192 8bff mov edi,edi 78145194 a4 movsb 78145195 51 push ecx 78145196 1478 adc al,0x78 |
|
*----> スタック バック トレース <----* WARNING: Stack unwind information not available. Following frames may be wrong. *** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\WINDOWS\system32\kernel32.dll - ChildEBP RetAddr Args to Child 0012ff3c 004017ff 0608e068 0040302c 0000000a MSVCR80!memcpy+0x154 0012ff50 0040182f 0608e068 0040302c 0000000a Crash!FuncA+0x1f 0012ff7c 00401170 00000001 003a3f48 003a4b00 Crash!wmain+0x1f 0012ffc0 7c816fd7 0012e0dc 7c94e1fe 7ffdf000 Crash!__tmainCRTStartup+0x10f 0012fff0 00000000 004012b9 00000000 78746341 kernel32!RegisterWaitForInputIdle+0x49 *----> ロウ スタック ダンプ <----* 000000000012ff34 01 00 00 00 78 33 40 00 - 50 ff 12 00 ff 17 40 00 ....x3@.P.....@. 000000000012ff44 68 e0 08 06 2c 30 40 00 - 0a 00 00 00 7c ff 12 00 h...,0@.....|... 000000000012ff54 2f 18 40 00 68 e0 08 06 - 2c 30 40 00 0a 00 00 00 /.@.h...,0@..... 000000000012ff64 4c 10 40 00 18 30 40 00 - 20 30 40 00 1c 30 40 00 L.@..0@. 0@..0@. 000000000012ff74 14 20 3e 62 2c 30 40 00 - c0 ff 12 00 70 11 40 00 . >b,0@.....p.@. |
|
0001:000007e0 ?FuncA@@YAHPAD0H@Z 004017e0 f Crash.obj 0001:00000810 _wmain 00401810 f Crash.obj 0002:00000000 __imp__GetCurrentProcessId@ 0 00402000 kernel32:KERNEL32.dll |
「0001f 83 c4 0c add esp, 12 ; 0000000cH」
というコードがあるので、ここを指していることになります。|
0012ff3c 50 ff 12 00:ChildEBP。memcpyが呼び出されベースポインタを退避。 0012ff4c ff 17 40 00:RetAddr。memcpyはcallされリターンアドレスを格納。 0012ff44 68 e0 08 06:memcpy引数1 0012ff48 2c 30 40 00:memcpy引数2 0012ff4c 0a 00 00 00:memcpy引数3 0012ff50 7c ff 12 00:ChildEBP。FuncAが呼び出されベースポインタを退避。 0012ff54 2f 18 40 00:RetAddr。FuncAはcallされリターンアドレスが格納。 0012ff58 68 e0 08 06:FuncA引数buf1 0012ff5c 2c 30 40 00:FuncA引数buf2 0012ff60 0a 00 00 00:FuncA引数size 0012ff64 4c 10 40 00:z 0012ff68 18 30 40 00 20 30 40 00 1c 30 40 00:x サイズは10だがアライメント分+2 0012ff74 14 20 3e 62:ArrayPad 0012ff78 2c 30 40 00:y |
|
0003:00000000 ___security_cookie 00403000 MSVCRT:gs_cookie.obj 0003:00000004 ___security_cookie_complement 00403004 MSVCRT:gs_cookie.obj 0003:00000008 ___native_dllmain_reason 00403008 MSVCRT:natstart.obj 0003:0000000c ___native_vcclrit_reason 0040300c MSVCRT:natstart.obj 0003:00000010 ___globallocalestatus 00403010 MSVCRT:xthdloc.obj 0003:00000014 ___defaultmatherr 00403014 MSVCRT:merr.obj 0003:0000035c __forceCRTManifest 0040335c MSVCRT:crtmanifest.obj |
|
EXTRN @__security_check_cookie@4:PROC EXTRN _memcpy:PROC PUBLIC ?FuncA@@YAHPAD0H@Z ; FuncA ; Function compile flags: /Odtp ; File C:\crash\crash\crash.cpp _TEXT SEGMENT _buf1$ = 8 ; size = 4 _buf2$ = 12 ; size = 4 _size$ = 16 ; size = 4 ?FuncA@@YAHPAD0H@Z PROC ; FuncA ; 8 : { ; ベースポインタ退避して、; スタックポインタをベースポインタへ設定 00000 55 push ebp 00001 8b ec mov ebp, esp ; 9 : buf1 += 100000000; ; eax レジスタを使ってbuf1へ100000000加算 00003 8b 45 08 mov eax, DWORD PTR _buf1$[ebp] 00006 05 00 e1 f5 05 add eax, 100000000 ; 05f5e100H 0000b 89 45 08 mov DWORD PTR _buf1$[ebp], eax ; 10 : memcpy(buf1, buf2, size); ; 引数をスタックへ積み上げ。(_cdecl呼出しなのでsize,buf2,buf1の順で積み上げ) 0000e 8b 4d 10 mov ecx, DWORD PTR _size$[ebp] 00011 51 push ecx 00012 8b 55 0c mov edx, DWORD PTR _buf2$[ebp] 00015 52 push edx 00016 8b 45 08 mov eax, DWORD PTR _buf1$[ebp] 00019 50 push eax ; _memcpyをコールし、スタックポインタを引数分12(size=4,buf2=4,buf1=4)戻す。 0001a e8 00 00 00 00 call _memcpy 0001f 83 c4 0c add esp, 12 ; 0000000cH ; 11 : return 1; ; 戻り値はeaxレジスタで返す 00022 b8 01 00 00 00 mov eax, 1 ; 12 : } |
|
PUBLIC __$ArrayPad$ PUBLIC _wmain EXTRN ___security_cookie:DWORD ; Function compile flags: /Odtp _TEXT SEGMENT _z$ = -24 ; size = 4 _x$ = -20 ; size = 10 __$ArrayPad$ = -8 ; size = 4 _y$ = -4 ; size = 4 _argc$ = 8 ; size = 4 _argv$ = 12 ; size = 4 _wmain PROC ; 16 : { ; ベースポインタ退避して、; スタックポインタをベースポインタへ設定 00030 55 push ebp 00031 8b ec mov ebp, esp ; スタックポインタをローカル変数分24(z=4, x=10+2, ArrayPad=4, y=4)バイト進ませる。 00033 83 ec 18 sub esp, 24 ; 00000018H ; スタックポインタ保持(スタックバッファオーバー検出用) 00036 a1 00 00 00 00 mov eax, DWORD PTR ___security_cookie 0003b 33 c5 xor eax, ebp 0003d 89 45 f8 mov DWORD PTR __$ArrayPad$[ebp], eax ; 17 : char x[10]; ; 18 : char* y; ; 19 : ; 20 : int z = FuncA(x, y, 10); ; 引数をスタックへ積み上げ。_cdecl呼出しなので(size,buf2,buf1の順で積み上げ) 00040 6a 0a push 10 ; 0000000aH 00042 8b 45 fc mov eax, DWORD PTR _y$[ebp] 00045 50 push eax 00046 8d 4d ec lea ecx, DWORD PTR _x$[ebp] 00049 51 push ecx ;
0004a e8 00 00 00 00 call ?FuncA@@YAHPAD0H@Z ; FuncA 0004f 83 c4 0c add esp, 12 ; 0000000cH 00052 89 45 e8 mov DWORD PTR _z$[ebp], eax ; 21 : return 0; ; 戻り値を0で返す。 00055 33 c0 xor eax, eax ; 22 : } ; スタックポインタを最初に保持していたものと現在のもので比較(スタックバッファオーバー検出)し、ベースポインタを戻す 00057 8b 4d f8 mov ecx, DWORD PTR __$ArrayPad$[ebp] 0005a 33 cd xor ecx, ebp 0005c e8 00 00 00 00 call @__security_check_cookie@4 00061 8b e5 mov esp, ebp 00063 5d pop ebp 00064 c3 ret 0 _wmain ENDP _TEXT ENDS END |
![]() |
|---|
|
*----> スレッド Id 0xd3c の状態のダンプ <----* eax=00000020 ebx=069ea510 ecx=7ff97000 edx=00000020 esi=00000000 edi=069ea510 eip=7c941010 esp=0d53fa0c ebp=0d53fa50 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 ファンクション: ntdll!RtlEnterCriticalSection 7c940ffc 0000 add [eax],al 7c940ffe 0000 add [eax],al 7c941000 90 nop 7c941001 90 nop 7c941002 90 nop 7c941003 90 nop 7c941004 90 nop ntdll!RtlEnterCriticalSection: 7c941005 648b0d18000000 mov ecx,fs:[00000018] 7c94100c 8b542404 mov edx,[esp+0x4] フォールト ->7c941010 837a1400 cmp dword ptr [edx+0x14],0x0 ds:0023:00000034=???????? 7c941014 754f jnz ntdll!RtlEnterCriticalSection+0x60 (7c941065) 7c941016 f0ff4204 lock inc dword ptr [edx+0x4] 7c94101a 7519 jnz ntdll!RtlEnterCriticalSection+0x30 (7c941035) 7c94101c 8b4124 mov eax,[ecx+0x24] 7c94101f 89420c mov [edx+0xc],eax 7c941022 c7420801000000 mov dword ptr [edx+0x8],0x1 7c941029 33c0 xor eax,eax 7c94102b c20400 ret 0x4 7c94102e 8da42400000000 lea esp,[esp] 7c941035 8b4124 mov eax,[ecx+0x24] *----> スタック バック トレース <----* WARNING: Stack unwind information not available. Following frames may be wrong. ChildEBP RetAddr Args to Child 0d53fa50 7c1cd34b 069ea510 00000081 00000000 ntdll!RtlEnterCriticalSection+0xb 0d53fefc 7c150ff8 005468cd 00000000 069dc958 MFC71!Ordinal5346+0x33 0d53ff80 7c349565 08def69c 00000000 00000000 MFC71!Ordinal4590+0xd1 0d53ffb4 7c80b683 06a04f70 00000000 00000000 MSVCR71!endthreadex+0xa0 0d53ffec 00000000 7c3494f6 06a04f70 00000000 kernel32!GetModuleFileNameA+0x1b4 |
| [参考文献] | ||
| 日経BPソフトプレス発行 インサイドWindows上・下[第4版] 日経BPソフトプレス発行 .NET&Windowsプログラマのためのデバッグテクニック徹底解説 |
||
| [注釈] | ||
| (※1) | 共通ランタイム環境CLR(CommonLanguageRuntime)の管理下で実行されるプログラム。C#.NETやVB.NETプログラムで、メモリ管理等が強化されている。 | |
| (※2) | CLR上で動作しない従来のCやC++プログラム |
(H.K.)
![]() TEL:03-5368-1641 |
Copyright(C) 1997-2012 SOFTECH CO.,LTD. |