pwn前置知识

pwn前置知识

在无聊时突然发现以前写的,写的很烂,想删掉,算了,还是放到博客了吧,其实以前还有写的好多文章,但是那时没有博客,就写的很随意, 以后如果有时间就整理出来,这篇算是水出来的

*ELF*

*elf文件的基本概念*

elf文件是一种用于二进制文件,可执行文件,目标代码,共享库和核心转储格式文件

简单理解 :一种二进制格式

目的 :提供一组二进制接口,这些接口可以延伸到多种操作系统中,从而减少重新编码,编译程序的需要(接口是一种用来定义程序的协议,它描述可属于任何类或结构的一组相关行为。也就是一组规则的集合)

*作用*

ELF文件参与程序的链接(创建一个程序)程序的加载(运行一个程序)

*ELF文件分类*

1、ELF header: 就相当于是一个总管,它决定了这个完整的 ELF 文件内部的所有信息。
2、Program Header Table: 描述文件中的各种segments,用来告诉系统如何创建进程映像的。 (readelf --segments app)
3、sections 或者 segments:segments是从运行的角度来描述elf文件,sections是从链接的角度来描述elf文件,也就是说,在链接阶段,我们可以忽略program header table来处理此文件,在运行阶段可以忽略section header table来处理此程序(所以很多加固手段删除了section header table)。从图中我们也可以看出, segments与sections是包含的关系,一个segment包含若干个section。
4、Section Header Table: 包含了文件各个segction的属性信息, ​

*首先,ELF文件格式提供了两种视图,分别是链接视图和执行视图。*

imgimg

*链接视图是以节(section)为单位,执行视图是以段(segment)为单位。链接视图就是在链接时用到的视图,而执行视图则是在执行时用到的视图。上图左侧的视角是从链接来看的,右侧的视角是执行来看的。总个文件可以分为四个部分*

img

*段表*

*内容*

段表就是保存ELF文件中各种各样段的基本属性的结构。段表是ELF除了文件以外的最重要结构体,它描述了ELF的各个段的信息,ELF文件的段结构就是由段表决定的。*编译器、链接器和装载器都是依靠段表来定位和访问各个段的属性的。*段表在ELF文件中的位置由ELF文件头的“e_shoff”成员决定的,比如SimpleSection.o中,段表位于偏移0x118。

img

每个段的名字 长度 在文件中的偏移 读写权限

img

img

*下面讲几个重要的表*

*符号表(.dynsym)*

符号表包含用来定位、重定位程序中符号定义和引用的信息,简单的理解就是*符号表记录了该文件中的所有符号*,所谓的符号就是经过修饰了的函数名或者变量名,不同的编译器有不同的修饰规则。

*重定位表*

*作用*

重定位表在ELF文件中扮演很重要的角色,首先我们得理解重定位的概念,程序从代码到可执行文件这个过程中,要经历编译器,汇编器和链接器对代码的处理。然而编译器和汇编器通常为每个文件创建程序地址从0开始的目标代码,但是几乎没有计算机会允许从地址0加载你的程序。如果一个程序是由多个子程序组成的,那么所有的子程序必需要加载到互不重叠的地址上。重定位就是为程序不同部分分配加载地址,调整程序中的数据和代码以反映所分配地址的过程。简单的言之,则是将程序中的各个部分映射到合理的地址上来。 换句话来说,重定位是将符号引用与符号定义进行连接的过程。例如,当程序调用了一个函数时,相关的调用指令必须把控制传输到适当的目标执行地址。 具体来说,就是把符号的value进行重新定位。

*重定位的需求*

总结一下,对于Linux而言,0X400000以下的空间默认不映射,从而起到保护程序安全的作用。对于windows而言,程序安全交由操作系统保证,因此最大限度利用资源,地址可以低到0X400000以下。

在生成程序的时候,很多涉及地址的代码,都使用一个绝对的虚拟内存地址,(这个虚拟内存地址假设程序加载到0x400000的地方才能使用)但当程序的加载基址发生变化时,新的加载基址和默认加载基址就不一样了那些涉及到地址的代码就不能用了,此时就需要把那些涉及地址的代码,把他们的操作数修改(去掉默认加载基址,再加上新的加载基址)才能使程序运行起来。

rel.text是针对.text断的重定位表

*字符串表*

ELF文件中用到很多字符串,比如段名,变量名,

一般字符串表在ELF文件中也以段的形式存放,一般是.strtab .shstrtab 这两个是字符串表和段字符串表,

字符串表用来存放普通的字符串比如符号的名字

段字符串表用来段表中的字符串,比如段的名字

什么是链接

就好比我们在写个C语言程序时调用了scanf函数,但我们并没有去写这个函数,这是用为存在一个库里面放了许多类似于scanf的基础函数,而链接的作用就是把我们写的函数和它所需要的函数库文件关联起来,也就是将几个输入目标文件加工后合并成一个目标文件

为什么要链接

便于管理

因为我们不可能把所有的代码放到一个源文件中,(不容易查找错误和更新),所以会出现多个源文件,而且多个源文件之间不是独立的,而会存在多种依赖关系,如一个源文件可能要调用另一个源文件中定义的函数,但是每个源文件都是独立编译的

下面讲一下两种链接方法

静态连接

简单地说就是把所有函数库文件在运行前与你写的函数链接起来(无论你是否调用它),这种方法比较占内存

*函数库文件*

组目标文件的集合,即很多目标文件经过压缩打包后形成的一个文件(一个目标文件就是一个函数)

*动态链接*

为什么要动态链接

*应为随着程序的开发,代码量变的庞大,静态连接的不足之处开始出现:浪费内存,板块的更新*

动态链接如何解决上述问题

动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。

就比如,程序相当于一个厨房,模块是食材。静态连接的做法是在做饭时(运行程序)把所有的食材都放到厨房,当你想做饭找食材时需要从一大堆东西中找。

动态链接则是把食材放到另一个屋子中,需要时才把它带进厨房,并记下它在另一个屋子的地址,以便下一次去拿知道位置(这也是延迟绑定)

*延迟绑定*

基本思想是当函数第一次被调用时,动态连接器才开始进行符号查询,重定位等操作,如果未调用,则不进行绑定

*elf文件保护机制*

*NX*

NX即堆栈不可执行的意思,NX(DEP)的基本原理是将数据所在内存页标识为不可执行,当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令就类似linux中的w,r,x的中的x(执行权限没有了)

绕过的方法调用函数恢复栈中的权限, 或者rop

*Canary*

*防御机制*

Canary是针对栈溢出的一种防护机制。看着下面的图简单讲一下

img

就是我们在利用栈溢出时在ebp上面不远的地方有个叫canary的随机数,它是在程序运行时产生的,并备份一份在一个寄存器中,在执行返回地址前会进行比较。如果不一样则不会跳转。也就是说我们在栈溢出的时候不能改变canary的值,(我要知道这个值,在覆盖时在这个位置填这个值)

绕过的话

一、是通过覆盖最低位字节然后打印出来

二、用one--by—one(虽然每次进程重启后Canary不同,但是同一个进程中的不同线程的Cannary是相同的,并且通过fork函数创建的子进程中的canary也是相同的,因为fork函数会直接拷贝父进程的内存。)通过一位一位的比较

也有是分析程序,按照一定的操作也行(很少)

*RELRO*

*防御机制*

有下面三中模式

partial relro(部分开启,got 可写)

full relro(全部开启,got 不可写)

NO RELRO(不开保护)

*ASLR*

*防御机制*

ASLR (Address space layout randomization,地址空间布局随机化)是 Linux操作系统的功能选项,作用于程序(ELF)装入内存运行时。是一种针对缓冲区溢出的安全保护技术,通过对加载地址的随机化,防止攻击者直接定位攻击代码位置,到达阻止溢出攻击的一种技术。

分为三个级别

· 0 :不开启任何随机化

· 1 :开启stack和libraries随机化。开启PIE的情况下,executable base也会随机化。

· 2 :开启heap随机化

*PIE*

*防御机制*

PIE 是 gcc 编译器的功能选项,作用于程序(ELF)编译过程中。是一个针对代码段( .text )、数据段( .data )、未初始化全局变量段( .bss )等固定地址的一个防护技术。ASLR 不负责代码段以及数据段的随机化工作,这项工作由 PIE 负责。但是只有在开启 ASLR 之后,PIE 才会生效。如果程序开启了PIE保护的话,在每次加载程序时都变换加载地址,从而不能通过 ROPgadget 等一些工具来帮助解题。 绕过方法

虽然程序每次运行的基址会变,但程序中的各段的相对偏移是不会变的,只要泄露出来一个地址,比如函数栈帧中的返回地址,通过静态分析的获取偏移地址,就能算出基址,从而实现绕过。

好吧就是一篇水文章

posted @ 2022-11-09 15:55  何思泊河  阅读(68)  评论(0编辑  收藏  举报