了解eax, esp, ebp的作用(x86上是exx, x64上是rxx)
1. eax -- 数据寄存器,用来存储/读取数据,做累加的操作
2. esp 指针寄存器- 堆栈指针,里面寸的都是指针,需要调用dd来查看内容
3. ebp 指针寄存器 - 基址寄存器,每段函数进入后都会有如下指令集:
00414330 push ebp <-- 存当前函数上层函数的基指针
00414331 mov ebp,esp <-- 存当前的堆栈指针, 传入的参数都都在这个esp里,由于下面还会有push操作,所以把esp存入ebp中,ebp成为当前函数的基指针,非常重要,此时ebp以上,为传入的参数,以下为临时变量的指针
00414333 sub esp,0D8h <--开辟一段内存,用于储存临时/局部变量
00414339 push ebx <--几个寄存器值先入站
0041433A push esi
0041433B push edi
0041433C lea edi,[ebp-0D8h] <--循环
00414342 mov ecx,36h
00414347 mov eax,0CCCCCCCCh <--debug版本将栈上所有的数据置为该值,用于检查当前变量是否初始化
首先调用kn来查看当前call stack和frame
0:000> kn
*** Stack trace for last set context - .thread/.cxr resets it
# ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0012fc3c 102afdd2 kernel32!RaiseException+0x52
01 0012fc7c 00414385 msvcr90d!CxxThrowException+0x52
02 0012fd70 004143f0 ConsoleCpp1!Test22+0x55 [f:\codes\cpp\consolecpp1\consolecpp1\consolecpp1.cpp @ 669] <--准备查看此stack上传入的参数以及临时变量
03 0012fe58 004144ee ConsoleCpp1!Test11+0x30 [f:\codes\cpp\consolecpp1\consolecpp1\consolecpp1.cpp @ 674]
04 0012ff68 00415268 ConsoleCpp1!wmain+0xae [f:\codes\cpp\consolecpp1\consolecpp1\consolecpp1.cpp @ 684]
05 0012ffb8 004150af ConsoleCpp1!__tmainCRTStartup+0x1a8 [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 583]
06 0012ffc0 7c81776f ConsoleCpp1!wmainCRTStartup+0xf [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 403]
07 0012fff0 00000000 kernel32!RegisterWaitForInputIdle+0x49
使用.frame /r #来查看frame上的register的情况
0:000> .frame /r 2
02 0012fd70 004143f0 ConsoleCpp1!Test22+0x55 [f:\codes\cpp\consolecpp1\consolecpp1\consolecpp1.cpp @ 669]
eax=0012fbec ebx=7ffd6000 ecx=00000000 edx=00000003 esi=0012fc7c edi=0012fc7c
eip=00414385 esp=0012fc84 ebp=0012fd70iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
ConsoleCpp1!Test22+0x55:
00414385 8b45f8 mov eax,dword ptr [ebp-8] ss:0023:0012fd68=00008de6
查看当前ebp的值,来查找传入的参数args->arg1和args->arg2,还有results(此处我也不是很确定,传说参数是从ebp+8开始是第一个参数,因为ebp+4存的是返回地址,ebp存的是上层ebp的值),此处查询得之两个参数为0x6554, 0x2892, 注意这两个数都是16进制
0:000> dd 0012fd70 + 8
0012fd78 00006554 00002892 0012ff5c 0012fe64
0012fd88 7ffd6000 cccccccc cccccccc cccccccc
0012fd98 cccccccc cccccccc cccccccc cccccccc
0012fda8 cccccccc cccccccc cccccccc cccccccc
0012fdb8 cccccccc cccccccc cccccccc cccccccc
0012fdc8 cccccccc cccccccc cccccccc cccccccc
0012fdd8 cccccccc cccccccc cccccccc cccccccc
0012fde8 cccccccc cccccccc cccccccc cccccccc
接下来寻找当前frame上的局部变量result,此处不知为何多出一个值为cccccccc的临时变量,查到第二个局部变量的时候才找到results的值0x6554 + 0x2892 = 0x8de6
0:000> dd 0012fd70 -4
0012fd6c cccccccc 0012fe58 004143f0 00006554
0012fd7c 00002892 0012ff5c 0012fe64 7ffd6000
0012fd8c cccccccc cccccccc cccccccc cccccccc
0012fd9c cccccccc cccccccc cccccccc cccccccc
0012fdac cccccccc cccccccc cccccccc cccccccc
0012fdbc cccccccc cccccccc cccccccc cccccccc
0012fdcc cccccccc cccccccc cccccccc cccccccc
0012fddc cccccccc cccccccc cccccccc cccccccc
0:000> dd 0012fd70 - 8
0012fd68 00008de6 cccccccc 0012fe58 004143f0
0012fd78 00006554 00002892 0012ff5c 0012fe64
0012fd88 7ffd6000 cccccccc cccccccc cccccccc
0012fd98 cccccccc cccccccc cccccccc cccccccc
0012fda8 cccccccc cccccccc cccccccc cccccccc
0012fdb8 cccccccc cccccccc cccccccc cccccccc
0012fdc8 cccccccc cccccccc cccccccc cccccccc
0012fdd8 cccccccc cccccccc cccccccc cccccccc
从此处dump文件的寄存器使用情况可以得知,vs2008编译器的局部变量存储位置就是ebp-8, ebp - 14, ebp-20以此类推,每次用8 byte的数据,这是dbg版本为了在变量前后都加上前后标志用以检查越界,这就可以解释为什么ebp-4是cccccccc了
CrashCPP!Test:
012b1c80 55 push ebp
012b1c81 8bec mov ebp,esp
012b1c83 81ece4000000 sub esp,0E4h
012b1c89 53 push ebx
012b1c8a 56 push esi
012b1c8b 57 push edi
012b1c8c 8dbd1cffffff lea edi,[ebp-0E4h]
012b1c92 b939000000 mov ecx,39h
012b1c97 b8cccccccc mov eax,0CCCCCCCCh
012b1c9c f3ab rep stos dword ptr es:[edi]
012b1c9e 8b4508 mov eax,dword ptr [ebp+8]
012b1ca1 8b08 mov ecx,dword ptr [eax]
012b1ca3 894df8 mov dword ptr [ebp-8],ecx
012b1ca6 8b4508 mov eax,dword ptr [ebp+8]
012b1ca9 8b4804 mov ecx,dword ptr [eax+4]
012b1cac 894dec mov dword ptr [ebp-14h],ecx
012b1caf 8b45f8 mov eax,dword ptr [ebp-8]
012b1cb2 0345ec add eax,dword ptr [ebp-14h]
012b1cb5 8945e0 mov dword ptr [ebp-20h],eax
012b1cb8 837df800 cmp dword ptr [ebp-8],0
012b1cbc 752b jne CrashCPP!Test+0x69 (012b1ce9)
012b1cbe 8bf4 mov esi,esp
012b1cc0 ff15d4922b01 call dword ptr [CrashCPP!_imp__getchar (012b92d4)]
012b1cc6 3bf4 cmp esi,esp
012b1cc8 e88cf4ffff call CrashCPP!ILT+340(__RTC_CheckEsp) (012b1159)
012b1ccd 8bf4 mov esi,esp
012b1ccf 6a00 push 0
012b1cd1 6a00 push 0
012b1cd3 6a00 push 0
012b1cd5 6803000080 push 80000003h
012b1cda ff15fc912b01 call dword ptr [CrashCPP!_imp__RaiseException (012b91fc)]
012b1ce0 3bf4 cmp esi,esp
到此,本轮的debug完成,下面是完整的c++代码和汇编代码,另外附上资料原文和连接
/*
* main
*/
class MyArgs
{
public:
int arg1;
int arg2;
MyArgs()
{
srand(time(NULL));
arg1 = rand();
arg2 = rand();
}
};
int Test22(int a, int b)
{
int result = a+b;
if(result%2==0)
{
throw 1;
}
return result;
}
void Test11(MyArgs* arg)
{
int result = Test22(arg->arg1, arg->arg2);
printf("Result: %d", result);
}
int _tmain(int argc, _TCHAR* argv[])
{
getchar();
MyArgs* arg = new MyArgs();
Test11(arg);
return 0;
}
int Test22(int a, int b)
{
00414330 push ebp
00414331 mov ebp,esp
00414333 sub esp,0D8h
00414339 push ebx
0041433A push esi
0041433B push edi
0041433C lea edi,[ebp-0D8h]
00414342 mov ecx,36h
00414347 mov eax,0CCCCCCCCh
0041434C rep stos dword ptr es:[edi]