当我运行下面的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;
}
c.f.。
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
- 我怎么才能让它工作?
显然下面的建议在gcc中不再适用了,因为现在的数组数据被放置在一个单独的不可执行的只读ELF段中。
出于历史原因,我把它留在这里。
有趣的是,链接器并没有抱怨你试图链接一个 char f[] = "...";
作为一个函数 f()
到你的应用程序。你试图调用一个函数 f()
. 有一个符号 f
链接到可执行文件,但令人惊讶的是它根本不是函数,而是某个变量。因此就无法执行它。这很可能是由于堆栈执行保护机制造成的。
要规避这个问题,显然,你只需要把字符串的内容传递给 文段 的进程内存。你可以实现这一点,如果你把字符串声明为 const char f[]
.
文本区域由程序固定,包括代码(指令)和只读数据。 该区域对应于可执行文件的文本部分。
由于 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)
如果我没有理解错的话,你是想运行不在文本空间,而是在初始化静态存储中的简单代码?如果失败了,那么只有三个原因:要么是你的代码初始化不正确(在这个简单的例子中不太可能),要么是你的数据空间被踩踏了(在这个简单的例子中看起来不像),要么是你的系统作为安全措施阻止了它(很有可能,因为你要做的是相当非典型的,主要用于缓冲区溢出利用)。