第七章,这一张我们了解ESP和EBP两个寄存器的差别,和通过OD查看程序是如何调用函数的(包括main函数和其他的函数)
首先,我们先给出结论,程序调用函数的过程:
1.执行函数1,将ESP保存着主函数1的栈区。然后将EBP压入栈,并将ESP赋值给EBP
2.然后esp开辟栈区,并将变量放入变量的值。
3.刚刚存放变量的栈区,将值传递给通用寄存器,然后call需要调用的函数2。
4.来到函数2,重复步骤1和2。
5.执行函数,将需要返回的值给EAX,将EBP的值给ESP,将EBP出栈,返回函数1.
6.然后再将ESP开辟的区域给关闭。
** 如果返回值是0,就会用异或指令将EAX清零(XOR EAX,EAX)
然后我们通过OD来查看一下
这里将ebp压入栈,并把esp的值给ebp,所以无论esp无论怎么变动,栈底的位置,永远都不会动。
00410680 /> 55 push ebp ; 将ebp压入栈
00410681 |. 8BEC mov ebp,esp ; esp赋值给ebp
2.esp开辟新的栈区,将参数的值放进栈区
00410683 |. 83EC 48 sub esp,0x48 ; 开辟新的栈区
00410686 |. 53 push ebx
00410687 |. 56 push esi
00410688 |. 57 push edi
00410689 |. 8D7D B8 lea edi,[local.18]
0041068C |. B9 12000000 mov ecx,0x12
00410691 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
00410696 |. F3:AB rep stos dword ptr es:[edi]
00410698 |. C745 FC 01000>mov [local.1],0x1 ; 1放入开辟的栈区
0041069F |. C745 F8 02000>mov [local.2],0x2 ; 2放入开辟的栈区
3.马上要调用函数了,先把需要传递的参数压入栈,然后call函数的地址。
004106A6 |. 8B45 F8 mov eax,[local.2] ; 将参数传给eax
004106A9 |. 50 push eax ; eax压入栈
004106AA |. 8B4D FC mov ecx,[local.1] ; 将参数传给ecx
004106AD |. 51 push ecx ; 将ecx压入栈
004106AE |. E8 5709FFFF call 1.0040100A ; 调用函数
4.重复步骤1和2
00401010 |> 55 push ebp ; 新的函数,同样是ebp压入栈
00401011 |. 8BEC mov ebp,esp ; 同样是esp赋值给ebp
00401013 |. 83EC 48 sub esp,0x48 ; 同样是开辟新的栈区
复制的过程离得太远了,就不复制下来了。
5.准备返回
00401037 |. 0345 F8 add eax,[local.2] ; 将返回值给eax
0040103A |. 5F pop edi ; 0
0040103B |. 5E pop esi ; 0
0040103C |. 5B pop ebx ; 0
0040103D |. 8BE5 mov esp,ebp ; ebp赋值给esp,准备返回
0040103F |. 5D pop ebp ; 删除栈帧
00401040 . C3 retn ; 返回
6.关闭栈区
004106B3 |. 83C4 08 add esp,0x8 ; 关闭开辟的栈区