参数传递和返回值如何在程序集级别的 x86 上的 C/C++ 中工作?

How does argument passing and returning values work in C/C++ on x86 at the assembly level?

本文关键字:上的 x86 C++ 工作 返回值 程序集 参数传递      更新时间:2023-10-16

我试图找出在 C/C++ 中调用函数时如何将参数传递给函数以及如何在程序集级别返回函数的返回值。 我找到了这些答案:

程序集 x86 - 调用 C 函数

参数传递如何工作?

它表示堆栈用于在 C/C++ 中将参数传入和传出函数。 然而,当我编写一个简单的C++测试程序并在 radare2 中对其进行反汇编时,它似乎没有使用堆栈将参数传递给函数。 相反,参数在函数调用之前放在esiedi中。

虽然这个网站上的答案会更直接地有帮助,但一个指向我可以了解更多信息的文档链接将不胜感激,即使适当的文档可能非常技术性,以至于超出了我的头脑。

测试C++程序:

void foo(int a, int b) {
return;
}
int main() {
int a=5;
foo(5,a);
return 0;
}

从雷达2拆卸的组件:

┌ (fcn) main 37
│   int main (int argc, char **argv, char **envp);
│           ; var int32_t var_4h @ rbp-0x4
│           ; DATA XREF from entry0 @ 0x50d
│           0x00000607      55             push rbp
│           0x00000608      4889e5         mov rbp, rsp
│           0x0000060b      4883ec10       sub rsp, 0x10
│           0x0000060f      c745fc050000.  mov dword [var_4h], 5
│           0x00000616      8b45fc         mov eax, dword [var_4h]
│           0x00000619      89c6           mov esi, eax
│           0x0000061b      bf05000000     mov edi, 5
│           0x00000620      e8d5ffffff     call sym foo(int, int)      ; sym.foo_int__int
│           0x00000625      b800000000     mov eax, 0
│           0x0000062a      c9             leave
└           0x0000062b      c3             ret

引发这个问题的是以下我正在尝试解决的初学者破解程序的反汇编代码。

我不是在寻求帮助解决这个难题,只是帮助理解在下面的示例中如何传递函数参数以及将来可以去哪里查找。

下面这个来自crackme的例子显示了被调用的sym.imp.puts(以下两个例子是手工打字的,所以它们可能包含错误,尽管我确实尝试过校对(:

; CODE XREF from main @ 0x11f8
; 0x36915
; "Wrong key!"
lea rdi, str.Wrong_key
; int puts(const char *s)
call sym.imp.puts;[oo]

puts似乎有地址str.Wrong_keyrdi传递给它。

另一方面,这个代码片段:

lea rax, [var_6ch]
mov rsi, rax
; const char *format
; "%d"
lea rdi, [0x000368ff]
mov eax, 0
; int scanf(const char *format)
call sym.imp.__isoc99_scanf;[ob]
mov eax, dword [var_6ch]
cmp eax, 1
je 0x1228

我无法理解此代码片段中发生了什么。 在此之前不使用var_6ch。 大概scanf是这样称呼的:scanf("%d", var_6ch);但我看不出var_6ch%d字符串是如何传递给scanf的。

前面的所有代码示例似乎都没有使用堆栈来传递参数,因此非常感谢任何和所有帮助。

在x86-64(这是你所在的体系结构(上,System V ABI 调用约定是最常用的,它定义了以下用于函数参数的寄存器(按声明顺序(:RDI、RSI、RDX、RCX、R8、R9、XMM0 到 XMM07。返回寄存器是 RAX。

另一方面,在x86 32位上,由于寄存器较少,因此通常在堆栈上传递参数。

当然,调用约定不仅定义了如何传递参数,要了解更多信息,您可以查看维基百科页面。

你在这段代码中看到的正是这样的:

lea rax, [var_6ch]               ; get the address of some variable
mov rsi, rax                     ; rsi = second parameter
; loads the variable's address into rsi
lea rdi, [0x000368ff]            ; rdi = first parameter
; loads the address of the format string into rdi
mov eax, 0                       ; clear eax
call sym.imp.__isoc99_scanf;[ob] ; call scanf(rdi, rsi)
mov eax, dword [var_6ch]
cmp eax, 1              
je 0x1228