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

x86-64位Shellcode在测试器中触发段错误求助

问题描述

我编写的x86-64位Shellcode单独编译运行时,Strace未显示错误,但使用Shellcode测试器运行时触发Segmentation Fault(段错误)。32位版本的Shellcode运行正常,该问题已困扰我数日,希望能得到技术帮助。

64位Shellcode代码
section .text

global _start

_start:
    ;execve
    ; - rax = syscall number (59, 0x3b)
    ; - rdi = const char *filename
    ; - rsi = const char *const *argv
    ; - rdx = const char *const *envp
    xor rax, rax
    xor rdx, rdx

    ; //bin/sh
    push rax
    mov rdi, rsp
    mov dword [rsp], 0x69622f2f
    mov dword [rsp+4], 0x68732f6e
    xor eax, eax
    mov [rsp+8], al

    ; -c
    push rax
    mov rsi, rsp
    mov word [rsp], 0x632d
    xor eax, eax
    mov [rsp+2], al

    ; whoami
    push rax
    mov rcx, rsp
    mov word [rsp], 0x6877
    mov dword [rsp+2], 0x696d616f
    xor eax, eax
    mov [rsp+6], al

    push rax
    push rcx
    push rsi
    push rdi
    mov rsi, rsp

    push 0x3b
    pop rax
    syscall

    ;exit
    ; - rax = syscall number (60, 0x3c)
    ; - rdi = int error_code
    push 0x3c
    pop rax
    shl rdi, 0x1
    neg rdi
    syscall
Shellcode测试器代码
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>

# define EXEC_MEM ((void *) 0x80000000)

int main() {
    char shellcode[] = "\x48\x31\xc0\x48\x31\xd2\x50\x48\x89\xe7\xc7\x04\x24\x2f\x2f\x62\xc7\x44\x24\x04\x6e\x2f\x68\x31\xc0\x88\x44\x24\x08\x50\x48\x89\xe6\x66\xc7\x04\x24\x2d\x63\x31\xc0\x88\x44\x24\x02\x50\x48\x89\xe1\x66\xc7\x04\x24\x77\x68\xc7\x44\x24\x02\x6f\x61\x69\x31\xc0\x88\x44\x24\x06\x50\x51\x56\x57\x48\x89\xe6\x6a\x3b\x58\x0f\x05";

    mmap(EXEC_MEM, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, -1, 0);
    memcpy(EXEC_MEM, (void *)shellcode, strlen(shellcode)+1);
    (*(int (*)())EXEC_MEM)();
    return 0;
}
编译命令

Shellcode编译

nasm -f elf64 -o 'shellcode.o' 'shellcode.asm' && ld -m elf_x86_64 -o 'shellcode' 'shellcode.o'

测试器编译

gcc -no-pie -fno-stack-protector -z execstack 'shellcode_tester.c' -o 'shellcode_tester'
Strace输出
execve("/home/jobsi/Desktop/tester", ["/home/jobsi/Desktop/tester"], 0x7fffffffe050 /* 45 vars */) = 0
brk(NULL)                               = 0x405000
arch_prctl(0x3001 /* ARCH_??? */, 0x7fffffffdef0) = -1 EINVAL (Invalid argument)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7fbe000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=75159, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 75159, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ffff7fab000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\203\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=2105184, ...}, AT_EMPTY_PATH) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
mmap(NULL, 2150256, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ffff7c00000
mmap(0x7ffff7c26000, 1568768, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x26000) = 0x7ffff7c26000
mmap(0x7ffff7da5000, 348160, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1a5000) = 0x7ffff7da5000
mmap(0x7ffff7dfa000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1f9000) = 0x7ffff7dfa000
mmap(0x7ffff7e00000, 53104, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ffff7e00000
close(3)                                = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7fa8000
arch_prctl(ARCH_SET_FS, 0x7ffff7fa8740) = 0
set_tid_address(0x7ffff7fa8a10)         = 13347
set_robust_list(0x7ffff7fa8a20, 24)     = 0
rseq(0x7ffff7fa9060, 0x20, 0, 0x53053053) = 0
mprotect(0x7ffff7dfa000, 16384, PROT_READ) = 0
mprotect(0x403000, 4096, PROT_READ)     = 0
mprotect(0x7ffff7ffb000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
munmap(0x7ffff7fab000, 75159)           = 0
mmap(0x80000000, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x80000000
--- SIGSEGV {si_signo=SIGSEGV, si_code=SI_KERNEL, si_addr=NULL} ---
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault (core dumped)
问题分析与解决方案

1. Shellcode字节数组手动提取错误

汇编代码中mov dword [rsp], 0x69622f2f对应的机器码应为c7 04 24 2f 2f 62 69(对应字符串"//bi"),但你在测试器的shellcode数组中错误地将最后一个字节0x69写成了0xc7,导致存储的路径字符串无效,execve调用失败后触发后续异常。

修复方法:
将测试器中shellcode数组的错误片段\xc7\x04\x24\x2f\x2f\x62\xc7替换为\xc7\x04\x24\x2f\x2f\x62\x69。

2. x86-64栈对齐问题

x86-64系统调用要求执行syscall时,栈指针rsp必须保持16字节对齐。当前Shellcode在进入syscall前的栈指针未满足对齐要求,会导致调用异常。

修复方法:
在syscall指令前添加栈对齐指令,例如:

push 0x3b
pop rax
and rsp, ~0xf  ; 将rsp对齐到16字节边界
syscall

或者在Shellcode开头调整栈对齐(因为测试器调用Shellcode时,返回地址会被压栈,导致rsp偏移8字节):

_start:
sub rsp, 8  ; 调整栈为16字节对齐
xor rax, rax
xor rdx, rdx
; ... 后续代码不变

3. 推荐自动提取Shellcode

手动提取机器码容易出错,建议用工具自动生成:

objcopy -O binary shellcode.o shellcode.bin
xxd -p shellcode.bin | tr -d '\n'

将生成的十六进制字符串直接复制到测试器的shellcode数组中即可。

内容的提问来源于stack exchange,提问作者Marius Romeiser

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 00:47:04