C 汇编程序函数转换

C assembler function casting

我遇到了这段代码(整个程序见 this 页,见名为 "srop.c" 的程序)。

我的问题是关于如何在 main 方法中使用 func。我只保留了我认为可能相关的代码。

*ret = (int)func +4;这一行让我很困惑。

关于这个我有三个问题:

  1. func(void)是函数,不应该用func()调用(注意括号)
  2. 承认这对我来说可能是一些未知的调用函数的方式,如何在应该 return void 时将其转换为 int
  3. 我知道作者不想保存帧指针也不想更新它(序言),正如他的评论所表明的那样。通过将函数转换为 int 并添加四行,如何提前跳过两行?

(gdb) disassemble func
Dump of assembler code for function func:
0x000000000040069b <+0>:     push   %rbp
0x000000000040069c <+1>:     mov    %rsp,%rbp
0x000000000040069f <+4>:     mov    [=10=]xf,%rax
0x00000000004006a6 <+11>:    retq
0x00000000004006a7 <+12>:    pop    %rbp
0x00000000004006a8 <+13>:    retq
End of assembler dump.

可能相关的是,编译后的 gcc 告诉我以下内容:
warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]

请看下面的代码。

void func(void)
{
    asm("mov    [=11=]xf,%rax\n\t");
    asm("retq\n\t");
}

int main(void)
{
    unsigned long *ret;

    /*...*/

    /* overflowing */
    ret = (unsigned long *)&ret + 2;
    *ret = (int)func +4; //skip gadget's function prologue

    /*...*/

    return 0;
}

[编辑] 根据非常有用的建议,这里有一些进一步的信息:

    calling func returns a pointer to the start of the function: 0x400530
    casting this to an int is dangerous (in hex) 400530
    casting this to an int in decimal  4195632
    safe cast to unsigned long 4195632

    size of void pointer:  8
    size of int:  4
    size of unsigned long:  8

[编辑 2:] @cmaster:能否请您指出更多有关如何将汇编函数放在单独文件中并 link 的信息?原程序不会编译,因为它不知道函数prog(放入汇编文件时)是什么,所以必须在编译前或编译时添加?

此外,gcc -S when 运行 在仅包含汇编命令的 C 文件上似乎添加了很多额外信息,不能 func(void) 由以下汇编程序表示代码?

func:
mov    [=13=]xf,%rax
retq
  1. 函数名是指向函数开始的指针。所以作者当时并没有调用该函数。只是保存对它开头的引用。

  2. 这不是空白。它是一个函数指针。更准确地说,在这种情况下,它的类型是:void (*)(void)。指针只是一个地址,因此可以转换为 int(但如果为 64 位系统编译,地址可能会被截断,因为在这种情况下 int 是 32 位)。

  3. 函数的第一条指令将 fp 压入堆栈。通过加 4,该指令被跳过。请注意,在您提供的代码片段中,该函数尚未被调用。这可能是您没有包含的部分代码。

此代码的假设远不止于此。无论如何,您显示的代码段仅尝试生成指向汇编程序函数体的指针,而不会尝试调用它。这是它的作用和假设:

  • func 本身产生一个指向函数的指针。

    假设 1:
    该指针实际上指向 func 的汇编代码的开头。这个假设不一定正确,有些架构中函数指针实际上是指向一对指针的指针,其中一个指向代码,另一个指向数据段。

  • func + 4 递增此指针以指向函数体的第一条指令。

    假设 2:
    函数指针可以递增,它们的递增是以字节为单位的。我相信这不在 C 标准中,但我在那个方面可能是错误的。

    假设 3: 编译器插入的序言正好是四个字节长。绝对没有什么可以规定编译器应该发出什么样的序言,允许有多种变体,长度非常不同。您给出的代码试图通过不 passing/returning 任何参数来控制序言的长度,但仍然可以有编译器生成不同的序言。更糟糕的是,序言的大小可能取决于优化级别。

  • 结果指针被转换为 int

    假设 4:
    sizeof(void (*)(void)) == sizeof(int)。这在大多数 64 位系统上是错误的:在这些系统上 int 通常仍然是四个字节,而指针占用八个字节。在这样的系统上,指针值将被截断。当 int 被转换回函数指针并被调用时,这可能会使程序崩溃。


我的建议:
如果你真的想用汇编程序编程,用 gcc -S 编译一个只有空函数的文件。这将为您提供一个汇编程序源文件,其中包含汇编程序生成有效目标文件所需的所有内容,并向您展示可以在何处为您自己的函数添加代码。以您喜欢的任何方式修改该文件,然后像往常一样将其与一些调用 C 代码一起编译。这样你就可以避免所有这些危险的小假设。