相關知識:x86架構、Compiler、Windbg指令
今天遇到一個值得分享的問題,先看一下Crash發生時候的Stack:
1: 2: STACK_TEXT: 3: WARNING: Frame IP not in any known module. Following frames may be wrong.
4: f7a22c60 f7944029 0000ffff f7a22c7c f794407a 0x0 5: f7a22c6c f794407a 0000ffff bb40e64e f7a22d4c BasicDriver!CheckParameterAndCallHelperFunction+0x19 [d:\XXX\basicdriver.c @ 25] 6: f7a22c7c 805777ff 824e5cc0 82328000 00000000 BasicDriver!DriverEntry+0x1a [d:\XXX\basicdriver.c @ 36] 7: f7a22d4c 8057790f 800009a8 00000001 00000000 nt!IopLoadDriver+0x66d 8: f7a22d74 80535c12 800009a8 00000000 827ba388 nt!IopLoadUnloadDriver+0x45 9: f7a22dac 805c71ec f7a76cf4 00000000 00000000 nt!ExpWorkerThread+0x100 10: f7a22ddc 80542de2 80535b12 00000001 00000000 nt!PspSystemThreadStartup+0x34 11: 00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16請看奇怪的第四行,Symbol Path都設定了,怎麼會有0x0出現呢?再來看看當時的暫存器的值:
1: kd> .cxr 0xfffffffff7a22898 2: eax=0000ffff ebx=00000000 ecx=bb40e64e edx=8054be20 esi=e1f8b780 edi=824e5cc0 3: eip=00000000 esp=f7a22c64 ebp=f7a22c6c iopl=0 nv up ei pl nz na pe nc 4: cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00210206 5: 00000000 ?? ???第三行的eip竟然顯示為0x00000000,難道今天忘記放乖乖在電腦上,所以硬體產生問題嗎?來檢查Crash發生時的x86指令(!analyze -v):
1: FOLLOWUP_IP: 2: BasicDriver!CheckParameterAndCallHelperFunction+19 [d:\XXX\basicdriver.c @ 25] 3: f7944029 5d pop ebp 4: 5: FAILED_INSTRUCTION_ADDRESS: 6: +689e952f01edd960 7: 00000000 ?? ???由第7行可知是位於0x00000000的無意義資料,可見得機器的確是因為執行了EIP=0x00000000這個地方的東西而掛掉。第2、3行給了我們一些線索,呼叫0x00000000的犯人位於0xf7944029附近,讓我們來看一下CheckParameterAndCallHelperFunction所包含的指令:
1: kd> uf BasicDriver!CheckParameterAndCallHelperFunction 2: BasicDriver!CheckParameterAndCallHelperFunction [d:\XXX\basicdriver.c @ 17]: 3: 17 f7944010 8bff mov edi,edi 4: 17 f7944012 55 push ebp 5: 17 f7944013 8bec mov ebp,esp 6: 19 f7944015 837d0800 cmp dword ptr [ebp+8],0 7: 19 f7944019 7d04 jge BasicDriver!CheckParameterAndCallHelperFunction+0xf (f794401f) 8: 21 f794401b 32c0 xor al,al 9: 21 f794401d eb0a jmp BasicDriver!CheckParameterAndCallHelperFunction+0x19 (f7944029) 10: 24 f794401f 8b4508 mov eax,dword ptr [ebp+8] 11: 24 f7944022 50 push eax 12: 24 f7944023 ff15186094f7 call dword ptr [BasicDriver!LittleHelper+0x8 (f7946018)] 13: 25 f7944029 5d pop ebp 14: 25 f794402a c20400 ret 4 有人說每寫一行組語,就會嚇跑一半的讀者
我們只要簡單看幾個地方,首先是找到第13行的0xf7944029,跟!analyze -v說的一樣,是pop ebp。前一個指令,也就是第12行的地方會呼叫存放於f7946018的函式,讓我們來檢查一下:
1: kd> dd f7946018 L1 2: f7946018 00000000真是乾淨的清潔溜溜。原來錯誤是發生在程式使用了一個沒有初始化的函式指標(function pointer),而call指令會把新指令的位置設定給eip,所以就造成了eip=0x00000000的現象摟!
BOOLEAN CheckParameterAndCallHelperFunction(int iValue){ if (iValue <0) return FALSE; return (fpHelperFunction)(iValue); }
另外,Compiler可能會把function開頭的push ebp以及尾端的三個指令最佳化:
1: ...... 2: 17 f7944012 55 push ebp 3: ...... 4: 24 f7944023 ff15186094f7 call dword ptr [BasicDriver!LittleHelper+0x8 (f7946018)] 5: 25 f7944029 5d pop ebp 6: 25 f794402a c20400 ret 4 7: 最佳化,以jmp取代上述四個指令: 8: jmp [f7946018]也就是說,速度上可以節省掉push/pop ebp兩個指令。以jmp取代call的好處是jmp不會退回return address,那執行完會到哪邊呢?會回到上一次用call的地方,也就是本例中的BasicDriver!DriverEntry。這也是一個加速(反正都在尾巴了,乾脆直接的返回更上一層的function)這樣最佳化對程式很好,但是分析dump的時候負擔也就會增加了。
No comments:
Post a Comment