Pwn学习笔记-Linux下ELF文件的保护机制

一、 canary保护

1、作用:

(1)函数开始执行的时候会先往栈里插入一段canary值,当程序真正返回的时候会验证cananry值是否合法,如果不合法就停止程序允许

(2)这样在覆盖ebp之前时会进行一个验证,验证canary是否准确,如果不准确那么它就会跳转到一个叫_stack_chk_fail的函数当中,就会大致栈溢出覆盖返回地址

2、编译选项:

  • 关闭:-fno-stack-protector

  • 启用(只为局部变量中含有char的函数插入保护代码):-fstack-protector

  • 全启用(为所有函数插入保护代码):-fstack-protector-all

在这里插入图片描述

3、实例解析

下图是开启Canary保护 的一个程序 的反汇编的main函数 的代码,
image-20220720183143132

首先可以看到把 fs段的0x28 先存放到rax当中 然后保存到[rbp-0x8]里面。rbp-0x8是什么?它不就是的就在rbp上面一个的一个位置吗,如果你的填充了溢出的话是就会导致rbp-8位置的数值发生了改变。

在出栈的时会进行一个判断:它将之前存放到[rbp-0x8]里面的内容存放到rdx当中,与之前的fs段的0x28进行xor异或判断,如果相等的话就执行je跳转到<main + 94>,如果不相等就会执行call指令,就会跳转到_stack_chk_fail函数当中。

关于跳转的过程(xor 和 je指令)再在这详细谈一谈
xor命令:异或,如果a、b两个值相等则为0,不同则为1
je指令:只在 ZF = 1的时候才会跳转,跳转到leave指令当中,才能避免执行call指令


在这里插入图片描述
ZF又是什么?上面是eflag标准寄存器的低16位的结构,每一个标志位都有其专门的含义。ZF是其中的的一个标志位==>0标准位,作用是判断前面那条指令是否为0,若为0则为真即ZF = 1,若不为0则为假即ZF = 0。

结合前面的所以内容可以得到的是:
在这里插入图片描述

另外再给出一种确定ZF标志位的值的方法:

查看执行xor命令以后的标志位寄存器的数值,然后将该数值转换成二进制,ZF标志位在第七位上,然后看该二进制的第七位是0还是1。(在gdb中用 i r 命令查看寄存器里面的内容)。同样也可以得到相同的结果。
这是正常情况下的执行了xor命令后的eflag寄存器的情况,
在这里插入图片描述

在这里插入图片描述
可以发现第七位的结果为1,所以ZF = 1,可执行je指令

这是溢出并修改了Canary值以后的标准寄存器:
在这里插入图片描述
在这里插入图片描述
可以发现ZF = 0,所以就会导致je不跳转,执行call命令。

常见的绕过方式:

(1)格式化字符串漏洞

(2)smashing

(3) 劫持_stack_chk_fail函数

二、 NX保护

1、作用:

将数据(栈、堆)所在页面标识为不可执行, 当程序溢出成功转入shellcode时,程序会尝试在数据页面上执行指令,此时CPU就会抛出异常,而不是去执行恶意指令

2、编译选项:

  • 开启:-z noexecstack
  • 关闭:-z exestack

3、 常见的绕过手段

(1)可以将构造的shellcode写到bss段(具体原因暂未知)

(2)可以利用ret2libc的方式,通过泄露某个函数的地址获取以此最终获取system函数的地址来getshell

三、 PIE保护:

1、作用:

是得程序地址空间分布随机化,增加ROP等利用的难度。(因为地址都是不确定的)

例如:ROP技术需要一些gadget,而这些gadget是需要它实际的地址的。像rdi、rbp等寄存器的地址、像一些函数的地址都无法获得。

2、编译选项

  • 开启:-pie-fPIC
  • 关闭:-no-pie

3、常见的绕过手段

四、 Fortify保护

1、作用

主要用来防止格式化字符串漏洞的利用

(1)包含%n的格式化字符串不能位于程序内存中的可写地址。

说明:如果想要使用,必须在程序编写时写好,比如你写在一个全局变量上固定写好,这段位置你不能写

(2)当使用位置参数时,必须使用范围内的所有参数,如果要使用%7$$x,必须同时使用前面的1$,2$,3$,4$,5$和6$。

说明: 存在格式化字符串漏洞,但是限制的输入比较短,而你的格式化字符又比较长

五、 RELRO保护:

(1)作用 :

(1)设置符号重定表位只读并在程序启动时就解析并绑定所有动态符号。 这样就会导致GOT表所在的区域不可写,能减少GOT表的攻击

(2)常见的解题中会存在将一些函数的got表修改为system函数的地址,这样当执行该函数时就会执行system函数。但是仍然可以作信息泄露

(2)编译选项

  • 开启部分:-z lazy (partial)

    (1)当程序运行之后,执行到了相应的got表以后,它才会解析相应的内容。 利用的就是dl_runtime_resorlve函数

  • 开启全部:-z now


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮箱至 1627319559@qq.com

×

喜欢就点赞,疼爱就打赏