math1as
Binary(pwn)学习笔记之一:基础知识

pwn是二进制安全的一个分方向,其主要目的是挖掘软件中的缓冲区溢出;格式化字符串等漏洞,来达到控制程序流程,甚至任意命令执行的结果

作为开始的一篇,首先还是复习一下binary的一些基础知识,以下是需要用到的资料。

[灰帽攻击安全手册].(美)哈里斯&哈珀

《汇编语言》王爽

Exploit编写系列教程1-10篇

Linux_Interactive_Exploit_Development_with_GDB_and_PEDA_Slides

http://shell-storm.org/shellcode/ 在线的shellcode库

这里首先以简单的栈溢出作为开始吧。

栈溢出的形式多种多样,其中最主要的方式是通过覆盖函数的返回地址,来使程序执行任意地址的指令的方法。

同时,由于现代计算机不区分代码和数据,于是我们还可以构造一段shellcode,直接跳转执行,来实现在程序中植入代码的效果。

由于笔者之前有一定的基础,所以这里比较简略,由于程序运行时每次初始化后的地址都有所不同,覆盖的返回地址不能是某段代码的一个静态的地址(硬编码)。

而最好是一条程序中地址相对固定的,类似于jmp rsp的指令。

这里如果是为了方便快速的寻找,大可不用gdb,直接使用objdump命令

objdump -d test 反汇编test中的需要执行指令的那些section

objdump -D test 与-d类似,但反汇编test中的所有section

对于objdump指令,可以使用|grep xxx管道符来过滤出我们想要的的指令。

对于python来说,我们要方便的生成填充用的buffer和之后的payload,可以直接用python -c "print '$\backslash$x11'*1" 之类的指令来实现

在python中要集成在pwning中的网络通信功能,需要用到zio库,这个库可以在Linux下通过pip来进行pip install zio,如果没有pip,则需要sudo apt-get install pip

参考网页:https://pypi.python.org/pypi/zio

在这其中,zio主要起到的是简化socket编程的作用。

大概的使用方法如下

from zio import * 来导入zio库。

如果你需要在本地进行调试

io = zio('./buggy-server') \# used for local pwning development

如果需要连接到远程服务器

io = zio(('1.2.3.4', 1337)) \# used to exploit remote service

Bigtang给出了更详细的用法

io = zio(target,timeout=5,print_read=COLORED(REPR,’yellow’),print_write=COLORED(REPR,’red’))

这里target就是ip,port. 也就是说后面还可以指定超时的时间和读写的字体颜色。

io.read_until(“input name:\x00″) 可以指定读取的终止部分。

io.write()方法,用于向服务器端传输指定的数据(字符串)

另外,zio内部带有很方便的转换函数l32,l64.可以把你的给的形似0x01020202的内存地址转化为hex编码,并且反序后的地址,这样就可以直接用write()方法了。

如果你通过write方法获取了一个交互shell,使用io.interact()来进入这个shell。

GDB:在linux下,唯一真正可靠的动态调试工具,大概就是gdb了,自带了disassemble的功能,可以查看到汇编源码,美中不足是没有GUI,而且指令较为复杂。

BT命令,可以查看当前使用的栈和其地址。另外,如果需要下断点,使用break 函数名,如break main,来对main()函数下断

如果要对某个内存地址下断点,使用break *内存地址来下断点,同时,后面还可以跟上条件语句,如 break *01010101 if x==1,这里如果有一个局部变量x,就可以进行判断

gdb查看指定地址的内存地址的值:examine 简写 x 用法:x/(n,f,u为可选参数) 内存地址(或者\$寄存器名称)

其中n是整数,表示从x开始到后面取几个内存单元的内容加以显示,每个内存单元的大小由u决定,u=b:1 byte h:2 bytes w:4 bytes g:8 bytes (均以字节为单位,1byte=2bits

f是显示的字符格式,x是16进制,c是字符串(char),d是10进制,i可以显示出反编译的汇编代码。

查看寄存器的指令,使用i r \$寄存器名称,或者是i r a来查看所有寄存器。

用r运行该程序 同时后面可以跟上参数python -c ' '来把输出的内容应用到程序的输入流上。

使用set disassembly-flavor intel可以把反汇编的结构显示为intel风格

set variable 变量名= 或者*(地址)= 可以修改内存的数值

使用metasploit的pattern_create.rb来精确计算缓冲区的大小,其原理是生成一个有序字符串,根据最终EIP被冲刷后的结果,来确定缓冲区的大小。

该工具位于framework\tools目录下,使用方法是 ./pattern_create.rb 字符串大小(例如5000)

最终通过./pattern_offset.rb (EIP地址)0x356b4234 5000 来计算出最终的buffer大小。

如果buffer过大,其实可以先大致的确定其大体部分,再把这个模型附加在后面,会更省时间和计算量。

//在gdb下,由于无法像od那样每步跟踪,所以对于rip来说无法直接观测到被覆盖后的结果。因此我们需要直接对rbp进行观察,计算出了rbp之前的buffer后,就可以加上rbp的长度,然后继续溢出

peda可以方便的让你查看寄存器的信息和其他数据,它也是用python编写的,而且直接附加到gdb。

可以用jmpcall来搜寻一些跳板指令,如jmpcall rsp