Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 间接“调用”指令是否总是指向函数序言?_C_Assembly_X86 64_Calling Convention - Fatal编程技术网

C 间接“调用”指令是否总是指向函数序言?

C 间接“调用”指令是否总是指向函数序言?,c,assembly,x86-64,calling-convention,C,Assembly,X86 64,Calling Convention,假设我们有一些C代码,它通过函数指针调用函数,无论是通过函数指针表还是作为参数传递的函数指针,如下所示: /* ... some other code .. */ void (*f)(void) = something; // f function pointer to some function (*f)(); 应将其编译为或等效的内容 mov %rcx, [something] ; here f=ecx callq *%rcx 问题:%ecx是否总是指向函数的序言,还是可以指向函数末尾

假设我们有一些C代码,它通过函数指针调用函数,无论是通过函数指针表还是作为参数传递的函数指针,如下所示:

/* ... some other code .. */
void (*f)(void) = something; // f function pointer to some function
(*f)();
应将其编译为或等效的内容

mov  %rcx, [something] ; here f=ecx
callq *%rcx
问题:%ecx是否总是指向函数的序言,还是可以指向函数末尾的一小段代码

例如:

void big_func(){
    /* lots of code here */
    printf("bar");
    printf("foo");
}
void small_func(){
    printf("foo");
用大函数C编译成

; 这里有更多的代码 1:48 8d 3d c4 0e 00 lea%rdi,[0xec4+%rip];ptr至酒吧 2:b8 00 mov%eax,$0x0 3:e8 e6 fe ff ff callq 1030 4:48 8d 3d b7 0e 00 lea%rdi,[0xeb7+%rip];普托富酒店 5:b8 00 mov%eax,$0x0 6:e8 d5 fe ff ff callq 1030 7:b8 00 mov%eax,$0x0 8:5d流行音乐%rbp 9:c3 ret 对small_func的调用是否可以指向4:作为入口点?像gcc这样的通用编译器会发生这种情况吗,还是只有一些人在幕后修改汇编代码时才会发生这种情况

问题:

考虑由GCC、CLAN等非专业编译器编译的所有汇编代码代码,而不需要特殊的ASM怪癖来支持后台的性能。 只考虑正常的行为。让我们假设开发人员是一个好人,并且没有实现任何讨厌的未定义行为 另外一个小问题:如果有人故意修改函数指针以跳过函数序言中的一些字节,会发生什么情况?这被认为是未定义的行为吗?例如:


编辑:我应该更清楚为什么会问这个问题。它是在一位研究大师的背景下,寻求一种新的方法,在非常低的软件或硬件级别上缓解基于ROP的攻击。如果间接调用可能指向函数序言以外的其他地方,则可能会中断基于标记的实现之一,丢失标记并在错误检测到攻击后终止程序

函数指针将始终引用函数的开头。C标准不允许从其他指针类型转换。它调用未定义的行为

但是特定的实现可能会生成正确的代码,特别是当函数没有设置堆栈帧时。但在大多数情况下,它将失败


但这毫无意义——您应该简单地将@big@函数拆分为两个较小的函数,并在需要时调用它们,而不使用伪技巧。

函数指针将始终引用函数的开头。C标准不允许从其他指针类型转换。它调用未定义的行为

但是特定的实现可能会生成正确的代码,特别是当函数没有设置堆栈帧时。但在大多数情况下,它将失败


但这毫无意义-你应该简单地将@big@函数分成两个较小的函数,并在需要时调用它们,而不使用伪技巧。

在C语言中,与大多数语言一样,函数有非常特定的含义、用途和实现。在调用中,函数指针必须像函数一样工作。我们不能调用非函数的代码,无论是直接调用还是函数指针。函数通常不知道它是直接调用的还是通过指针调用的-它必须接受传递的参数,执行其函数并通常返回给调用方,无论它是直接调用还是通过函数指针调用。在C中,函数只有一个入口点

在C中,没有一个不是函数的代码片段的概念,它可以被传递到函数指针或由函数指针调用。C有标签和goto,但没有标签指针,例如标签变量或标签变量goto


并非所有函数都需要序言,它们只需要能够接受它们的参数并执行某些操作并返回给调用者-这并不一定需要序言或尾声,但它们仍然是具有单个入口点的函数。

在C中,与大多数语言一样,函数具有非常特定的含义,目的和实现。在调用中,函数指针必须像函数一样工作。我们不能调用非函数的代码,无论是直接调用还是函数指针。函数通常不知道是直接调用还是通过指针调用-它必须接受传递的参数,执行它的函数并返回给调用者,不管它是如何被直接调用的,还是通过函数指针调用的

在C中,没有一个不是函数的代码片段的概念,它可以被传递到函数指针或由函数指针调用。C有标签和goto,但没有标签指针,例如标签变量或标签变量goto

并非所有函数都需要序言,它们只需要能够接受它们的参数
做点什么并返回给调用者-这不一定需要序言或尾声,但它们仍然是具有单个入口点的函数。

我想说您的问题不清楚。你问什么问题?这个问题有什么意义。如果从函数序言中跳过一些字节,会发生什么?如果在这里nop,则从零开始;否;或者mov eax,eax崩溃在大多数情况下,在您的示例中,它不能指向4:因为这样,末尾的pop%rbp将不起作用,并且您的printf堆栈也将不对齐。在适当的情况下,可以手工编写此类函数。并非所有函数都有序言。第一条指令属于正文,这是问题的重要部分,还是您使用的序言更像函数的开始?我使用的序言是一般意义上的函数的开始,我的缺点是,如果这不够清楚,那么如果您定义了big_func{…;small_func;},那么它可以编译为另一个函数的tailcall。或者更好的是,理论上,如果编译器将big_func放在small_func之前,那么在没有jmp的情况下,执行可能会陷入其中,但我认为真正的编译器不会寻求这种优化。e、 g.展示了手工操作,其中print\u newline只是print\u char'\n'。我想你的问题不清楚。你问什么问题?这个问题有什么意义。如果从函数序言中跳过一些字节,会发生什么?如果在这里nop,则从零开始;否;或者mov eax,eax崩溃在大多数情况下,在您的示例中,它不能指向4:因为这样,末尾的pop%rbp将不起作用,并且您的printf堆栈也将不对齐。在适当的情况下,可以手工编写此类函数。并非所有函数都有序言。第一条指令属于正文,这是问题的重要部分,还是您使用的序言更像函数的开始?我使用的序言是一般意义上的函数的开始,我的缺点是,如果这不够清楚,那么如果您定义了big_func{…;small_func;},那么它可以编译为另一个函数的tailcall。或者更好的是,理论上,如果编译器将big_func放在small_func之前,那么在没有jmp的情况下,执行可能会陷入其中,但我认为真正的编译器不会寻求这种优化。e、 g.演示了手动执行此操作,其中print_newline只是print_char'\n'。相关:GNU C允许您获取标签的地址,以便在函数中与goto一起使用。但它肯定不是函数指针,在实践中,将标签指针强制转换为函数指针,然后deref调用它,这将是UB,而且很可能是突破性的。与goto一起使用必须来自同一个函数@彼得考德斯,谢谢你指出这一点。这是非标准的,对吧?是的,这是GNUC的扩展。理论上,C编译器可以创建一个包含多个函数入口点的代码块。你可以在asm中这样做。但你是对的,它在语言层面上是不可见的。e、 g.演示了如何手动执行此操作,其中print_newline只是print_char'\n',您可以优化tailcall,使其落入另一个函数,而无需jmp。但在实践中,像GCC和clang这样的编译器最多只能使用jmp,我认为不会失败。同样相关的是:-函数的入口点不必是最低地址指令。我认为,如果您假设前面的代码是一个具有自己的.size的不同函数,那么这是安全的。编译器不会让asm变成这样,而且它肯定不是纯C可以利用的东西。相关:GNUC允许您获取标签的地址,以便在函数中与goto一起使用。但它肯定不是函数指针,在实践中,将标签指针强制转换为函数指针,然后deref调用它,这将是UB,而且很可能是突破性的。与goto一起使用必须来自同一个函数@彼得考德斯,谢谢你指出这一点。这是非标准的,对吧?是的,这是GNUC的扩展。理论上,C编译器可以创建一个包含多个函数入口点的代码块。你可以在asm中这样做。但你是对的,它在语言层面上是不可见的。e、 g.演示了如何手动执行此操作,其中print_newline只是print_char'\n',您可以优化tailcall,使其落入另一个函数,而无需jmp。但在实践中,像GCC和clang这样的编译器最多只能使用jmp,我认为不会失败。同样相关的是:-函数的入口点不必是最低地址指令。我认为,如果您假设前面的代码是一个具有自己的.size的不同函数,那么这是安全的。编译器不会让asm变成这样,而且它肯定不是你可以从纯C中利用的东西,或者用大尾巴调用small_func来代替手动内联。正如我在其他地方评论的那样,理论上,编译器可以生成符合 到small_func,甚至没有JMP,但实际上它们没有。或者用大尾巴调用small_func,而不是手动内联。正如我在其他地方评论的那样,理论上,编译器可以使asm变成小函数,甚至没有JMP,但实际上它们没有。
void (*f)(void) = something; // f function pointer to some function
f=(void (*func_ptr)(void)) ((*char)f+2)
(*f)(); //skips `push ebp`