栈知识、部分保护措施
GDB显示的栈地址有时候并不是可靠的地址,gdb也是用特殊的进程映像来拿地址的。且gdb默认关闭栈地址随机化。但是,偏移量是没有错误的。目前还没学到咋解决
第一个栈帧是main函数栈帧,之前的一些系统函数什么的没有栈帧。
执行了leave之后的情况:会pop ebp,所以esp+1,然后ebp恢复原来样子了。然后会执行retn,那就是将返回地址相当于pop给eip寄存器。
canary(金丝雀)保护措施,在 ebp下面会有一个值,创建栈帧的时候就有了。如果销毁栈帧时检测到这个值被修改了,那么他就会崩溃退出。可以在gcc编译的时候打开
2个16进制数字代表1个字节
64位一个ebp是8字节,32位是4
ret2shellcode题目讲解
1.动调拿到溢出字节数
需要注意,如果栈存放的是指针,这工具会把指针的数据打印出来。所以esp实际上存的是指针,不是我们想要溢出的字符串。
2.写exp
payload一定要有shellcode(arm后的),但是返回地址需要考虑一下,最好不在栈上做执行,因为有太多保护措施。观察软件main函数会对一个全局变量赋值,那就在bss段进行执行吧。
ljust(填充前方字节流长度到几,填充数据),这是个函数,可以这样写:
arm(shellcraft.sh()).ljust(目标长度,b'A')。
有时候需要recv完毕再send不然会IO错误。
返回导向编程
ret2syscall
本质上是操作空间内核里面的代码。
在举例中,my_puts是用户函数,其中调用了函数write。eax是传入系统调用号。在动态链接库的作用下,write对应的汇编代码进行实现功能,调用更加底层的系统函数/接口之类。
ldd命令可以查看一个可执行程序的所有动态链接库,如ldd a.out
需要关注libc.so.6,记录着c语言标准动态链接库的软连接。(记录软连接在更新动态链接库的时候更加便捷)就在lib文件夹里面 。有时候会出现本地打不通,远程打通,或者反过来,可能原因之一就是动态链接库版本不同了,这种的排错还是比较困难的。
动态链接库也是elf文件,有位置无关代码编写技术 ,这个技术被PIE借鉴了。
动态链接库会载入在shared那个区域。ret2libc的目的就是把程序从text跳转到shared libraries。
execve就是上面libc.so包装在system里面的一个系统调用。
int 0x80就是一个中断号,代表系统调用。再读取eax里面存的调用号。
但是程序没有这样连续的片段,其实将间断的片段(gadget)组合起来也可以达到这样的效果,就是ROP。
执行过程
详见视频P3的2:27:10左右。需要注意ret的效果和EIP的作用,以及pop了esp会移动的。
利用ret控制eip,用pop eax可以直接把想要的值覆盖在栈上。
做题
2:47:28左右
运气好部分寄存器值已经是对的,可以不用pop什么了
用drawio工具,看栈的变化,进一步理解ROP过程以编写payload
只是了解:popad
按 edi
、esi
、ebp
、esp
(原栈指针,不过该值不存回 esp
)、ebx
、edx
、ecx
、eax
顺序弹出。