栈溢出几种保护方式

栈溢出主要是通过覆盖栈内保存的函数返回地址eip,然后在EIP处写入shellcode地址做的。


对于栈溢出的保护有很多

1 使用栈保护

比如linux上的 -fstack-protector windows上的 GS开关。

这种处理的方式是。

进入函数时将一个特定的DATA保存在stack 的高位置

[buffer][DATA][ebp][eip]

低位置                高位置  

执行代码

退出时将DATA取出来和原来的进行比较。

扫描二维码关注公众号,回复: 195847 查看本文章

如果想要覆盖高位置的EIP同时也会将栈内的DATA数据修改。

如果DATA被修改,表示溢出了。此时就会执行退出指令。

(简单的是采用保存一个数据,如果这个数据被猜测出来。还是可以通过填充来绕过这个检测

所以有加强版的。就是使用esp和这个值进行xor.由于esp可能每次都不同。所以不容易被猜解。

这种方式的确能够很好保护,避免出现堆栈溢出的状况。


2 ASLR 加载地址随机化

Address Space Layout Randomization

由于前一种需要将固定的地址写入EIP中(如果是跳转到其他模块。要求其他模块必须加载地址固定。如果是执行栈内的代码。必须要求栈的起始地址固定)

这样就造成了不方便。


于是就需要使用ret2reg的技术。主要是指的查看有没有寄存器是和栈地址相关的。

比如执行的时候 寻找是否有 某处的代码 是 call reg  jmp reg (这个地址应该是固定的(比如某个模块的代码))比如call esp call eax之类的。

而这个reg又指向栈内的地址。将这个固定的地址代码写入eip。于是根据call或者jmp的形式又跳转回到了栈内(即使栈地址是不固定的)



3数据执行保护

比如栈内的数据是不可执行的。例如windows开启了DEP。

这样shellcode放在栈内也无法执行。

可以通过 返回到系统函数的方式。

比如执行system("/bin/bash")。

这样只要 在bin里面找到/bin/bash等字符串。或者写入自定义字符串。


有时候system的地址可能是以0结束的。比如0x7fffff00。遇到00就不拷贝了。

这样的话就需要用到 plt或者got了。

什么是plt 和got


#include <stdio.h>
 
void showmsg(char *szMsg)
{
	printf("%s\n", szMsg);
}
 
int main(int argc, char **argv)
{
	char szMsg[] = "Hello, world!";
	showmsg(szMsg);
 
	return 0;
}


对printf的调用被编译器改成了puts@plt,位于0×08048350,这是一个PLT(Procedure Linkage Table)条目,往上翻查看这个地址的代码:

void showmsg(char *szMsg)
{
 8048434:	55                   	push   %ebp
 8048435:	89 e5                	mov    %esp,%ebp
 8048437:	83 ec 18             	sub    $0x18,%esp
	printf("%s\n", szMsg);
 804843a:	8b 45 08             	mov    0x8(%ebp),%eax
 804843d:	89 04 24             	mov    %eax,(%esp)
 8048440:	e8 0b ff ff ff       	call   8048350 <puts@plt>
}
 8048445:	c9                   	leave  
 8048446:	c3                   	ret

08048350 <puts@plt>:
 8048350:	ff 25 04 a0 04 08    	jmp    *0x804a004
 8048356:	68 08 00 00 00       	push   $0x8
 804835b:	e9 d0 ff ff ff       	jmp    8048330 <_init+0x38>

a.out:     file format elf32-i386
 
DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE 
08049ff0 R_386_GLOB_DAT    __gmon_start__
0804a000 R_386_JUMP_SLOT   __stack_chk_fail
0804a004 R_386_JUMP_SLOT   puts
0804a008 R_386_JUMP_SLOT   __gmon_start__
0804a00c R_386_JUMP_SLOT   __libc_start_main


所以需要使用strcpy 将地址进行一个个地拷贝。

具体参看http://blog.csdn.net/linyt/article/details/47429823

pop pop ret 第一次会将strcpy的首次plt 运行修复got 时push的值给pop出来。后续的

strcpy是会拷贝末尾的0的。



猜你喜欢

转载自blog.csdn.net/groundhappy/article/details/70239114
今日推荐