打印
[疑难问答]

函数调用过程中 栈和寄存器的变化过程

[复制链接]
1084|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主

探究以下程序在函数调用过程中   栈顶EBP  栈底ESP  的变化过程

int Plus(int x,int y){        int z=2;        return x+y+z;}int main(int argc, char* argv[]){        int r=Plus(3,4);        return 0;}

反汇编后代码是这样的

1.     00C113EE    6A 04           PUSH 4                         //参数3压栈               2.     00C113F0    6A 03           PUSH 3                         //参数4压栈3.     00C113F2    E8 A9FCFFFF     CALL 00C110A0                  //调用函数4.     00C113F7    83C4 08         ADD ESP,8                      //平栈

其中第3步函数调用内部汇编如下

1.                00C113EE    6A 04           PUSH 42.                00C113F0    6A 03           PUSH 33.                00C113F2    E8 A9FCFFFF     CALL 00C110A0                   //调用函数 指令跳转----3.1                00C110A0    E9 DB020000     JMP 00C11380                    //跳转到函数地址准备调用3.2                00C11380    55              PUSH EBP                        //为了还原现状,保存当前栈底3.3                00C11381    8BEC            MOV EBP,ESP                     //开辟新的函数栈,栈底提升到原栈顶的位置3.4                00C11383    81EC CC000000   SUB ESP,0CC                     //开辟新的函数栈,栈顶向上偏移一段距离,这段距离就是缓冲区3.5                00C11389    53              PUSH EBX                        //为了还原现状,保存当前EBX3.6                00C1138A    56              PUSH ESI                        //为了还原现状,保存当前ESI3.7                00C1138B    57              PUSH EDI                        //为了还原现状,保存当前EDI3.8                00C1138C    8DBD 34FFFFFF   LEA EDI,[EBP-0CC]               //循环填充缓冲区,设置填充的起始地址3.9                00C11392    B9 33000000     MOV ECX,33                      //循环填充缓冲区,设置填充次数3.10        00C11397    B8 CCCCCCCC     MOV EAX,CCCCCCCC                //循环填充缓冲区,设置填充内容3.11        00C1139C    F3:AB           REP STOS DWORD PTR ES:[EDI]     //循环填充缓冲区,开始填充3.12        00C1139E    C745 F8 0200000 MOV DWORD PTR SS:[EBP-8],2      //将变量2写入栈中3.13        00C113A5    8B45 08         MOV EAX,DWORD PTR SS:[EBP+8]    //计算相加,结果保存到EAX中3.14        00C113A8    0345 0C         ADD EAX,DWORD PTR SS:[EBP+0C]   //计算相加,结果保存到EAX中3.15        00C113AB    0345 F8         ADD EAX,DWORD PTR SS:[EBP-8]    //计算相加,结果保存到EAX中3.16        00C113AE    5F              POP EDI                         //还原EDI3.17        00C113AF    5E              POP ESI                         //还原ESI3.18        00C113B0    5B              POP EBX                         //还原EBX3.19        00C113B1    8BE5            MOV ESP,EBP                     //消除新栈,还原栈况,新栈底为原栈顶3.20        00C113B3    5D              POP EBP                         //消除新栈,还原栈况,原栈底之前已经保存在栈中,POP即可还原3.21        00C113B4    C3              RETN                            //指令跳转到函数调用处----4.                00C113F7    83C4 08         ADD ESP,8                       //平栈5.                00C113FA    8945 F8         MOV DWORD PTR SS:[EBP-8],EAX

程序未运行时 EBP    ESP    EIP 如下


0.堆栈初始状态是这样的

1.    PUSH 4
    当第一个参数压栈时,栈底EBP不变,栈顶ESP上移一个字节,即ESP=ESP-4,指令EIP跳转到下一条指令,因为PUSH 4 这条指令是两个字节 所以EIP指令下移两个字节,EIP=EIP+2
2.    PUSH 3
    当第二个参数压栈时,栈底EBP不变,栈顶ESP上移一个字节,即ESP=ESP-4,指令EIP跳转到下一条指令
3.    CALL 00C110A0  =   PUSH EIP + jmp
    当调用函数Plus时,先将EIP的下一条指令(00C113F7)压栈保存  为的是当被调用函数结束时程序可以回来继续运行 ,栈底不变,因为有指令被压栈,所以栈顶上移

3.1    JMP 00C11380
    继续跟进程序 发现00C110A0 会 JMP 00C11380 此时 EIP指向 00C11380  因为是jmp 所以堆栈没有影响

3.2    PUSH EBP   
    因为CALL了一个函数,所以会生成一个新的函数栈,此时需要先将原来的栈底保存起来,为了函数调用结束之后可以还原当前的栈况,不保存会发生栈底丢失。 push操作导致栈顶上移 ESP=ESP-4

3.3    MOV EBP,ESP
3.4    SUB ESP,0CC
    生成新栈
    因为CALL了一个函数,所以会生成一个新的函数栈,在原栈顶头上开辟一个新栈,原来的栈顶当作新栈的栈底,原来的栈顶上移0CC个字节 ESP=ESP-0CC,上移的空间就是缓冲区

3.5    PUSH EBX
    保存现场,为了函数调用结束后 可以还原寄存器当前的状态,所以先将各个寄存器的状态压栈保存

3.6    PUSH ESI

3.7    PUSH EDI
3.8    LEA EDI,[EBP-0CC]
3.9    MOV ECX,33
3.10    MOV EAX,CCCCCCCC
3.11    REP STOS DWORD PTR ES:[EDI]

循环填充缓冲区

LEA EDI,[EBP-0CC] 表示将  EBP-0CC 这个地址 送入EDI  EDI影响填充位置,这里是为了设置填充的起始地址 , 即从缓冲区的头部开始填充
MOV ECX,33 给ECX赋值为33  ECX影响了循环次数
MOV EAX,CCCCCCCC  将EAX赋值CCCCCCCC  EAX影响填充的内容
REP STOS DWORD PTR ES:[EDI]   用EAX的值循环33次 填充EDI地址中的内容

3.12    MOV DWORD PTR SS:[EBP-8],2
    对应函数中的一个变量  int z=2   在EBP-8的位置写入2


3.13    MOV EAX,DWORD PTR SS:[EBP+8]
3.14    ADD EAX,DWORD PTR SS:[EBP+0C]
3.15    ADD EAX,DWORD PTR SS:[EBP-8]
    计算2+3+4  结果放入EAX

3.16    POP EDI
    还原寄存器的状态    3.5      3.6      3.7保存的 现在用到了

3.17    POP ESI

3.18    POP EBX
    注意 此时虽然POP了 只是给寄存器赋值 栈顶跟随改变,但是内存中的数据还是存在的 并没有随着栈顶的降低而消除

3.19    MOV ESP,EBP
    消除新栈,修改栈顶
3.20    POP EBP
    消除新栈,修改栈底。EBP之前已经保存到栈中,直接POP即可还原



3.21    RETN= POP EIP

4.    ADD ESP,8
    外平栈,由于此时多PUSH了3和4  为了栈平衡 在函数外部ESP+8  使得栈可以还原到最初状态  

至此 函数调用前和函数调用后 栈空间是完全一样的

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

35

主题

1557

帖子

0

粉丝