You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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存在两个致命问题:

  1. 硬编码内存地址:mov ecx, hello被编译成了0xb9 00 a0 04 08,这个地址是汇编器编译时生成的.data段地址,当Shellcode被复制到mmap分配的内存中时,该地址完全无效,访问会直接触发段错误。
  2. 依赖外部数据段: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 15:02:11