首页 > 解决方案 > x86 解释,函数参数和局部变量的数量

问题描述

x86-64 系统的 C ABI 如下: 寄存器 rdi、rsi、rdx、rcx、r8、r9 用于按顺序传递参数。堆栈用于第 7 个参数。返回值使用 rax 寄存器。rsp 寄存器包含堆栈指针。

吹函数中定义了多少个函数参数bloop

我认为只有一个函数参数,rdi. 这个对吗?

在下面的函数中声明了多少个局部变量(不是参数)bloop

我认为没有局部变量。这个对吗?

0000000000001139 <bloop>:
    1139:       55                      push   %rbp
    113a:       48 89 e5                mov    %rsp,%rbp
    113d:       48 83 ec 10             sub    $0x10,%rsp
    1141:       48 89 7d f8             mov    %rdi,-0x8(%rbp)
    1145:       48 83 7d f8 29          cmpq   $0x29,-0x8(%rbp)
    114a:       7f 1b                   jg     1167 <bloop+0x2e>
    114c:       48 8b 05 dd 2e 00 00    mov    0x2edd(%rip),%rax
    1153:       48 89 c6                mov    %rax,%rsi
    1156:       48 8d 3d b5 0e 00 00    lea    0xeb5(%rip),%rdi
    115d:       b8 00 00 00 00          mov    $0x0,%eax
    1162:       e8 c9 fe ff ff          callq  1030 <printf@plt>
    1167:       90                      nop
    1168:       c9                      leaveq
    1169:       c3                      retq

标签: assemblyx86-64reverse-engineering

解决方案


由于这个 asm 显然是反优化调试模式(默认-O0优化级别)的编译器输出,因此您可以假设所有寄存器 args 在函数入口时都溢出到堆栈中。为什么clang用-O0产生效率低下的asm(对于这个简单的浮点和)?

所以是的,这使逆向工程变得微不足道,并排除了任何未使用的函数 args 或 args 在它们到达的同一个寄存器中传递给 printf。

指令的杂散nop和使用leave意味着这可能是 GCC 输出,而不是 clang 或 ICC。仅与排除const int foo = 0x29;GCC 不会优化的可能性或某事真正相关-O0ICC 和 clang 为让 GCC 生成这个 asm 的源代码生成不同的 asm。我没有检查每个编译器版本,只是检查了这些编译器的最新版本。

(此外,这看起来像是对 PIE 可执行文件或共享库的反汇编。在传统的位置相关的 ELF 可执行文件中,左侧的地址列将具有更高的地址,并且编译器会习惯mov $imm32, %edi将静态地址放入寄存器中。)


所以是的,有一个 64 位整数/指针 arg(它当然到达 RDI),并且对 printf 的调用传递了加载的全局或静态 64 位变量的值mov 0x2edd(%rip), %rsi,以及全局/静态格式的地址将字符串放入 LEA 的寄存器中。

是的,除非他们完全未使用,否则我看不到当地人。在-O0,gcc 将优化int unused;但不会int foo = 123;。有任何本地人,甚至register const compare = 0x29;会得到 GCCsubq $24, %rsp而不是 16 (0x10)。(请参阅下面的 Godbolt 链接。)它实际上不会进行持续传播。


我可以让 GCC9.3 -O0 从此源代码中准确生成这个 asm:

#include <stdio.h>
long global_var;

void bloop(long x) {
    if (!(x>0x29))
        printf("%ld", global_var);
}

在使用 gcc9.3 的 Godbolt 上-O0 -fpie -fverbose-asm

# godbolt strips out directives like .section .rodata
.LC0:
        .string "%ld"

bloop:
        pushq   %rbp  #
        movq    %rsp, %rbp      #,
        subq    $16, %rsp       #,
        movq    %rdi, -8(%rbp)  # x, x
        cmpq    $41, -8(%rbp)   #, x
        jg      .L3 #,
        movq    global_var(%rip), %rax  # global_var, global_var.0_1
        movq    %rax, %rsi      # global_var.0_1,
        leaq    .LC0(%rip), %rdi        #,
        movl    $0, %eax        #,
        call    printf@PLT      #
.L3:
        nop     
        leave   
        ret

nop没有目的;我不知道为什么未优化的 GCC 输出有时会有一个。

另请参阅如何从 GCC/clang 程序集输出中删除“噪音”?有关查看编译器输出的更多信息。


推荐阅读