首先寄存器使用惯例:
eip :指令地址寄存器,保存程序计数器的值,当前执行的指令的下一条指令的地址值,16位中为ip,32位为eip。eip不可以直接赋值,一般都是cpu自动加1来更新,指令call和ret以及jmp可以改变eip的值。
另外汇编代码格式有ATT和intel格式,gcc和objdump的默认格式就是ATT。几个小区别,1首先是指令ATT汇编指令后面有一个l,比如intel格式为mov,ATT格式为movl
2寄存器,ATT格式有%,比如intel格式为ebp,ATT格式为%ebp
3还有一个最主要的区别就是操作指令的,操作数的顺序是相反的,源操作数和目的操作数的顺序相反。
关于栈中的寄存器:
esp :栈指针,又叫栈顶寄存器,总是指向栈顶元素,已经压入栈的最顶上的那个元素,而不是待压入的。栈指针可以移动,通过上下移动实现栈的开辟和释放。栈是向小地址方向生长的。esp寄存器中保存的是当前栈的栈顶元素的地址。
ebp :帧指针,又叫栈基址寄存器,总是指向当前栈的栈底元素。保存的是当前栈的栈底元素的地址。帧指针不可以移动,用来作为当前栈的基址,通过对基址ebp的偏移来寻址访问栈中的其他元素的值。比如:0x8(%ebp)ebp所指向的地址值在加上0x8的地址。
ebp永远都是针对当前栈的,所以当一个函数调用了另外一个函数的时候,就需要先将调用函数的栈的ebp给入栈保存,这样避免了与被调用函数的栈ebp冲突,然后在被调用函数的栈结束调用释放的时候,恢复现场的时候,在将调用函数的ebp弹出来,这样又可以回到调用函数的栈了。
栈通过栈指针栈顶esp和帧指针栈底ebp来固定栈框。
eax :保存函数的返回值的惯用寄存器
% :直接寻址寄存器
( ) :内存间接寻址
$ :立即数
例如:movl $8, %eax #把立即数8存到寄存器eax中
movl $8, (%esp) #把立即数8存到内存esp所指的内存地址中。
这里主要来通过函数调用来彻底弄清楚栈的过程,这里直接利用bufbomb中的一段简单的c代码,通过反汇编来分析一下其汇编代码。这个c代码很简单,就是test函数中调用了getbuf函数。
其反汇编代码为:
这里的汇编代码比较长我们来截取一下只看关于栈和函数调用的部分: