【问题标题】:C assembler function castingC 汇编器函数转换
【发布时间】:2015-03-29 19:27:58
【问题描述】:

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

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

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

对此我有三个问题:

  1. func(void) 是一个函数,不应该用func() 调用(注意括号)
  2. 接受这对我来说可能是一些未知的调用函数的方式,当它应该返回 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    $0xf,%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    $0xf,%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

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

另外,gcc -S 在只包含汇编命令的 C 文件上运行时似乎添加了很多额外的信息,func(void) 不能用以下汇编代码表示吗?

func:
mov    $0xf,%rax
retq

【问题讨论】:

    标签: c linux assembly casting static-linking


    【解决方案1】:

    此代码的假设远不止是它的优点。无论如何,您显示的 sn-p 仅尝试生成指向汇编器函数体的指针,它不会尝试调用它。以下是它的作用和假设:

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

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

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

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

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

    • 结果指针被转换为int

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


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

    【讨论】:

    • 谢谢你,一个很好的答案。检查假设#4,您是完全正确的,但是内存地址太低了,即使被截断也可以工作。您能否给我提供一些关于如何链接这两个程序的更多信息,包括在编译时以及如何从 C 程序中调用汇编函数?
    • 由于您正在修改生成的汇编程序文件,因此您已经有了所需的入口点(这些入口点由通常以点. 开头的汇编程序指令定义)。也就是说,链接与您从用于生成模板的 .c 文件编译时完全相同。要调用您的汇编程序函数,您“只”需要在 C 头文件中提供正确的函数原型。我说“仅”,因为您的编译器不会检查其正确性,您需要遵守编译器使用的调用约定。
    • 关于进一步阅读:google for "ABI " 你应该找到一些包含所有相关细节的规范(ABI 代表“抽象二进制界面”)。对于不太正式的信息,请使用“调用约定”重试。请注意,对于 X86 处理器,有一些完全不同的 ABI,您需要找到在您的系统上使用的 ABI。例如,其中一个 ABI 传递堆栈上的所有参数,而另一个使用寄存器作为前几个参数。一个好的开始可能是:en.wikipedia.org/wiki/X86_calling_conventions
    • 再次感谢您的回答,我的问题只是如何调用用汇编程序编写的函数(不带任何参数)。我已经写了我是如何做到的,作为对“从 c 调用汇编函数”这个问题的回复:stackoverflow.com/questions/13901261/…
    【解决方案2】:
    1. 函数名是指向函数开头的指针。所以作者当时没有调用该函数。只是保存对它开头的引用。

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

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

    【讨论】:

      猜你喜欢
      • 2021-04-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-13
      • 2014-08-20
      • 1970-01-01
      • 2014-07-24
      • 2017-01-08
      相关资源
      最近更新 更多