前言
上来先道歉,对不起(>人<;)对不起,博客鸽了好久。私下有好多朋友问我毕业工作的事情,毕竟搞二进制最重要的是要有热情!我能做的也是有限,每个人的学习方式不完全相同,所以想开一个面试题解析的专题。本专题的初衷并不是为了应试面试,而是根据面试题差缺不漏,看看自身有哪些知识点还没有掌握~
编写不易,如果能够帮助到你,希望能够点赞收藏加关注哦Thanks♪(・ω・)ノ
PS:文章末尾有联系方式,交个朋友吧~
本文链接:https://hollk.blog.csdn.net/article/details/128591001
防爬链接,未经允许请勿转载
文章目录
- 问题1:linux下有哪些常见保护机制
- 问题2:Linux下保护机制原理是什么
- Canary保护
- NX保护
- ASLR保护
- PIE保护
- 问题3:这些保护机制有哪些绕过方式
- Canary保护
- NX保护
- PIE保护和ASLR保护
- 问题4:你知道的函数调用约定有哪些
- 问题5:stdcall、cdecl、fastacall有什么区别
问题1:linux下有哪些常见保护机制
这个问题如果是pwn手的话应该是张口就来,常见保护机制有Canary
、NX
、ASLR
、PIE
等,这些保护机制的介绍及开启关闭方式在我的《堆栈保护机制》这篇文章中有详细记录,可以直接点击蓝色字跳转
问题2:Linux下保护机制原理是什么
Canary保护
栈溢出的基本原理是,通过向栈中输入超过局部变量内存空间的数据,导致数据覆盖至BP指针
、IP指针
等,达到劫持程序控制流的效果。所以人们会想如果数据超过了内存限制的边界,是否可以进行一个预警,或者直接让程序停下来不造成进一步的破坏。
Canary保护开启后,首先会由security_init
函数初始化当前栈TLS
中stack_guard
成员变量的随机数值:
security_init (void)
{
// 将_dl_random的最后一个字节设置为0x0,_dl_random随机数在进入函数之前由kernel写入
uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random);
// 设置随机数到stack_guard中
THREAD_SET_STACK_GUARD (stack_chk_guard);
_dl_random = NULL;
}
接下来在fs寄存器0x28的位置进行取值,fs:0x28
指向的是当前栈TLS结构stack_guard
typedef struct
{
void *tcb;
dtv_t *dtv;
void *self;
int multiple_threads;
uintptr_t sysinfo;
uintptr_t stack_guard;
...
} tcbhead_t;
函数在开始执行前会向%ebp-0x8
的位置插入一条类似于cookie
的信息,这个cookie信息就是取自fs寄存器0x28处的值,插入汇编如下:
mov rax, qword ptr fs:[0x28]
mov qword ptr [rbp - 8], rax
当函数执行返回前,会取出这个cookie与fs寄存器0x28处的值进行异或
处理,如果结果为0
,证明canary没有
被覆盖或篡改,汇编检验如下:
mov rdx,QWORD PTR [rbp-0x8]
xor rdx,QWORD PTR fs:0x28
je 0x4005d7 <main+65>
call 0x400460 <__stack_chk_fail@plt>
由于跳转使用了je
指令,当异或操作为1
时即canary的值已经被篡改
,将不会跳转值main函数,而是执行__stack_chk_fail
。需要注意的是__stack_chk_fail函数也是glibc中的,这就意味着动态链接的情况下也遵循绑定延迟机制
NX保护
禁用栈空间执行权限,防止跳转至栈中执行shellcode,这个没什么好说的,利用shellcode的前提是shellcode所在段具有执行权限,NX直接在栈区一刀砍掉
ASLR保护
ASLR开启后,堆、栈、共享库映射区域地址进行随机化处理,个人认为只不过是起始地址发生变化导致后续线性布局一起进行随机化,也就是说线性布局前后相邻偏移量是不变的,linux内核具体实现可以参考这篇文章:
Linux kernel ASLR Implementation
PIE保护
由于ASLR保护仅仅只对栈、堆、mmap、动态链接库映射内存区域进行了随机化,他管不着代码段、数据段、bss段,这三个部分的随机化就由PIE保护来做。PIE生效的前提是ASLR开启哦!
一个程序本身的地址是不支持随机化加载的,但是PIE保护的实现是在编译器层面实现的(有pie编译选项,ASLR可以在编译后开启关闭),PIE默认是开启状态,在PIE下编译出来的程序叫做位置无关代码(可以在我的《程序员自我修养学习心得》系列找到)
PIE编译后,我们可以在ida看到的代码段的样子就像上图一样,只会有一个低地址位的偏移,这样一来程序就可以通过随机高地址位加载到任意位置。PIE和ASLR的结合使得内存像浆糊一样,但是根据ISO三原则:保密性、完整性、可用性,三者其中一项提高,势必会影响另外一项或两项的降低,因此PIE会影响性能,实际工作中不开启的情况还是有的
问题3:这些保护机制有哪些绕过方式
Canary保护
从上面的canary整条流程进行梳理,从头到尾的顺序应该是security_init初始化TLS中的stack_guard
–>取出fs:0x28处stack_guard随机数值插入%ebp-0x8位置
–>函数执行后重新提取canary值并与fs:0x28做异或
–>如果异或为1(已篡改)则执行__stack_chk_fail函数
姿势一:泄露canary
条件:PIE关闭、程序可以循环输出每一个输入字节
因为在security_init中设置随机数结尾为0x0,这样设置是为了保证canary可以以\x00为结尾,确保可以截断字符串,那么在溢出的时候就可以首先覆盖canary的低字节,通过覆盖canary最后一个\x00字节来打印出canary信息,计算好到栈中bp偏移后直接将打印出来的内容覆盖至canary存放位置(覆盖了,但还是原来的值)
姿势二:canary爆破
由于canary的值为随机数,并且会随着进程的重启发生变化,但是同一个进程的不同线程或fork函数创建的子进程中canary是不发生改变的,利用这一原理可以进行canary按位爆破
姿势三:劫持__stack_chk_fail函数
前面说过,在篡改canary中的之后,程序将会进入__stack_chk_fail函数流程。但__stack_chk_fail函数遵循绑定延迟机制,所以可以篡改__stack_chk_fail函数的GOT表执行其他操作
姿势四:修改TLS中存储的Canary值
由于函数结束前会通过fs:0x28的方式重新对TLS中的stack_guard取值,当溢出长度很长的时候,可以同时覆盖Canary和TLS存储
NX保护
ROP就可以,NX主要防的是shellcode,而gadget本身就处于代码段,属于程序内部执行代码
PIE保护和ASLR保护
姿势一:shellocde
这个姿势需要一个前提就是NX保护要关闭(做题情况可能很少,但是实际环境NX可能不开启),使用C语言编写exp,使用execl()函数执行shellcode
姿势二:构造ROP泄露内存地址
前面说过虽然进行了随机化处理,但是类似于动态链接库映射类内存,相邻两个函数真实地址的偏移是不变的。因此构造ROP首先leak出某个函数的真实地址,然后进行推算执行函数地址。进程不停,地址不会发生新的变化!
问题4:你知道的函数调用约定有哪些
stdcall、cdecl、fastcall、thiscall、naked call
问题5:stdcall、cdecl、fastacall有什么区别
- stdcall
- 参数从右向左入栈
- 函数内部进行堆栈平衡
- 编译修饰约定:下划线 + 函数名 + @ + 总参数字节数
- cdecl
- 参数从右向左入栈
- 函数调用者平衡堆栈
- 编译修饰约定:下划线 + 函数名
- fastcall
- 第一个和第二个小于等于DWORD长度的参数通过ecx和edx传递,其他参数从右向左入栈
- 函数内部进行堆栈平衡
- 编译修饰约定:@ + 函数名 + @ + 总参数字节数
以上就是这期的五个问题,我的回答一定会有一些不足之处,欢迎小伙伴们评论补充哦!编写不易,如果能够帮助到你,希望能够点赞收藏加关注哦Thanks♪(・ω・)ノ
交个朋友吧~
扫描下方二维码,一起讨论研究👇👇👇