使用windbg分析CPU、内存、崩溃、调试等,难免会遇到汇编代码,尤其堆栈信息。
汇编函数结构
一个函数的汇编结构如下:
push ebp ; 保存上个栈底ebp
mov esp,ebp ;设置新栈底
...
...
ret ; 返回。
举例:
int bar(int c,int d)
{
地址 指令码 汇编
8048394: 55 push ebp
8048395: 89 e5 mov ebp,esp
8048397: 83 ec 10 sub $0x10,esp
int e=c+d; // C/C++代码
804839a: 8b 45 0c mov 0xc(ebp),eax
804839d: 8b 55 08 mov 0x8(ebp),edx
80483a0: 8d 04 02 lea (edx,eax,1),eax
80483a3: 89 45 fc mov eax,-0x4(ebp)
return e; // C/C++代码
80483a6: 8b 45 fc mov -0x4(ebp),eax
}
80483a9: c9 leave
80483aa: c3 ret
注意,指令码和汇编一一对应。
除了汇编、C/C++程序编译的二进制文件是CPU识别的指令码。
其它C#、JAVA编译后的二进制文件,都是自身语言的解释性指令,运行过程,还需要解析成CPU识别的指令码,才能被执行。
堆栈的帧信息
每个函数占用的栈空间为栈帧。堆栈的增长方向和栈中存的数据内容。
其中,
ebp:32位栈底寄存器,16位为bp.
esp:32位栈顶寄存器,16位为sp.
例如如下C++代码:
int add(int x2, int x1) {
int z;
z = x + y;
return z;
}
int main() {
add(0, 1)
return 0;
}
当正执行add函数时,堆栈信息如下:
高地址
x1
x2
main的add(0.1)下一个指令地址
main的栈基址。
z
低地址
注意VS默认参数入栈顺序是从左到右,所以x1先于x2参数入栈。
函数调用信息
如前面所述,堆栈中可以跟踪函数调用过程的帧。
意味着从堆栈可以得到如下信息:
函数调用顺序。
上个函数地址/或当前帧的函数地址。
函数参数地址。
函数局部变量地址。
工具windbg的命令kb看到的堆栈,就是这些信息。