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
相关产品推荐
相关产品推荐

