x86-64 Shellcode运行触发段错误,求排查解决及软硬件问题确认
Shellcode执行段错误问题解决
问题背景
编写x86_64架构的shellcode并嵌入C文件后,编译执行触发段错误。操作流程为:编写汇编代码→提取opcode→嵌入C文件编译运行。环境为VirtualBox中的Kali虚拟机,NASM版本2.16.01,GCC版本12.2.0。
汇编代码
;nasm -f elf64 test.nasm; ld test.o -o test section .text global _start _start: xor rax, rax push rax mov rax, 0x68732f6e69622f2f push rax mov rdi, rsp xor rsi, rsi xor rdx, rdx xor rax, rax mov al, 0x3b ; execve() syscall
objdump反汇编输出
$ objdump -D -M intel test.o test.o: file format elf64-x86-64 Disassembly of section .text: 0000000000000000 <_start>: 0: 48 31 c0 xor rax,rax 3: 50 push rax 4: 48 b8 2f 2f 62 69 6e movabs rax,0x68732f6e69622f2f b: 2f 73 68 e: 50 push rax f: 48 89 e7 mov rdi,rsp 12: 48 31 f6 xor rsi,rsi 15: 48 31 d2 xor rdx,rdx 18: 48 31 c0 xor rax,rax 1b: b0 3b mov al,0x3b 1d: 0f 05 syscall
嵌入opcode的C文件
#include <stdio.h> #include <string.h> unsigned char code[] = "\x48\x31\xc0\x50\x48\xb8\x2f\x2f" "\x62\x69\x6e\x2f\x73\x68\x50\x48" "\x89\xe7\x48\x31\xf6\x48\x31\xd2" "\x48\x31\xc0\xb0\x3b\x0f\x0"; void main() { printf(" shell length: %d\n", (int)strlen(code)); int (*ret) () = (int(*) ()) code; ret(); } //gcc -fno-stack-protector -z execstack test.c -o test2
执行错误信息
./test2 shell length: 30 zsh: segmentation fault ./test2
问题原因与修复方案
1. Opcode截断错误
从objdump输出可见,syscall指令的完整opcode是0f 05,但C代码中最后一段写成了\x0f\x0,缺失了关键的\x05字节。这会导致CPU执行到此处时指令不完整,触发非法操作进而引发段错误。
修复:将code数组最后部分修改为"\x48\x31\xc0\xb0\x3b\x0f\x05"。
2. strlen()计算长度不可靠
strlen()会在遇到第一个\x00字节时停止计数,虽然当前shellcode中没有\x00,但这种方式无法准确获取shellcode的真实长度。正确的做法是使用sizeof(code) - 1(减去数组末尾自动添加的null终止符)。
修复:修改printf行代码:
printf(" shell length: %d\n", (int)(sizeof(code) - 1));
3. PIE地址随机化影响(可选)
Kali Linux默认开启PIE(位置无关执行),即使开启了-z execstack,数据段的地址随机化仍可能导致shellcode执行异常。编译时添加-fno-pie选项可关闭PIE,确保code数组的地址固定。
修改编译命令:
gcc -fno-stack-protector -z execstack -fno-pie test.c -o test2
修复后的完整C代码
#include <stdio.h> #include <string.h> unsigned char code[] = "\x48\x31\xc0\x50\x48\xb8\x2f\x2f" "\x62\x69\x6e\x2f\x73\x68\x50\x48" "\x89\xe7\x48\x31\xf6\x48\x31\xd2" "\x48\x31\xc0\xb0\x3b\x0f\x05"; void main() { printf(" shell length: %d\n", (int)(sizeof(code) - 1)); int (*ret) () = (int(*) ()) code; ret(); }
编译执行后即可正常获取shell。
内容的提问来源于stack exchange,提问作者metal4all
相关产品推荐
相关产品推荐

