动态链接和静态链接
静态链接和动态链接的相关概念,我们在栈溢出篇曾粗略提到过
我们说到,为了防止每次调用函数都需要将函数的libc库拷贝到文件中,加大文件的存储占用
不如在程序运行时将动态库加载到内存中,多个程序运行只需要调用一份
静态库特点:
1.静态库对函数库的链接是放在编译时期完成的
2.程序在运行时与函数库再无瓜葛,移植方便。
3.浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件
4.如果静态库进行更新则应用该库的所有程序都需要重新编译(全量更新)。
动态库特点:
1.动态库把对一些库函数的链接载入推迟到程序运行时期。
2.可以实现进程之间的资源共享。(因此动态库也称为共享库)
3.将一些程序升级变得简单。
4.甚至可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)
还是不太清楚?我们这样来理解,我们在吃饭前需要备好碗筷
如果我们每吃一道菜就要准备一副碗筷,这是静态库
如果我们只用一副碗筷就吃完所有菜,这是动态库
ROP
在明白了动态库和静态库的区别后,我们接着讲栈溢出的相关内容
我们已经学过的栈溢出,需要函数中有着出题人给我们预先准备好的system函数和binsh字符串
如果没有呢?我们又该从何入手
在开始之前,我们先明白几个概念
1、rop:在栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程。
2、gadgets:在程序中的指令片段,有时我们为了达到我们执行命令的目的,需要多个gadget来完成我们的功能。gadget最后一般都有ret,因为我们需要将程序控制权(EIP)给下一个gadget。即让程序自动持续的选择堆栈中的指令依次执行。(涉及到接下来的栈变化情况,现在看不懂的没关系,接下来会图片演示详讲)
3、ropgadgets:一个pwntools的一个命令行工具,用来具体寻找gadgets的。例如:我们从pop、ret序列当中寻找其中的eax
4.在linux系统中,函数的调用是有一个系统调用号的
我们接下来详细介绍这个系统调用号
系统调用号
Linux 的系统调用通过 int 80h 实现,用系统调用号来区分入口函数
而我们需要用到的system(/bin/sh)函数的系统调用号为11,也就是0xb
他的系统调用规范是execve(“/bin/sh”, 0,0)
那么我们如何向栈中传入这一串的数据来达到我们调用system的目的呢?
计算机语言分为高级语言和低级语言
我们先要知道,像c语言,python语言这样的,是属于高级语言
高级语言指较接近自然语言和数学公式的编程,基本脱离了机器的硬件系统,用人们更易理解的方式编写程序
低级语言包括机器语言(二进制语言)和汇编语言
这两种语言都是面向机器的语言,和具体机器的指令系统密切相关
所以我们输入的是system(/bin/sh),但是实际上计算机他不能读懂这段代码是什么意思
他要先进行编译,将其转化为自己能读懂的汇编语言
所以system(/bin/sh)用汇编语言来表示也可以转化为
pop eax 系统调用号载入, execve为0xb |
仔细思考一下,静态链接是将静态库在程序运行前就载入于文件中
所以,我们有了思路,既然静态链接导致的后果是文件过于庞大,因为其加载了静态库
所以我们可以在程序文件中搜索特定的汇编语言,将其一条条串连起来
这里我觉得有必要再插入一段说明,关于这个串连
我们是如何进行栈溢出的,实际上是覆盖ret addr来达到我们控制程序执行流的目的对吧
所以我们只需要确保每一个汇编代码执行完了以后都有一个ret指令,我们就可以使程序执行流按照我们预想的方式来跳转
那么我们又该如何寻找这些汇编代码呢?这里就用到了我们刚才提过的ropgadgets工具
ropgadgets
我们在linux终端中输入
ROPgadget -binary 文件名 --only" 要搜索的 " |
于是,我们得到了 pop eax/ebx 的地址
这里是是否还有一个疑问?欸他这里是连在一起的啊,就比如第一行pop eax和pop ebx是连在一起的怎么办?
很简单,我们只需要同时传入他们各自所需要的参数即可
我们再搜索ebx.ecx.edx 以及int 80h系统调用和bin/sh
到这里为止,我们就清楚了rop构造执行流的完整思路,接下来我们用一道例题来演示
ret2syscall
我们先用checksec检查他开启了哪些保护机制以及查看他的位数
打开了NX保护机制,我们无法在栈中读写数据,所以只能采用构造ROP执行流的办法
再用ida打开看一下main函数写了什么
可以看到gets函数,判断应该也是一个栈溢出题
但是没有给我们提供system函数地址和字符串binsh地址
所以我们这里利用ROP构造
我们来查找pop eax等汇编代码的地址
得到了地址之后,我们开始构造payload