堆溢出利用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m0_37809075/article/details/82849388

堆的性质

  • 是在程序运行动态分配内存(需要参考用户的反馈)
  • 使用malloc函数或者new函数申请
  • 堆的读,写,释放都是通过堆指针来完成
  • 使用完成后,将堆指针交给释放函数回收这片内存
  • 增长方向由低地址到高地址

堆的数据结构

  • 堆分为块首和块身
  • 堆管理系统一般指向块身的起始位置
  • 块表位于堆区的起始位置,用于索引堆区中所有堆块的信息,决定了整个堆区的组织方式。
    • 空表(空闲双向链表)
      • 按照堆块的大小不同,空表分为128条
      • 其中空表索引的第一项是所有大于等于1024字节的堆块。在这里插入图片描述
    • 快表(快速单向链表)
      • 加速堆块分配采用的策略
      • 这类单向链表不会发生堆块合并
      • 快表总是初始化为空,每条快表最多只有4个结点 在这里插入图片描述

堆块的操作

堆块的分配
  • 快表分配:1.寻找,2.将其改为占用态,3.从堆表中卸下,4.返回一个指针指向自身块身给程序使用
  • 普通空表分配:1.寻找最优的空闲块分配(最小满足)
  • 0号空表分配:反向查找,先找最大,然后看是否满足,再找最合适的
堆块的释放

将堆块的状态改为空闲,链入相应的堆表末尾

堆块的合并

将两个块从空闲链表中卸下,调整合并后大块的块信息,将新块重新链入空闲

堆的管理

在这里插入图片描述

堆的分配函数


所有的堆分配函数最终都使用位于ntdll.dll中的RtlAllocateHeap()函数进行分配。

堆溢出利用

原理

在这里插入图片描述
堆溢出的利用建立在堆块的释放,在释放的时候有往指定内存的写数据操作。
在这里插入图片描述
其中bink后向指针是目标,flink前向指针是子弹
在这里插入图片描述比如覆盖后向地址为4444,前向指针为0000。在unlink的时候,就要把0000写入到4444地址处。假设我们0000的地址是shellcode的地址,4444处存储着一个库函数,并且之后会调用。那么在调用这个函数时,就会转而调用shellcode。这个过程叫DWORD SHOOT

常用目标
  • 内存变量:能影响程序执行的重要标志位
  • 代码逻辑:修改代码段的关键逻辑部分,比如NOP掉
  • 函数返回地址:修改函数返回地址来劫持进程(不固定)
  • 攻击异常处理机制:当产生异常时,会转入异常处理机制,将里面的数据结构作为目标
  • 函数指针:调用动态链接库的函数,C++中的虚函数调用
  • PEB线程同步函数的入口地址:每个进程的PEB都存放着一对同步函数指针,指向RtlEnterCriticalSection()和RtlLeaveCriticalSection(),并且进程退出的时候会被ExitProcess()调用(固定PEB的地址不会变)
实例

在这里插入图片描述

  • memcpy进行字符拷贝的时候出现问题,超过200字节的数据将覆盖尾块的块首。
  • 当h2分配时,将导致DWORD SHOOT。将0x7FFDF020处的RtlEnterCriticalSection()函数指针改为shellcode地址。
  • DWORD SHOOT完毕后,堆溢出导致异常,最终调用ExitProcess函数结束进程
  • 其中前向指针是子弹(shellcode的起始地址),后向指针是目标(PEB的函数指针地址0x7FFDF020固定的)

堆利用的注意事项

  • 调试堆和常态堆的区别
    int3下断点,attach进程
  • 在shellcode中修复环境
    DF的值
    堆区的修复
  • 定位shellcode跳板
    堆的地址不固定,需要定位shellcode的地址。用到一些指令跳板来定位shellcode
    call DWORD PTR [EDI+0x78]
    call DWORD PTR [ESI+0x4C]
    call DWORD PTR [EBP+0x74]
  • DWORD SHOOT后的“指针反射”现象
    在这里插入图片描述
    会发生第二次的DWORD SHOOT,会在shellcode的后4个偏移位置写入目标地址,这样会破坏shellcode,可能造成错误。(利用跳板技术)

猜你喜欢

转载自blog.csdn.net/m0_37809075/article/details/82849388