shellcode
在pwn的过程中常常需要通过自己写shellcode来获取shell,本文将介绍几种简单的shellcode
注:本文以x86为基础
调用系统函数
在开始写shellcode时,首先需要想到,我应该如何调用shell呢?
在写C语言中,通常我们需要调用
1 | system("/bin/sh") |
从而获得shell
在写汇编时,有时候并不需要这么做
linux
在使用linux时写汇编时完全不必再去libc寻找system函数,然后传递参数并且call system,只需要使用 系统调用
先看一段引自wiki的介绍
system call is the programmatic way in which a computer program requests a service from the kernel of the operating system it is executed on.
简单来讲,系统调用就是其实就是调用函数,而这个调用和call又有所区别,它执行的代码不在你的程序中,而在系统的内核空间中。系统调用包含了 文件读写,运行程序,获取时间等 一系列和系统有关的函数。若是在写C语言时调用这些函数,生成的汇编代码往往是到libc等库中找到程序地址,传递参数,再call其地址,而系统调用并不需要如此的麻烦,只需要用系统中断以及结合寄存器进行传参
系统调用主要有以下内容
int 0x80 与 syscall
若是需要系统调用,只需要在汇编中加入一句: int 0x80 ,这将告诉CPU,现在要进行一次中断从用户态进入内核态(我也不知道这是啥),而 0x80 表示中断编号,意味着告诉内核 程序要进行系统调用
若是在 x64 下 则 应将 0x80 替换为 syscall
系统调用编号
在linux中存在着许多系统调用,为了区分请求的API,内核开发者给每个系统调用都分配了一个系统调用号,调用时需要将系统调用号储存在EAX寄存器中,例如要调用 sys_write ,其系统调用号为4,则需要
1 | xxxxxxxx |
传递参数
系统调用时传递参数通常有2种方式,在参数小于等于5个时使用,EBX,ECX,EDX,ESI,EDI这5个寄存器,若是参数大于5个,则需要为EBX提供一个存放参数的内存的地址,还是以sys_write
为例子
查阅文档可以知道,其C函数声明为
1 | ssize_t write(int fd, const void *buf, size_t count); |
所以调用时的汇编代码为
1 | ;字符串'abc'入栈 |
windows
这个我也不会,挖坑待填
确定参数地址
系统调用时传递参数往往需要使用内存地址,通常情况下程序都会开启alsr,显然参数的地址大多数情况下是动态的(除了全局变量),这时候需要利用一些汇编指令获得地址
push
在汇编中
1 | push xxx |
上面的代码意味着将 xxx 入栈,并且esp减4,并且push后esp正好对应xxx的内存地址,于是有如下方法
假设要向栈中放入 /bin/sh 字符串,并且将其地址赋给ebx
1 | ;/sh |
这样以来ebx的值便是 /bin/sh 的地址了
call
在程序执行call时,会将下一条指令的地址入栈然后跳转到要执行的地方,若是程序在栈上执行,则可以利用call获得地址
注:call的字节码为 e8 ab cd ef gh ,其中 ghefcdab 是 要跳转到的地址 - call后执行的下一条指令的地址
还是以 /bin/sh 为例
\xe8\x08\x00\x00\x00/bin/sh\x00
注:我实在不知道该如何表达,这是下面的汇编指令的前面的字节码
1 | pop ebx |
shellcode
由前面的2种确定地址的方式,最终可以写出2种shellcode
注:在这里以linux为例子,这只是最简单的shellcode
使用push
注:本文用pwntools生成字节码
1 | shellcode = asm( |
使用call
1 | shellcode = '\xe8\x08\x00\x00\x00/bin/sh\x00' |
测试shellcode
在编写完成后,有时需要对shellcode进行一些测试,这时有多种方法
C写的shellcode测试器
源码直接给出,忘了从哪看到的了
1 |
|
pwntools
这个貌似只能测试linux下的
例:
1 | from pwn import * |
run_assembly 同理,将参数换为汇编文本即可
以及run_shellcode_exitcode ,run_assembly_exitcode 可以等待 shellcode 执行完毕 返回其返回值
更骚的操作
有时候一些漏洞会对输入进行过滤,此时需要对shellcode进行一些魔改
挖坑待填
最后
最后,便留个思考题吧,如何不用[](){}<>
写一个输出Hello World
的程序呢?
提示:编译时64位机器需要加上-m32
附录
x86系统调用查询:http://syscalls.kernelgrok.com/
x64系统调用查询:http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/