通过x86汇编再看内存中的函数调用过程

以前通过C语言的学习大概了解了一些函数调用的过程。即在进程的栈中存在栈指针和基指针两种指针,一直指向栈顶,而基指针则指向当前函数的返回值。通过改变栈指针和基指针来实现函数的调用和返回。自从接触汇编后才对这块儿有了更深的理解。

在x86平台的汇编中栈涉及到三个寄存器:
SS(段寄存器)
SP(栈指针)
BP(基指针)
SS寄存器一直指向栈段地址,SP指向栈段偏移地址,因为汇编中地址的表示方式是"XXXX:XXXX"(不过居然不存在栈底这个概念)。

当一个函数在执行的过程中需要调用另一个函数时则会使用call指令,call指令执行时会先将IP寄存器(代码段偏移地址)的值push入栈中然后再调用jmp指令进行跳转,跳转到调用函数的地址,然后开始执行调用的函数的代码。

在执行完成后再使用ret指令进行返回,ret指令会将栈顶元素pop到IP寄存器中从而继续执行之前函数的代码。

那么举个例子,先上C语言测试代码:

int cz(int a, int b) {
    return a + b;
}

int main() {
    int c = cz(7, 8);
    return 0;
}

内容很简单,先定义一个cz函数,将两个变量a和b相加并返回。之后在main函数里进行调用。编译成可执行文件后再用反汇编工具看到汇编代码如下:

        ; ================ B E G I N N I N G   O F   P R O C E D U R E ================

        ; Variables:
        ;    var_4: int32_t, -4
        ;    var_8: int32_t, -8


                     _cz:;cz函数入口
0000000100000f60         push       rbp    ;将BP指针推入栈,BP此时的内容是cz函数的返回栈地址
0000000100000f61         mov        rbp, rsp
0000000100000f64         mov        dword [rbp+var_4], edi;下面几步执行a和b的相加
0000000100000f67         mov        dword [rbp+var_8], esi
0000000100000f6a         mov        esi, dword [rbp+var_4];由于栈是向下生长,所以地址要做减法
0000000100000f6d         add        esi, dword [rbp+var_8]
0000000100000f70         mov        eax, esi
0000000100000f72         pop        rbp
0000000100000f73         ret;函数返回并更新IP指针让main函数继续执行
                        ; endp
0000000100000f74         align      128


        ; ================ B E G I N N I N G   O F   P R O C E D U R E ================

        ; Variables:
        ;    var_4: int32_t, -4
        ;    var_8: int32_t, -8


                     _main:;mian函数入口
0000000100000f80         push       rbp
0000000100000f81         mov        rbp, rsp
0000000100000f84         sub        rsp, 0x10
0000000100000f88         mov        edi, 0x7                                    ; 参数a
0000000100000f8d         mov        esi, 0x8                                    ; 参数b
0000000100000f92         mov        dword [rbp+var_4], 0x0
0000000100000f99         call       _cz                                     ;执行cz函数
0000000100000f9e         xor        esi, esi
0000000100000fa0         mov        dword [rbp+var_8], eax
0000000100000fa3         mov        eax, esi
0000000100000fa5         add        rsp, 0x10
0000000100000fa9         pop        rbp
0000000100000faa         ret
                        ; endp
0000000100000fab         db  0x90 ; '.'

关键的地方加了注释,由于我的电脑是64位机,所以寄存器前面加了“r”表示取64位的值。

PS:当时在索尼现场搞bug的时候经常会看到的PC指针应该就是高通平台的代码段片偏移地址,当时看着底层的各种crash栈信息真是一脸懵逼。

你可能感兴趣的:(通过x86汇编再看内存中的函数调用过程)