开始学习汇编,虽然以前上学时学过,但很多年不用,早就忘了。
主要分两种汇编,Intel格式的和AT&T格式的。前者windows上用,后者在Unix/LInux上用。 不过还是有工具可以转换的。
网上介绍AT&T的很多,与Intel格式的区别主要就是指令加长度表示,操作数顺序相反,[]变(),寄存器加%,立即数加$,寻址偏移格式等。
看汇编,以下是x86,intel格式,vc,release
int func(int x,int y){return x+y;}
00401000 mov eax,[esp+08] ;我在想为什么不用pop,后来理解原来esp处是上级函数的返回地址(因call调用),所以只能用内存寻址
00401004 mov ecx,[esp+04] ;一样
00401008 add eax,ecx ;x+y,结果在eax中
0040100A ret
0040100B nop
void main(){
printf("%d/n",func(1,2));
...
00401010 push 02 ;压参,从右边到左
00401012 push 01 ;
00401014 call 00401000 ;调用func,结果在eax中
00401019 add esp,08 ;恢复sp位置,因为func是c-call不会恢复main的参数堆栈位置。根据我的实验stdcall是不需要这句的,因为stdcall由被调用参数恢复压栈位置的。但c-call和stdcall参数入栈顺序都是从右向左。
0040101C push eax ;printf的参数
当debug版和在linux上,函数内一般会先建栈框架,就是将当前esp赋给ebp做为当前函数的栈基地址,这样能够在退出函数时恢复栈地址。
push ebp ;开头:将上级函数的栈基地址保存
mov ebp,esp ;将当前栈位置做为当前函数的栈基地址
。。。。。
。。。。。 ;其它处理,如定义临时变量需要栈,调用其它函数时要压参数栈
。。。。。
mov esp,ebp ;结尾:恢复到开头的栈地址(保证了中间过程栈移动不匹配)
pop ebp ;取出保存的上级函数的栈基