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
由于这显然是在调试模式下编译的,因此可以假定所有寄存器args在函数入口溢出到堆栈中。 (Why does clang produce inefficient asm with -O0 (for this simple floating point sum)?)
所以是的,这简化了逆向工程,并排除了所有未使用的函数args或args传递到它们到达的同一寄存器中的printf。
所以是的,有一个64位整数/指针arg(当然是到达RDI的),并且对printf的调用传递了装载有mov 0x2edd(%rip), %rsi
的全局或静态64位变量的值以及全局/静态格式的字符串放入LEA的寄存器中。
是的,我看不到任何本地语言,除非将它们声明为register const
或使编译器对其进行优化的东西。 (例如$0x29
常量可以保存在某种命名的C变量中。)
(这看起来像是对PIE可执行文件或共享库的反汇编。在传统的与位置相关的ELF可执行文件中,左侧的地址列将具有较高的地址,并且编译器将使用mov $imm32, %edi
将静态地址放入注册。)
mov
和nop
都是指令。指令是处理器执行的东西,是构成机器程序的东西。如果您不熟悉此概念,则阅读有关汇编编程的教程可能会有所帮助。
函数使用什么指令在很大程度上与它具有多少个参数和局部变量无关。 nop
和某些mov
指令的存在不会告诉您有关函数的参数和变量的任何信息。
告诉您的是这些指令具有的操作数。如果您不熟悉什么是操作数或x86指令如何使用它们的操作数,我必须再次请您参考教程,因为这超出了此问题的范围。
识别函数参数的一般方法是检查函数使用的调用方保存寄存器,而无需事先为其分配值。尽管这不是万无一失的方法,但通常是最好的启发式方法。
在您的函数中,使用保存呼叫者的寄存器rdi
,rsi
和rax
。其中,仅rdi
的原始值会对功能产生影响。至于rsi
和rax
,该函数将覆盖其原始值而无需查看。因此,这些不太可能是函数参数(rax
从未在SysV调用约定中用作函数参数)。因此,该函数可能在rdi
中只有一个参数。我看不到调用者分配的堆栈插槽的任何访问权限,因此不太可能将任何变量隐藏在那里。
可能仍然是该函数编写为在rsi
或某些其他寄存器中具有参数,而这些参数只是未使用。如果没有其他信息(例如,调试符号,呼叫站点的拆卸等),我们将永远无法确定。
关于局部变量:由于C编译器可以将局部变量优化到无法识别的程度,因此通常无法重建C函数在编译为汇编时使用的局部变量。它还可以出于各种目的添加其他局部变量。
但是,在您的特定情况下,可能是在关闭优化的情况下编译了该函数。在这种情况下,许多C编译器以非常直接和可预测的方式编译C代码,其中为每个局部变量分配了一个堆栈插槽,并且对局部变量的每次内存访问都会对该堆栈插槽产生一个加载或存储。
然而,仍然无法绝对确定地说明这些变量可能具有什么类型,或者如果两个相邻的堆栈插槽是两个单独的变量,则是一个特别大的变量(例如long double
)或一个结构变量,或者数组类型。我们再也不会知道。
在您的示例中,指令sub $0x10, %rsp
分配了两个8个字节的堆栈插槽。由于编译器必须以16字节为增量分配堆栈插槽以进行对齐,因此这意味着原始函数至少具有一个变量(64位类型),但可能有多达9个变量(其他变量为char
类型)。 。
由于只有一个堆栈插槽(-0x8(%rbp)
)最终被访问,我们只能确定地说该函数至少具有一个变量。由于访问以64位宽进行,因此该变量可能具有64位宽的类型。该函数可能具有额外的未使用的局部变量,或者该变量可能是具有多个成员或数组的结构,每个成员只能访问第一个成员。我们不能肯定地说。