在C中执行Shellcode触发Segmentation Fault的解决方案咨询
在Kali Linux中执行Shellcode触发Segmentation Fault的解决方案
问题背景
我在Kali Linux虚拟机中尝试用C语言执行用于打印“Hello, Shellcode!”的Shellcode,但触发了Segmentation Fault错误。以下是我的操作步骤及相关代码:
1. 测试代码与汇编提取
测试代码shellcode_example.c:
// shellcode_example.c #include <stdio.h> int main() { printf("Hello, Shellcode!\n"); return 0; }
提取汇编指令的命令:
gcc -o shellcode_example shellcode_example.c objdump -d -M intel shellcode_example
得到的main函数汇编:
080484b6 <main>: 80484b6: 55 push ebp 80484b7: 89 e5 mov ebp,esp 80484b9: 83 ec 08 sub esp,0x8 80484bc: 68 60 86 04 08 push 0x8048660 80484c1: e8 aa fe ff ff call 8048370 <puts@plt> 80484c6: 83 c4 04 add esp,0x4 80484c9: b8 00 00 00 00 mov eax,0x0 80484ce: c9 leave 80484cf: c3 ret
2. Shellcode执行代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> // 示例NOP Shellcode unsigned char shellcode[] = { 0x90, 0x90, 0x90, 0x90, 0xC3 // 自认为是可用的Shellcode }; int main() { void *mem = mmap(NULL, sizeof(shellcode), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (mem == MAP_FAILED) { perror("mmap"); return 1; } // 将Shellcode复制到分配的内存 memcpy(mem, shellcode, sizeof(shellcode)); // 转换为函数指针 typedef void (*ShellcodeFunction)(); ShellcodeFunction func = (ShellcodeFunction)mem; // 执行Shellcode func(); // 释放内存 munmap(mem, sizeof(shellcode)); return 0; }
3. 已尝试的无效方案
- 使用编译命令:
gcc -fno-stack-protector -z execstack -O OutputFileName yourShellCode.c - 尝试使用局部/全局变量存储Shellcode
4. 更新后的Shellcode及对应汇编
更新后的Shellcode:
unsigned char shellcode[] = { 0xb8, 0x04, 0x00, 0x00, 0x00, 0xbb, 0x01, 0x00, 0x00, 0x00, 0xb9 ,0x00 ,0xa0 ,0x04, 0x08, 0xba, 0x0d ,0x00 ,0x00, 0x00 ,0xcd ,0x80 ,0xb8, 0x01, 0x00 ,0x00 ,0x00, 0x31, 0xdb, 0xcd, 0x80 };
对应汇编代码:
section .data hello db 'Hello, World!',0 section .text global _start _start: ; write系统调用 mov eax, 4 mov ebx, 1 mov ecx, hello mov edx, 13 int 0x80 ; exit系统调用 mov eax, 1 xor ebx, ebx int 0x80
解决方案
问题根源
你更新后的Shellcode存在两个致命问题:
- 硬编码内存地址:
mov ecx, hello被编译成了0xb9 00 a0 04 08,这个地址是汇编器编译时生成的.data段地址,当Shellcode被复制到mmap分配的内存中时,该地址完全无效,访问会直接触发段错误。 - 依赖外部数据段:Shellcode需要的字符串
Hello, World!存储在独立的.data段,而Shellcode本身被加载到新内存区域时,无法访问原程序的.data段内容。
正确的Shellcode编写方式:嵌入字符串+位置无关代码
要编写可独立执行的Shellcode,必须把字符串直接嵌入到指令流中,并用位置无关的方式引用它。以下是修复后的方案:
步骤1:编写位置无关的汇编代码(hello.s)
global _start _start: ; write系统调用:向stdout输出字符串 mov eax, 4 ; sys_write的系统调用号(32位Linux) mov ebx, 1 ; 文件描述符stdout=1 pop ecx ; 弹出栈顶的字符串地址(call指令把返回地址压栈,我们利用这个特性) mov edx, 16 ; 字符串长度("Hello, Shellcode!\n"共16字节) int 0x80 ; 触发系统调用 ; exit系统调用:正常退出 mov eax, 1 ; sys_exit的系统调用号 xor ebx, ebx ; 退出码0 int 0x80 ; 嵌入字符串,利用call指令的特性获取地址 call _start ; call会把下一条指令的地址(即字符串地址)压栈 db 'Hello, Shellcode!' db 0x0a ; 换行符
步骤2:编译并提取Shellcode
执行以下命令编译汇编代码,提取纯机器码:
nasm -f elf32 hello.s -o hello.o ld -m elf_i386 hello.o -o hello objcopy -O binary hello hello.bin xxd -p hello.bin | tr -d '\n'
执行后会得到十六进制的Shellcode:e8ffffffb804000000bb0100000059ba10000000cd80b80100000031dbcd8048656c6c6f2c205368656c6c636f6465210a
步骤3:修改C执行代码
把提取到的Shellcode替换到执行代码中:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> unsigned char shellcode[] = { 0xe8, 0xff, 0xff, 0xff, 0xb8, 0x04, 0x00, 0x00, 0x00, 0xbb, 0x01, 0x00, 0x00, 0x00, 0x59, 0xba, 0x10, 0x00, 0x00, 0x00, 0xcd, 0x80, 0xb8, 0x01, 0x00, 0x00, 0x00, 0x31, 0xdb, 0xcd, 0x80, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x53, 0x68, 0x65, 0x6c, 0x6c, 0x63, 0x6f, 0x64, 0x65, 0x21, 0x0a }; int main() { void *mem = mmap(NULL, sizeof(shellcode), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (mem == MAP_FAILED) { perror("mmap"); return 1; } memcpy(mem, shellcode, sizeof(shellcode)); typedef void (*ShellcodeFunction)(); ShellcodeFunction func = (ShellcodeFunction)mem; func(); munmap(mem, sizeof(shellcode)); return 0; }
步骤4:编译并运行
由于你的代码已经用mmap分配了可执行内存,不需要额外的编译参数,直接编译运行即可:
# 先安装32位编译库(Kali默认64位) sudo apt install gcc-multilib # 编译32位程序 gcc -m32 shellcode_runner.c -o shellcode_runner # 运行 ./shellcode_runner
执行后会输出:Hello, Shellcode!
额外说明
- 必须使用
-m32编译32位程序,因为Shellcode是基于32位Linux系统调用(int 0x80)编写的 - 不要直接从
printf的汇编提取Shellcode,因为它依赖PLT/GOT和外部数据段,无法独立执行 - 始终确保Shellcode是**位置无关(PIC)**且不依赖外部内存区域的
内容的提问来源于stack exchange,提问作者Fidelio Rainbow
相关产品推荐
相关产品推荐

