今天我们说一说,CALL就是调用函数,CALL的流程是怎么样的,它是怎么执行的,从哪里来?又将回哪里去?
我用易语言,写了一个函数,反汇编后很简单的一段代码
00400050 call 00490050
00400055 mov ecx,eax
00490050 PUSH EBP
00490051 MOV EBP,ESP
00490053 MOV EAX,00000001
00490058 MOV ESP,EBP
0049005A POP EBP
0049005B RET
00490050 到0049005B 这一段就是一个函数,非常简单,什么都没做,直接返回一个常数1
计算机是怎么执行的呢?
首先 从00400050开始,call 00490050这一句
call 00490050
等于
push 00400055
jmp 00490050
假如执行CALL 00490050这一句之前 ESP=00120000
我们假设00120000这时候的内存是这样的
0011FFEA 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00120000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
我们现在其实下面就是要执行 push 00400055
jmp 00490050
执行push 00400055这行汇编指令 其实是CPU经过了2个步骤 ,1,sub esp,4
2,mov [esp],00400055
我们来看下ESP位置 esp=0011FFFC
0011FFEA 00 00 00 00 00 00 00 00 00 00 00 00 55 00 40 00
00120000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
然后执行下一条指令,jmp 00490050 这一条esp没变化,跳到00490050处来执行
00490050 PUSH EBP
**首先00490050处又是一个push,执行完毕后
esp=11FFF8
0011FFEA 00 00 00 00 00 00 00 00 ebp放在这里 55 00 40 00
00120000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
现在注意看一下 [esp]里现在存放是进CALL之前ebp ,而[esp+4]存放的就是CALL指令后面的地址
**下面执行00490051 mov ebp,esp
这个时候 ebp=esp=11FFF8,那么我们知道 [ebp]=进CALL前的ebp [ebp+4]=返回地址,如果是复杂点带参数的函数,你会发现
[ebp+8]是参数一,[ebp+c]是参数二等等
**下面执行mov eax,00000001.这句没什么好解释的,一般返回值放在eax中,这个就是把返回值赋值为1
**下面执行mov esp,ebp
这个时候esp=ebp=11FFF8
**下面执行pop ebp ,这句等价于 mov ebp,[esp]
add esp,4
执行完毕后
我们来看[esp]里存放是进CALL前的ebp,那么现在ebp=进CALL前的ebp
然后现在 esp加了个4后 又等于了0011FFFC 我们来看看
0011FFEA 00 00 00 00 00 00 00 00 ebp放在这里 55 00 40 00
00120000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
从0011FFFC看起就是
0011FFFC 50 00 40 00 00 00 00 00 00 00 00 00 00 00 00
**下面执行ret ,ret这句等价于 jmp [esp]
add esp,04
这里[esp]=00400055
那么就是 jmp 00400055
add esp,04
esp=0011FFFC+4以后就是00120000
JMP 00400050
就跳到了CALL下一行指令处,并且ESP这里跟进CALL前是一样的了