Linux下Shellcode编写
学习自:《Penetration Testing with Shellcode》
基本过程是首先使用汇编通过系统调用的方式实现程序功能,编译成可执行文件,然后使用 objdump 进行机器码提取
Hello World
先看一个汇编通过系统调用写 Hello World 的例子
要输出一个 hello world,可以通过 write 函数来实现,通过下面的方法查找 write 函数的系统调用号,我用的 ubuntu 16.04 是这俩文件(找出来的是十进制的)
cat /usr/include/asm/unistd_32.h | grep write
cat /usr/include/asm/unistd_64.h | grep write
#define __NR_write 1
#define __NR_pwrite64 18
#define __NR_writev 20
#define __NR_pwritev 296
#define __NR_process_vm_writev 311
rax 是存放系统调用号的,这里就应该是 1
使用 man 2 write
可以找到 write 函数的参数
ssize_t write(int fd, const void *buf, size_t count);
第一个参数是三种输出模式
0 | 1 | 2 |
---|---|---|
stdin | stdout | stderr |
标准输入 | 标准输出 | 标准错误 |
第二个参数是字符串的指针,第三个参数是输出的字数,而 64 位的程序,寄存器传参:rdi, rsi, rdx, rcx, r8, r9 剩下的才用栈,所以 rdi 应该是 1,rsi 应该是字符串的地址,rdx 应该是长度
global _start
section .text
_start:
mov rax, 1 ;设置rax寄存器为write的系统调用号
mov rdi, 1 ;设置rdi为write的第一个参数
mov rsi, hello_world ;设置rsi为write的第二个参数
mov rdx, length ;设置rdx为write的第三个参数
syscall ;调用syscall
section .data
hello_world: db 'hello world',0xa ;字符串hello world以及换行
length: equ $-hello_world ;获取字符串长度
把代码保存为 hello-world.asm 然后汇编、链接,应该会这样显示:
yichen@ubuntu:~$ nasm -felf64 hello-world.asm -o hello-world.o
yichen@ubuntu:~$ ld hello-world.o -o hello-world
yichen@ubuntu:~$ ./hello-world
hello world
段错误 (核心已转储)
因为我们还没有让他正常退出,可以在后面 exit 的 syscall,让他正常退出即可
mov rax,60
mov rdi,0
syscall
使用 objdump 提取 shellcode,这一长串的魔法般的正则我就不解释了我也不会
yichen@ubuntu:~$ objdump -M intel -D hello-world | grep '[0-9a-f]:' | grep -v 'file' | cut -f2 -d: | cut -f1-7 -d' ' | tr -s ' ' | tr ' ' ' ' | sed 's/ $//g' | sed 's/ /\x/g' | paste -d '' -s
xb8x01x00x00x00xbfx01x00x00x00x48xbexd8x00x60x00x00x00x00x00xbax0cx00x00x00x0fx05xb8x3cx00x00x00xbfx00x00x00x00x0fx05x68x65x6cx6cx6fx20x77x6fx72x6cx64x0a
在 C 语言中使用 shellcode:
#include<stdio.h>
#include<string.h>
unsigned char code[]="xb8x01x00x00x00xbfx01x00x00x00x48xbexd8x00x60x00x00x00x00x00xbax0cx00x00x00x0fx05xb8x3cx00x00x00xbfx00x00x00x00x0fx05x68x65x6cx6cx6fx20x77x6fx72x6cx64x0a";
int main(){
printf("shellcode length:%d
",(int)strlen(code));
int (*ret)()=(int(*)())code;
ret();
}
编译的时候需要关掉栈不可执行,当你去执行的时候会发现出错了
yichen@ubuntu:~$ gcc -z execstack -o test 1.c
yichen@ubuntu:~$ ./test
shellcode length:2
t
因为 shellcode 中存在一些 x00,我们称为:bad character,它会使字符串截断,就没有后面什么事情了,所以要想办法消除这些 bad character
bad character 列表 | ||
---|---|---|
00 |