• 堆溢出利用


    1. 堆的数据结构

    堆表索引空闲态堆块,重要的堆表有两类:空表、快表。我们通过下面代码练习识别堆表、堆块:

    #include <windows.h>
    main()
    {
    	HLOCAL h1,h2,h3,h4,h5,h6;
    	HANDLE hp;
    	hp = HeapCreate(0,0x1000,0x10000);//InitialSize:0x1000, MaxSize:0x10000, 成功创建堆区后,会把整个堆区的起始地址返回给eax
    	__asm int 3
    
    	h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,3);//将分配的内存全部清零
    	h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,5);
    	h3 = HeapAlloc(hp,HEAP_ZERO_MEMORY,6);
    	h4 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
    	h5 = HeapAlloc(hp,HEAP_ZERO_MEMORY,19);
    	h6 = HeapAlloc(hp,HEAP_ZERO_MEMORY,24);
    	
    	//free block and prevent coaleses
    	HeapFree(hp,0,h1); //free to freelist[2] 
    	HeapFree(hp,0,h3); //free to freelist[2] 
    	HeapFree(hp,0,h5); //free to freelist[4]
    	
    	HeapFree(hp,0,h4); //coalese h3,h4,h5,link the large block to freelist[8]
    
    	
    	return 0;
    }
    

    Release版运行,跳入OD调试界面:

    堆区起始地址0x3A0000.
    当一个堆刚被初始化时,它只有一个空闲的大块(“尾块”),而它被记录在了空表的第0项。除了第0项外,其余各项空表索引均指向自己。
    空表索引区位于偏移0x178处

    可知尾块地址为0x3A0688(注意这个地址是尾块数据区地址;如果启用快表,这个地址将是快表。想要启动快表,可将代码hp = HeapCreate(0,0x1000,0x10000);替换为hp = HeapCreate(0,0,0);):

    尾块现大小为0x130(单位为8字节),即0x980字节,加上0x688,等于0x1008
    堆块首数据结构如图:

    2. 堆的管理

    2.1 分配

    有几个前提:
    1)若请求32个字节,实分配40个字节,因为有8个字节做块首
    2)堆块分配最小单位为8字节
    3)初始状态下,快表和空表都为空,不存在精确分配,将发生次优块分配
    4)由于次优分配,分配函数会陆续从尾块中切走一些小块,修改尾块块首中的size信息,把freelist[0]指向新的尾块位置

    接下来把int3改为nop

    继续执行到0x401061后(对应代码h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,3);),可发现:

    h1实请求3字节,但分配了16字节(8字节块首 + 8字节最小堆分配单位)
    继续执行,最终可知实际堆块分配情况:h1~h4均为2个堆单位,h5、h6均为4个堆单位,所以最终尾块大小为0x130-2*4-4*2=0x120:

    2.2 释放

    前三次释放不会发生合并(因为不连续),按照空表组织规则,h1、h3所指堆块会被链入freelist[2],h5被链入freelist[4]。
    此时堆块们如图,h1、h3、h5的data区的前8个字节存储着前向、后向链表指针:

    查看0x3A178处的空表索引:

    此时空表结构如图:

    2.3 合并

    释放h4后,h3、h4、h5发生合并。
    首先这三个空闲块都从空表中摘下,合并后链入空表freelist[8]。合并只会修改空表索引和块首数据,原块块身基本没有变化
    此时空表索引如图:

    注意:空表的第一个块不会向前合并,最后一个块不会向后合并。

    3. DWORD SHOOT

    利用堆溢出淹没掉下一个堆块块首,从而改写块首中的前向、后向指针。一旦此堆块被回收,将发生:

    void remove(ListNode* node){
        node->blink->flink=node->flink;
        node->flink->blink=node->blink;
    }
    

    而恰恰现在的flink、blink都是伪造的,所以将导致攻击者能够向任意内存写入任意数据。当然不仅仅是回收堆块,任何涉及链表的操作(分配、合并等)都可成为攻击点。

    3.1 常用攻击目标

    • 内存变量:如标志位
    • 代码逻辑:如将调用指令替换为nop
    • 函数返回地址
    • 异常处理机制:堆溢出容易引起异常,从而转入异常处理机制,所以异常处理涉及的数据结构往往成为DWORD SHOOT攻击目标
    • 函数指针
    • P.E.B中线程同步函数的入口地址:在进程退出时会被ExitProcess()调用
    Those who seek some sort of a higher purpose or 'universal goal', who don't know what to live for, who moan that they must 'find themselves'. You hear it all around us. That seems to be theofficial bromide of our century. Every book you open. Every drooling self-confession. It seems to be the noble thing to confess. I'd think it would be the most shameful one.
  • 相关阅读:
    hibernate的dialect大全
    jdbc.properties 链接各类数据库的基本配置以及URL写法
    Springboot中redis的学习练习
    博客开通了
    Java String类的hashCode()函数
    Java String类中CaseInsensitiveComparator.compare()方法的实现
    git pull远程所有分支
    Python的权限修饰符
    Tmux快捷键
    __future__模块
  • 原文地址:https://www.cnblogs.com/spenghui/p/7616799.html
Copyright © 2020-2023  润新知