2

运行以下 C 程序(在 Ubuntu 中使用 gcc 编译)时出现分段错误。

#include <stdio.h>

char f[] = "\x55\x48\x89\xe5\x48\x89\x7d\xf8\x48\x89\x75\xf0\x48\x8b\x45\xf8\x8b\x10\x48\x8b\x45\xf0\x8b\x00\x89\xd1\x29\xc1\x89\xc8\xc9\xc3";

int main()
{
    int (*func)();
    func = (int (*)()) f;

    int x=3,y=5;
    printf("%d\n",(int)(*func)(&x,&y));
    return 0;
}

该字符串f包含以下函数的机器代码。

int f(int*a, int*b)
{
    return *a-*b;
}

参看:

f.o:     file format elf64-x86-64

Disassembly of section .text:

0000000000000000 <f>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 89 7d f8             mov    %rdi,-0x8(%rbp)
   8:   48 89 75 f0             mov    %rsi,-0x10(%rbp)
   c:   48 8b 45 f8             mov    -0x8(%rbp),%rax
  10:   8b 10                   mov    (%rax),%edx
  12:   48 8b 45 f0             mov    -0x10(%rbp),%rax
  16:   8b 00                   mov    (%rax),%eax
  18:   89 d1                   mov    %edx,%ecx
  1a:   29 c1                   sub    %eax,%ecx
  1c:   89 c8                   mov    %ecx,%eax
  1e:   c9                      leaveq 
  1f:   c3                      retq   

这是使用编译的:

gcc test.c -Wall -Werror
./a.out
Segmentation fault

预期的输出是-2- 我怎样才能让它工作?

4

2 回答 2

4

显然,下面的建议不再适用于 gcc,因为现在的数组数据位于一个单独的不可执行的只读 ELF 段中。

由于历史原因,我将把它留在这里。


有趣的是,链接器并没有抱怨您试图将 achar f[] = "...";作为函数链接f()到您的应用程序。你试图调用一个函数f()。有一个符号f链接到可执行文件,但令人惊讶的是它根本不是函数,而是一些变量。因此它无法执行它。这可能是由于堆栈执行保护机制造成的。

为了避免这种情况,显然,您只需要将字符串获取到进程内存的文本段。如果您将字符串声明为const char f[].

来自Aleph One 的 Smashing The Stack For Fun and Profit

文本区域由程序固定,包括代码(指令)和只读数据。该区域对应于可执行文件的文本部分。

由于const char[] 只读的,编译器将其与代码一起放入文本区域。从而绕过执行阻止机制,机器能够执行其中的机器代码。


例子:

/* test.c */
#include <stdio.h>

const char f[] = "\x55\x48\x89\xe5\x48\x89\x7d\xf8\x48\x89\x75\xf0\x48\x8b\x45\xf8\x8b\x10\x48\x8b\x45\xf0\x8b\x00\x89\xd1\x29\xc1\x89\xc8\xc9\xc3";

int main()
{
    int (*func)();
    func = (int (*)()) f;

    int x=3,y=5;
    printf("%d\n",(int)(*func)(&x,&y));
    return 0;
}

产量:

$ gcc test.c -Wall && ./a.out
-2

(Fedora 16,gcc 4.6.3)

于 2012-09-16T13:20:35.150 回答
0

如果我对您的理解正确,您正在尝试运行不在文本空间中而是在您的初始化静态存储中的简单代码?如果这失败了,那么只有三个原因:您的代码初始化不正确(在这种简单的情况下不太可能),您的数据空间已被踩到(在这种简单的情况下看起来不像),或者您的系统是防止它作为一种安全措施(很可能是因为您尝试做的事情相当不典型,主要用于缓冲区溢出利用)。

于 2012-09-16T12:56:28.830 回答