【问题标题】:How can I execute a function from its memory address in C / assembly? [duplicate]如何从 C / 程序集中的内存地址执行函数? [复制]
【发布时间】:2021-02-28 18:36:28
【问题描述】:

我有以下汇编函数(已经用 objdump 显示)

0000000000000000 <add>:
   0:   b8 06 00 00 00          mov    $0x6,%eax
   5:   c3                      retq  

现在我用 C 编写了以下代码:

#include <stdio.h>

typedef int (*funcp) (int x);

unsigned char foo[] = {0xb8,0x06,0x00,0x00,0x00,0xc3};

int main(void)
{
  int i;
  funcp f = (funcp)foo;
  i = (*f);
  
  printf("exit = %d\n", i);
  return 0;
}

在全局变量 foo 中,我在汇编中输入了我的函数的内存地址并尝试执行它,但它没有按预期返回 6。 如何为其内存地址执行函数?此外,我在哪里可以对这个主题进行更多研究?

obs:有时我收到分段错误(核心转储)错误

【问题讨论】:

  • 首先,您必须使用() 来调用函数。
  • 数据部分通常是不可执行的,这意味着你的代码会遇到分段错误。
  • gcc -z execstack temp1.c 为我工作。我得到6 作为输出。
  • @fuz: -z execstack 在 Linux 上使 all 可读页面可执行,直到最近的内核版本。 Linux 的 ELF 程序加载器在看到可执行的 GNU 堆栈元数据时会设置 read-implies-exec 进程“个性”标志。 Linux default behavior against `.data` section - PT_GNU_STACK == RWX 不再暗示 exec-all;我刚刚更新了我的 Arch Linux 并且 gcc -zexecstack 不会使 .data(或 .rodata)可执行。另请参阅Unexpected exec permission from mmap...
  • @fuz:是的,__attribute__((section(".text"))) 有效。我收到 Assembler 消息:/tmp/ccNetmKQ.s:28: Warning: ignoring changed section attributes for .text 但它确实运行。 godbolt.org/z/draGeh。在我的桌面上,-Wl,-N 使其无法链接:/usr/bin/ld: cannot find -lgcc_s这两件事(文本部分中的数组和读/写文本部​​分)对于非常量可执行数组都是必需的。

标签: c linux assembly x86 disassembly


【解决方案1】:

NX 标志可能是您的“朋友”。永远不会作为二进制机器代码执行的内存部分可以标记为 No-eXecute。见https://en.wikipedia.org/wiki/NX_bit。因此,取决于架构、操作系统和设置,甚至是 BIOS 设置。

因此,此功能可能开启或关闭。如果在程序的数据部分使用 NX,它将不会运行。您将需要 mmap() 设置了 PROT_EXEC 的一块内存,将数据复制进去,然后运行它。

对于以下内容,我将二进制文件更改为 amd64 代码(+1 func)。使用 mmap() 副本时,它可以工作。当直接调用 foo 时,它会失败(在我的 NX 激活的机器上)

(没有错误检查的代码,释放内存等)

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>

typedef int (*funcp) (int x);

unsigned char foo[] = {0x8d,0x47,0x01,0xc3};
//unsigned char foo[] = {0xc3,0xb8,0x06,0x00,0x00,0x00,0xc3};

int main(void)
{
  int i;

  void *mem2;

  mem2 = mmap(0,4096,PROT_WRITE|PROT_READ|PROT_EXEC,MAP_PRIVATE|MAP_ANONYMOUS|MAP_EXECUTABLE,-1,0);

  memcpy(mem2,foo,sizeof(foo));


  funcp f = (funcp)mem2;
  i = f(42);

  printf("exit = %d\n", i);
  return 0;
}

【讨论】:

  • 如果没有__builtin___clear_cache 让编译器知道该缓冲区中的存储不是“死的”,即它将被作为代码读取,这在 GNU C 中是不完全安全的。 (在 x86 上不会发生实际的缓存失效,它具有一致的 I-cache,但可能存在编译时优化问题。)参见 How to get c code to execute hex machine code?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-18
  • 1970-01-01
  • 2015-02-26
  • 1970-01-01
  • 2019-04-07
  • 1970-01-01
  • 2015-07-15
相关资源
最近更新 更多