一、pwntools常用函数
函数 | 用途 |
---|---|
send(data) | 发送数据(字符串形式发送) |
sendline(data) | 发送一行数据,默认在行尾加 \n(字符串形式发送) |
recv(numb=1096,timeout=default) | 接收指定字节数的数据 |
buf = p.recvuntil(“\n”, drop=True) | 直到接收到\n为止,drop=True表示丢弃\n,buf为接收到的输出但不包括丢弃的\n |
recvrepeat(timeout=default) | 接收数据直到 EOF 或 timeout |
recvall() | 接收数据直到 EOF |
recvline(keepends=True) | 接收一行,可选择是否保留行尾的 \n |
listen(端口) | 开启一个本地的监听端口 |
remote(‘IP地址’, 端口) | 与目标IP建立一个套接字管道与之远程交互(在线的) |
interactive() | 可同时读写管道,相当于回到 shell 模式进行交互,在取得 shell 之后调用 |
p8() p16() p32() p64() | 把括号内数据打包成8位/16位/32位/64位的二进制数 |
u8() u16() u32() u64() | 把括号内字符串解包成二进制数 |
process(‘文件路径’),p = process(argv=[‘./vuln’, payload]) | 与本地文件建立一个交互通道,可传递参数 |
sendlineafter(“string”,payload) | 接收到string后发送payload |
close() | 关闭交互的通道 |
ELF(‘文件路径’) | 获取文件对象或者libc库对象 |
plt[‘函数名’] | 获取函数在PLT表中的地址 |
got[‘函数名’] | 获取函数在GOT表中的地址 |
symbols[‘函数名’] 或sym[‘函数名’] |
获取函数plt地址,用在libc里面就是获取libc里的偏移地址 |
asm(“汇编指令”) | 把汇编指令转换成对应的机器码,机器码是以字符串形式返回 |
bss(offset) | 返回 .bss 段加上 offset 后的地址 |
asm(shellcraft.amd64.linux.sh(),arch=‘amd64’)(这里是指明x64环境,如果已经有context设置环境就直接写为asm(shellcraft.sh())) | 生成shellcode,一般与asm进行联用,转为对应机器码 |
context(arch='amd64’或‘i386’,os=‘linux’,log_level=‘debug’) | 设置环境 |
gdb.attach(p,‘b* main’) | 调动gdb进行脚本调式 |
栈溢出
一、x86架构下的栈溢出
栈区:简单来说就是c语言中创建的局部变量(例如函数花括号里的变量)的存储位置。内存中的栈区指的是系统栈,由系统自动维护。
栈在程序加载进内存后就会出现
入栈:每个函数都有一个属于自己的栈帧空间,最先压入栈内的是函数的返回地址(用来返回到下一条指令),之后是函数的基地址、参数入栈。例如主调函数在调用函数a时,主调函数先存入自身栈帧的为返回地址、自身基地址、传入函数a的实参(如果有的话),然后替函数a创建一个新栈帧,在新栈帧中先压入函数a的返回地址(为了函数调用结束时,可以返回到下一条指令继续程序),再压入主调函数的基地址(函数调用结束时,返回到主调函数的基地址)以及函数a中的局部变量。此时是高地址往低地址生长(主调函数在的位置为高地址)
退栈:而函数调用结束,则与调用时相反,先从被调函数的局部变量开始直接弹出栈,栈顶指向存储着被调函数基地址(存储主调函数基地址),被调函数基地址被弹出后,释放出主调函数的基地址给ebp,然后将返回地址弹出交给eip去执行,之后便返回到下一条指令的地址(这里举例为主调函数直接调用一个函数,如果层层嵌套,则先会返回到上一个主调函数的栈帧),继续程序的运行,ebp指向此刻的主调函数的基地址
系统中当前正在运行的函数总是在栈顶
与函数状态相关的主要寄存器:esp,ebp,eip:
(1)esp:栈指针寄存器,存放一个指针,该指针永远指向系统栈正在运行的栈帧的栈顶(存储函数调用栈的栈顶地址),在压栈和退栈时发生变化
(2)ebp:基地址指针寄存器,该指针永远指向系统栈正在运行的栈帧的底部,在函数运行时不变,可以用来索引确定函数参数或局部变量的位置
(3)在esp和ebp之间的内存空间为当前栈帧
(4)eip:用来存储即将执行的程序指令的地址,cpu 依照 eip 的存储内容读取指令并执行,eip 随之指向相邻的下一条指令
因此,所谓的栈溢出漏洞,就是利用一些危险函数进行读取远超一个变量所需的数值,覆盖到相邻栈中的数值,从而修改相邻栈中的变量的值,往这些修改的值中注入我们所需要的跳转的例如shellcode,函数地址等,使程序崩坏或是让程序执行一些我们想要执行的程序,达到破坏的目的
x86下的CPU包含的8个四字节的通用寄存器:
寄存器使用约定:寄存器eax、edx和ecx为主调函数保存寄存器(caller-saved registers),当函数调用时,若主调函数希望保持这些寄存器的值,则必须在调用前显式地将其保存在栈中;被调函数可以覆盖这些寄存器,而不会破坏主调函数所需的数据。寄存器ebx、esi和edi为被调函数保存寄存器(callee-saved registers),即被调函数在覆盖这些寄存器的值时,必须先将寄存器原值压入栈中保存起来,并在函数返回前从栈中恢复其原值&#