在汇编级别的 x86 上,参数传递和返回值如何在 C/C++ 中工作? [英] How does argument passing and returning values work in C/C++ on x86 at the assembly level?

查看:25
本文介绍了在汇编级别的 x86 上,参数传递和返回值如何在 C/C++ 中工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to find out how, when calling a function in C/C++ the arguments to the function are passed and how the return value from the function is given back at the assembly level. I found these answers:

汇编 x86 - 调用 C 函数

参数传递是如何工作的?

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

which say that the stack is used to pass arguments to and from functions in C/C++. However when I wrote a simple C++ test program and disassembled it in radare2, it did not appear to be using the stack to pass arguments to the function. Instead, the arguments were put in esi and edi before the function call.

虽然本网站上的答案会更直接有用,但如果提供一个指向我可以了解更多信息的文档链接,我将不胜感激,尽管适当的文档可能过于技术性,让我无法理解.

While an answer on this site will be more immediately helpful, a link to documentation where I could learn more would be greatly appreciated, even though proper documentation will probably be so technical it goes over my head.

测试 C++ 程序:

void foo(int a, int b) {
    return;
}

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

从radare2拆卸的组件:

The disassembled assembly from radare2:

┌ (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的以下反汇编代码.

What prompted this question was the following disassembled code from a beginner crackme I am trying to solve.

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

I am not asking for help solving this crackme, just help understanding how function arguments are passed in the below examples and where I could go to look this up in the future.

crackme 中的以下示例显示了 sym.imp.puts 被调用(以下两个示例是手工输入的,因此它们可能包含错误,尽管我确实尝试过校对):

The following example from the crackme shows sym.imp.puts being called (the following two examples are hand typed so they may contain mistakes, although I did try to proofread):

; 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_key 的地址从 rdi 传递给它.

puts appears to have the address to str.Wrong_key passed to it from rdi.

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

On the other hand this code snippet:

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.

I am unable to understand what is going on in this code snippet. var_6ch is not used before this. Presumably scanf is being called like this: scanf("%d", var_6ch); But I fail to see how var_6ch or the %d string are passed to scanf.

之前的所有代码示例似乎都没有使用堆栈来传递参数,因此感谢您提供任何帮助.

All of the previous code samples do not appear to use the stack to pass arguments so any and all help is appreciated.

推荐答案

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

On x86-64 (which is the architecture you're on), the System V ABI calling convention is the most commonly used, and defines the following registers to be used for function parameters (in order of declaration): RDI, RSI, RDX, RCX, R8, R9, XMM0 to XMM07. The return register is RAX.

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

On the other side, on x86 32bit, since there are fewer registers, parameters are usually passed on the stack.

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

Of course a calling convention does not only define how to pass parameters, to know more you can take a look at the wikipedia page.

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

What you're seeing in that snippet of code is exactly this:

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

这篇关于在汇编级别的 x86 上,参数传递和返回值如何在 C/C++ 中工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆