基本寄存器和反汇编知识
EBP: 扩展基址指针寄存器(extended base pointer) 其内存放一个指针,该指针指向系统栈最上面一个栈帧的底部。
ESP: 栈顶指针,用于指向栈的栈顶(下一个压入栈的活动记录的顶部),而EBP为帧指针,指向当前活动记录的底部。
EIP: 指令寄存器,存放当前指令的下一条指令的地址。CPU该执行哪条指令就是通过IP来指示的。
具体代码:
反汇编代码:
#include "stdafx.h"
int sum(int x, int y)
{
01271000 push ebp
01271001 mov ebp,esp
01271003 push ecx
//__asm mov ebp, 0
int* p= NULL;
01271004 mov dword ptr [p],0
x = *p;
0127100B mov eax,dword ptr [p]
0127100E mov ecx,dword ptr [eax]
01271010 mov dword ptr [x],ecx
return (x + y);
01271013 mov eax,dword ptr [x]
01271016 add eax,dword ptr [y]
}
01271019 mov esp,ebp
0127101B pop ebp
0127101C ret
--- 无源文件-----------------------------------------------------------------------
0127101D int 3
0127101E int 3
0127101F int 3
--- c:\users\lifangzheng\documents\visual studio 2008\projects\crashtest\crashtest\crashtest.cpp
int sumstub(int x, int y)
{
01271020 push ebp
01271021 mov ebp,esp
01271023 push ecx
int tmp = 0;
01271024 mov dword ptr [tmp],0
printf("enter fun() ...\n");
0127102B push offset ___xi_z+34h (12720F4h)
01271030 call dword ptr [__imp__printf (12720A0h)]
01271036 add esp,4
tmp = sum(x, y);
01271039 mov eax,dword ptr [y]
0127103C push eax
0127103D mov ecx,dword ptr [x]
01271040 push ecx
01271041 call sum (1271000h)
01271046 add esp,8
01271049 mov dword ptr [tmp],eax
printf("leave fun() ...\n");
0127104C push offset ___xi_z+48h (1272108h)
01271051 call dword ptr [__imp__printf (12720A0h)]
01271057 add esp,4
return tmp;
0127105A mov eax,dword ptr [tmp]
}
0127105D mov esp,ebp
0127105F pop ebp
01271060 ret
--- 无源文件-----------------------------------------------------------------------
……
--- c:\users\lifangzheng\documents\visual studio 2008\projects\crashtest\crashtest\crashtest.cpp
int _tmain(int argc, _TCHAR* argv[])
{
01271070 push ebp
01271071 mov ebp,esp
printf("enter main() ...\n");
01271073 push offset ___xi_z+5Ch (127211Ch)
01271078 call dword ptr [__imp__printf (12720A0h)]
0127107E add esp,4
printf("sum = %d\n", sumstub(0x1234, 0x5678));
01271081 push 5678h
01271086 push 1234h
0127108B call sumstub (1271020h)
01271090 add esp,8
01271093 push eax
01271094 push offset ___xi_z+70h (1272130h)
01271099 call dword ptr [__imp__printf (12720A0h)]
0127109F add esp,8
printf("leave main() ...\n");
012710A2 push offset ___xi_z+7Ch (127213Ch)
012710A7 call dword ptr [__imp__printf (12720A0h)]
012710AD add esp,4
return 0;
012710B0 xor eax,eax
}
012710B2 pop ebp
012710B3 ret
Dump实例分析
1.通过编译器环境或者其他手段得到Dump日志。
2.输入 !analyze –v:自动分析命令
3.堆栈片段分析:
. 0(线程序号) Id: 1744(线程ID).fd4 Suspend: 0 Teb: 7ffdf000 Unfrozen
ChildEBP RetAddr Args to Child
002ef814 01271046 00001234 00005678 00000000 CrashTest!sum+0xe
002ef828 01271090 00001234 00005678 002ef87c CrashTest!sumstub+0x26
002ef838 0127121d 00000001 003f1ad8 003f1bb0 CrashTest!wmain+0x20
002ef87c 76caee1c 7ffd4000 002ef8c8 77c037eb CrashTest!__tmainCRTStartup+0x10f
WARNING: Stack unwind information not available. Following frames may be wrong.
002ef888 77c037eb 7ffd4000 72a4cec4 00000000 kernel32!BaseThreadInitThunk+0x12
002ef8c8 77c037be 01271365 7ffd4000 00000000 ntdll!RtlInitializeExceptionChain+0xef
002ef8e0 00000000 01271365 7ffd4000 00000000 ntdll!RtlInitializeExceptionChain+0xc2
几个名词:
ChildEBP: a pointer to a memory location which stores the address of the previous function on the stack (“stack frame”).(ebp的值:堆栈基地址)
RetAddr: The “return address” where processing will resume once this function returns (finishes what it had to do).(函数执行完以后返回的:计算机指令地址)
*当前寄存器状态:*
0:000> r
eax=00000000 ebx=00000000 ecx=00001234 edx=77be70f4 esi=00000001 edi=0127337c
eip=0127100e esp=002ef810 ebp=002ef814 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
CrashTest!sum+0xe:
0127100e 8b08 mov ecx,dword ptr [eax] ds:0023:00000000=????????
崩溃原因:eax为0,所以读取空指针指向内存里的值出现崩溃。
0127100e 是计算机指令地址。
绝大多数dump都可以通过!analyze -v指令得到崩溃的最直接原因。
另外一种特殊情况
在做组件的过程中可能会遇到,上层应用调用组件功能,并且设置了完备的异常处理机制,比如SEH,但是不幸的是,组件此时发生崩溃,虽然由dump输出,!analyze确发现不了崩溃的原因。
这种情况下的堆栈常如下所示:
0:000> ~*kv //显示出所有线程,找到对应的函数标识
. 0 Id: 12a0.44c Suspend: 0 Teb: 7ffdf000 Unfrozen
ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
001df590 7762be2e 001df544 001df5b8 00000000 ntdll!KiFastSystemCallRet
001df5d8 7762be9c 00000002 7ffd5000 00000000 kernel32!WaitForMultipleObjectsEx+0x8e
001df5f4 776406b7 00000002 001df628 00000000 kernel32!WaitForMultipleObjects+0x18
001df660 77640952 001df740 00000001 00000001 kernel32!GetThreadSelectorEntry+0x1ee
001df674 77640900 001df740 00000001 001df710 kernel32!UnhandledExceptionFilter+0x249
001df684 7764087b 001df740 00000001 6c699e83 kernel32!UnhandledExceptionFilter+0x1f7
001df710 77da7f62 00000000 77d4e31c 00000000 kernel32!UnhandledExceptionFilter+0x172
001dffc0 77d937be 011d107d 7ffd5000 00000000 ntdll!EtwReplyNotification+0x366
001dffd8 00000000 011d107d 7ffd5000 00000000 ntdll!RtlInitializeExceptionChain+0xc2
1) Kernel32! UnhandledExceptionFilter第一个参数为 EXCEPTION_POINTERS结构
2)再根据 KiUserExceptionDispatcher函数的原型得到本次异常发生时保存的 CONTEXT 结构信息。(windows 系统底层具体处理异常的函数)
VOID KiUserExceptionDispatcher (
IN PEXCEPTION_RECORD ExceptionRecord,
IN PCONTEXT ContextRecord
)
3)通过命令/显示EXCEPTION_POINTERS数据结构里各个变量值
0:000> dd 001df740 L2
001df740 001df840 001df85c
4)调用cxr命令解析崩溃瞬间的堆栈
0:000> .cxr 001df85c
eax=00000000 ebx=7ffd5000 ecx=00000000 edx=0fb91408 esi=001dfd68 edi=001dfd58
eip=011d13e8 esp=001dfc80 ebp=001dfd58 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010212
CrashTest!sum+0x28:
011d13e8 8b08 mov ecx,dword ptr [eax] ds:0023:00000000=????????