heap

堆块:

chunk

堆是以一个个的堆块构成的,这些堆块就叫chunk

chunk的大小是8字节对齐,但是一个堆块的具体大小是16字节对齐的,比如一个堆块只能是 0x40,0x50,0x60 不会是0x48这样的数据

其中一个堆块的header头部字节占16字节大小,也就是0x10字节

64位程序下的最小长度是32字节大小的

这里我们先来了解一下chunk的头部是什么样子的


image-20240614170750740.png)

0x10字节 包含了prev size 以及 size,prev size 指向了上一个chunk的大小,size则是本chunk的大小,size的区域中则还存在着三个类似于标志位的东西

  1. non-main-arema查看当前的chunk是否不属于主线程,1是不属于,0是属于
  2. is-mmap表示当前的chunk是否是由mmap来分配空间的,1表示是,0表示不是
  3. prev_inuse表示前面的一个的chunk是否被使用,1表示前面的chunk正在被用户使用,0表示前一个chunk已经被释放了

同时,我们需要知道的是,chunk的pre size记录着上一个chunk的大小,但是只有在前一个chunk被free释放的时候也就是prev_inuse是0的时候才会生效,其他时候只会作为上一个chunk的user date

bin

bin有垃圾桶的意思,顾名思义,在堆中bin指的也就是被free掉的内存了,其实也是对于这些不同大小的释放之后的chunk进行管理,同时我们按照由ptmalloc释放的内存大小分为了不同的bin

这些bin都是按照链表的形式去存储chunk的

Fastbin

大小是0x20-0x80 每一个fastbin按照0x10的大小开始增长,比如0x20,则存储着大小为0x20个字节的chunk的chunk链表。同时对于大小属于fastbin的chunk被free之后下一个的chunk的preinuse不会改变,也不会合并

Smallbin、unsortedbin

smallbin是62个chunk大小一样的双向链表,大小按照0x10进行递增图中的ptr 0 和ptr 1 分别是arena中的fd头和bk尾,所以每个smallbin都是先从arena开始,arena结束的一个双向循环链表的样子

unsortedbin其实和smallbin是一样的,但是它的链表中的chunk是大小不一一条双向链表

largebin

largebin其实也和smallbin是一样的双向链表,只是因为,largebin的大小是0x410以上的,所以增量是不一样的,使得每一个chunk相连的时候的chunk的大小也可能是不一样的,所以这也就是为什么largebin的chunk中有四个指针

当两个相连的chunk是大小相等的时候,就只用fd和bk进行链接,这时可以视为smallchunk

当两个相连的chunk不一样大小的时候,,就需要用到fd-next-sz去链接下一个大小不一样的chunk,用bk-next-sz去链接前一个大小不一样的chunk。

合并

那我们就来看看是怎么进行释放的堆空间bin合并操作的

向前合并:查看本chunk的pre_inuse,假如是0,说明上一个chunk被释放可以和本chunk合并,那么就直接合并,同时修改size大小

向后合并:查看后面的后面的chunk的pre_inuse是否为0,是则可以进行合并

调用malloc分配内存的过程以及总结

我们已经了解过了上面的各种bin的结构了,那我们来想一想它们最初是怎么形成的呢?

首先是理解为什么要出现我们的chunk,bin..... 这些结构的出现的原因是我们不想一直通过系统调用去申请内存,这样一直的系统调用无论是从时间上以及安全等等的角度来说都是不合理的,这也就是这些数据结构出现的真正原因

了解到了这些结构产生原因,我们再进行,topchunk是chunk的一个最大的内存,所以的chunk都是由它来一一割裂形成的,内核通过申请内存来构造了topchunk

这里我们从malloc调用申请堆内存来说明

  1. 比较申请内存的大小和每个fastbin,假如小于fastbin且fastbin中有可用的chunk就直接分配内存;假如大于则向smallbin申请
  2. 比较申请内存的大小和每个smallbin,假如满足同上申请分配,否则就去unsortedbin去申请(这里会进行malloc_consolidate操作,就是,因为需要分配的是一个大的空间,所以ptmalloc会遍历fastbin中的chunk进行合并,并链接到unsortedbin中去)
  3. 比较申请内存的大小和每个unsortedbin,由于unsorted的chunk大小不一样,(注意:假如取出来的chunk不满足,也就是小了,则把这个chunk放入到相应大小的small和largebin中)假如大于申请的内存则去切割这个chunk(假如切割之后的大小小于0x10就没必要去切割),将切割之后的chunk放入unsorted。不满足申请largebin
  4. 比较申请内存的大小和每个largbin,同上的操作,不过这里会去选择满足申请的最小的chunk,并且进行切片,切片之后的放入unsortedbin。不满足只能去topchunk切割了
  5. 连topchunk都不够了,只能去mmap系统调用申请内存了。

这里我们来梳理一下chunk怎么来的,由于申请之后释放内存大小不一样来使得chunk被分配到了不同的bin中,同时unsortedbin中的chunk是被切割之后的chunk的收容所同时也会收入free大于了fastbin的chunk,但是smallbin和largebin中的chunk却来自于unsortedbin

调用free,程序在干嘛?

  1. free的chunk属于fastbin,直接收入完成,不属于下一步
  2. 查看chunk的is_mmap的判断是mmap分配的吗,是则munmap回收,不属于下一步
  3. 释放的chunk这时去查看前一个chunk是否是空闲,是则前合并
  4. 释放的chunk去查看后一个chunk是否是topchunk(特殊),是则与topchunk合并了,直接完成,不属于则下一步
  5. 释放的chunk这时去查看后一个chunk是否是空闲,是则后合并
  6. 之后就直接放入unsortedbin了

unlink是用来将一个双向链表(存储着空闲的chunk)中的一个元素取出来

所以,unlink是一个取空闲元素的一个操作

在malloc中的unlink

  1. 从恰好大小合适的largebin中的去获取chunk(其中fastbin和smallbin中没有unlink)
  2. 从比申请的chunk所在的bin大的bin中取chunk

在free中的unlink

  1. 向后合并,合并物理地址相邻低地址空闲的chunk
  2. 向前合并,合并物理地址相邻高地址空闲的chunk(除开topchunk)

在malloc_consolidate中的unlink

  1. 向后合并,合并物理地址相邻低地址空闲的chunk
  2. 向前合并,合并物理地址相邻高地址空闲的chunk(除开topchunk)

![image-20240618143444860]

这里是unlink的过程,其实就是从 一个空闲的双链表中取出一个内存空间

但是相连的三个chunk中要取出中间的chunk是有校验的,也就是前一个chunk的bk指向当前这个chunk,后一个chunk的fd指向当前这个chunk

比如假如没有校验的情况之下,假如我们有uaf的时候,可以去控制当前的这个chunk的fd指向一块targe-0x18的位置,把我们的bk值设置为targe的expect的值,然后当我们指向回去的时候 targe-0x18 的bk就指向了我们的地址了

校验

在2.23版本之上的libc就对于这样的数据有校验了,也就是

P->fd->bk=P

P->bk->fd=P

所以我们只能申请一段fake_chunk来使得fd指向P的低0x10的位置,使得bk来指向P低0x18的位置,这样才能绕过校验

于是的结果就是P原本的chunk地址,因为fd的指向被修改到了P的低0x18个字节的位置

fastbin_attack

fastbin_attack通过fastbin来进行的攻击,其中的漏洞就是通过去利用去修改本来应该已经被free的fastbin中的chunk

USF(use after free)

顾名思义就是在当前的chunk被free之后还是能够进行的进行使用,也就是我们可以直接去修改chunk中的fd值,从而实现再一次进行申请地址时,我们可以去申请到我们fd指向的target_addr

double free

double free也是我们能够去利用的一个点

其中的原理其实就是,我们去free了chunk1,再去free一次chunk1,这样的结果就是,第一次free的chunk1的fd指向了chunk1,然后我们再去申请出来一个chunk1,然后去修改chunk1中的fd,指向我们的target_addr,然后再次free两次就可以直接去到target

随着glibc版本的增强,double free的检测也在增强,能够连续free两次同一个地址也会报错了

这时我们就会使用——>chunk1——>chunk2——>chunk1这样的形式去利用我们的结果,同样可以做到任意地址写

house of spirit

这里算是一个校验了,不太算是一个攻击,但是我们攻击取要用到这个,house of spirit针对的是fastbin这样的单链结构,当我们在想要利用构造fakechunk来实现我们的劫持程序的过程中,这里会对于我们构造的fake_chunk的size大小是否是当前fastbin对应的大小,只有当其对应的大小等于了我们的fastbin的大小,才会去给到这块fd指向的地址分配,同时不管这里是栈上,bss段上,还是什么地方,都还是把它视为堆的,所以它有prev_size,size等等

OFF BY ONE

通过去溢出一个字节,从而使得去修改了size的大小(比如:通过两个连续的chunk,我们去修改了第一个chunk的size大小【通过溢出手段,比如一个0x38大小的chunk,p指针指向这个chunk,然后p[0x38]=size,就修改了size的大小】),然后再进行free,就覆盖掉了两个chunk,然后我们再申请该大小的chunk就可以直接去修改第二个chunk的fd,bk了就可以间接劫持程序流。

#include <stdlib.h>
int main()
{void *p1,*p2,*p3;p1=malloc(0x38);p2=malloc(0x30);p3=malloc(0x30);char *p=p1;p[0x38]=0x81;free(p2);p2=malloc(0x71);return 0;
}

OFF BY NULL

chunk shrink

通过去使得chunk缩小,利用unlink构造出chunk overlap(两个chunk直接有重叠的空间)

我们同样是通过去溢出一个字节(这里我们需要知道的是只能溢出一个字节,也就是两个十六进制为)比如:当不再是fastbin的时候,申请了一个0x230大小的一个chunk,我们只能溢出修改的是0x30的位置

  1. 首先:我们去申请符合unsortedbin要去的chunk1 比如0x230,再申请一个chunk2,0x100(用于之后总体合并)
  2. 然后我们将第一个chunk1,free掉,并且将其size修改小,由于我们只能去修改一个字节大小的数据,所有我们只能将chunk的size设置为0x200,(注意,这里我们要提前去找到chunk1+0x200的位置,构造fake_chunk,使得fake_chunk的pre_size是0x200,这样才能过unlink的check)
  3. 这时候chunk2的pre_size的值仍然是0x230(这样再我们继续申请chunk时就可以直接去将中间的chunk合并完了),这时候我们会发现我们的unsortedbin里面由free的0x230变为了0x200,(可以注意的问题是:我们下面的代码由于直接去将0x200申请到了每一个位置,所有原本的0x100是被挤没了的,其实我们注意一下写入的chunk接入到ptr3的位置,是可以不挤掉的。)这时候我们去清空unsortedbin的内容,比如0x180+0x60(最好有一个0x60,申请之后就是0x70,可以找到相应的chunk的size大小)
  4. 然后我们去free0x180的chunk,(为了什么?因为0x180之后是0x60,他们之间的chunk链是合法的,free了0x180之后0x60的pre_size是0x180并且自己也是合法的大小)
  5. 最后我们去free(chunk2),由于chunk2的pre_size是之前chunk1的size,会导致直接去free掉了从chunk1到chunk2之间的所有堆空间,但是:我们的申请的0x60并没有被我们free,所有导致的结果就是,可以free同一块chunk了
#include <stdio.h>
#include <stdlib.h>
int main()
{void *ptr1=malloc(0x18);void *ptr2=malloc(0x220);void *ptr3=malloc(0x100);malloc(0x10);size_t *p1=ptr2;for(int i=0;i<0x220/8;i++){p1[i]=0x200;}free(ptr2);  char *p2=ptr1;p2[0x18]=0;ptr1=malloc(0x180);void *ptr4=malloc(0x60);free(ptr1);free(ptr3);free(ptr4);return 0;
}

House Of Einherjar

这是一种堆利用的方式,采用的思路也是通过构造overlap实现间接劫持程序流,利用的手法也就是通过去修改chunk的pre_size,去实现两个不相邻chunk的unlink,从而实现中间chunk的overlap

#include <stdio.h>
#include <stdlib.h>
int main()
{void *ptr1=malloc(0x200);void *ptr2=malloc(0x18);void *ptr3=malloc(0x100-0x10);malloc(0x10);free(ptr1);size_t *p=ptr3;p[-1]=0x100;p[-2]=0x230;free(ptr3);free(ptr2);return 0;
}

这里是最简单的一个house of einherjar 直接就是通过去修改我们的ptr3的pre_size和size,使得我们free的结果指向了ptr1,然后直接进行了合并。这时候overlap就是我们的ptr2.

但是:面对于高版本的glibc,会增加很多的check手段

  1. 比如说当我们进行合并的时候,会进行unlink的检测,也就是合并的ptr1(按照上面的来说)的chunk1的fd——>FD,chunk1的bk——>BK之后,检测FD——>bk=chunk1以及BK——>fd=chunk1。
  2. 这时候我们绕过的方式就是通过构造fake_chunk,使得fake_chunk的fd=fake_chunk本身,fake_chunk的bk=fake_chunk本身,这样的结果就是直接绕过检测了
  3. 在以上代码中,我们将free之后的ptr3+pre_size指向了ptr1,这样完成了覆盖ptr2,但同时进行的检测还有我们指向的ptr1的size必须==ptr3的pre_size,所有我们同时构造的fake_chunk的size必须等于ptr3的pre_size

高版本绕过:

#include <stdio.h>
#include <stdlib.h>
int main()
{void *ptr1=malloc(0x200);void *ptr2=malloc(0x18);void *ptr3=malloc(0x100-0x10);malloc(0x10);size_t *p=ptr1;p[0]=0;p[1]=0x221;p[2]=ptr1;p[3]=ptr1;p=ptr3;p[-1]=0x100;p[-2]=0x220;free(ptr3);free(ptr2);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/735738.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

固件的提取

固件提取的三类方法:直接从官网上找到目标型号的设备固件下载 使用Telnet或者ssh从目标设备中获取固件 从开发板中的flash芯片中或者通过uart和jtag调试接口将固件提取下来JTAG(Joint Test Action Group),是一种用于测试和调试电子设备的技术标准。它使用4线或5线接口,其中…

转:在Linux上运行WinForm

C#winform软件实现一次编译,跨平台windows和linux、mac兼容运行,兼容Visual Studio原生界面Form表单开发 - 亲善美 - 博客园 (cnblogs.com)一、背景: 微软的.net core开发工具,目前来看,winform界面软件还没有打算要支持linux系统下运行的意思,要想让c#桌面软件在linux系…

固件的烧录以及部分PCB基础

固件 固件的基础定义: 固件(firmware)一般存储于设备中的电可擦除只读存储器(允许用户通过特定的电子方式复写存储内容,在【工作情况下是只读的,并且关闭电源仍存储数据)EEPROM(Electrically Erasable Programmable ROM)或FLASH芯片中,一般可由用户通过特定的刷新程序进…

Golang:go-querystring将struct编码为URL查询参数的库

Golang:go-querystring将struct编码为URL查询参数的库 原创 吃个大西瓜 Coding Big Tree 2024-05-09 08:30 北京go-querystring is a Go library for encoding structs into URL query parameters.译文:go-querystring 将struct编码为URL查询参数的Golang库文档https://pkg.g…

C#之缓存

原文链接:https://zhuanlan.zhihu.com/p/657458522 缓存指在中间层中存储数据的行为,该行为可使后续数据检索更快。 从概念上讲,缓存是一种性能优化策略和设计考虑因素。 缓存可以显著提高应用性能,方法是提高不常更改(或检索成本高)的数据的就绪性 既然缓存是是一种性能…

有关paddleocr在pyinstall中打包问题的解决方案

借鉴网址python解决paddleocr打包问题_pyinstaller怎么解决paddleocr中的动态导入-CSDN博客 在打包时我使用的spec文件如下:(需要将pathe和binaries换为自己的paddleocr路径) block_cipher = Nonea = Analysis([main.py], pathex=[E:\\PyEnviroment\\Lib\\site-packages…

初始docker

前置知识 通过面向对象的知识来了解docker中的镜像和容器就很好理解了,docker是C/S架构镜像:是一个只读的模板,可以用来创建容器。类容器:是docker的运行实例,提供了一个独立的可移植的环境,可以在这个环境中运行应用程序。实例,1个或多个docker仓库:用来存储docker镜像…

【日记】度过了一个堕落的周末……(184 字)

正文昨天睡了一天觉,今天看了一天《三体》电视剧。真是堕落到没边了呢(笑。本来想写代码完成年度计划,或者多写几篇文章,但实在不想写,也不想动笔。感觉这个周末什么都没做呢,休息倒是休息好了。今天 30 号,也不知道灵平安到学校没有。本有一些想写的东西,但懒得动笔了…

BigCodeBench: 继 HumanEval 之后的新一代代码生成测试基准

HumanEval 是一个用于评估大型语言模型 (LLM) 在代码生成任务中的参考基准,因为它使得对紧凑的函数级代码片段的评估变得容易。然而,关于其在评估 LLM 编程能力方面的有效性越来越多的担忧,主要问题是HumanEval 中的任务太简单,可能不能代表真实世界的编程任务。相比于 Hum…

window 下 pyenv 安装与使用

安装 GitHub上下载这个文件 https://github.com/pyenv-win/pyenv-win 点击这个 下载到本地解压文件并且重命名为> pyenv 并放置到合理位置添加环境变量 ...\pyenv-win\bin ...\pyenv-win\shims添加完成之后执行如下命令验证 pyenv --versionpyenv的基本命令 查看支持的pytho…

RabbitMQ如何备份与恢复数据

阅读目录一、场景 二、元数据备份和还原1、操作 2、导出数据3、导入数据 4、验证数据 三、消息数据备份和还原1、确定数据目录 2、为避免数据的一致性,需先停掉服务 3、备份数据目录4、还原数据 5、修改数据目录权限 6、启动B服务器上rabbitmq服务 7、验证消息数据是否还原成功…

Codeforces Round 918 G. Bicycles (二维最短路)

G. Bicycles题意:在一个无向图里你要从1点到达n点,每条路的路径长度是该路的权值乘于你当前的慢度因子。而在每个点上我们都有一个慢度因子可以进行更换,问你到达n点所需要的最短时间。 思路:我们很容易想到每次遇到更小的慢度因子我们就要更换,但因为存在你先去绕远路拿更…

中电金信:银行业私有云何去何从

​2009年,云计算开始从概念走向实践。在这一年,Gartner在预测2010十大发展趋势中,将云计算列在榜首。在这之后,谷歌、亚马逊、IBM等科技巨头纷纷加码对云计算的研发投入。2010年正式迎来云计算时代,这一年也被定为“云元年”。2013年4月,2013中国国际云计算技术和应用展览…

使用GCOV和LCOV测试C++代码覆盖率

使用GCOV和LCOV测试C++代码覆盖率 目录使用GCOV和LCOV测试C++代码覆盖率1. GCOV和LCOV简介2. GCOV和LCOV安装3. GCOV+LCOV测试代码覆盖率 1. GCOV和LCOV简介 GCOV是一个测试代码覆盖率的工具,可以与GCC一起使用来分析程序,以帮助创建更高效、更快的运行代码,并发现程序的未测…

springboot异常解决

Circular view path [test]: would dispatch back to the current handler URL [/interceptor/test] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)] with root cause问题解决 问题解释…

uniapp+thinkphp5实现微信登录

之前做了微信登录,所以总结一下微信授权登录并获取用户信息这个功能的开发流程。前言 之前做了微信登录,所以总结一下微信授权登录并获取用户信息这个功能的开发流程。 配置 1.首先得在微信公众平台申请一下微信小程序账号并获取到小程序的AppID和AppSecret https://mp.weixi…

用免费可视化工具做智慧城市,一屏实现城市统筹管理

在智慧城市的建设中,实现高效的统筹管理是至关重要的。通过免费可视化工具“山海鲸可视化”,这一目标可以轻松达成。山海鲸可视化是一款免费可视化工具,具备二三维融合、易用性、安全性以及高质量画面渲染等特色,是制作智慧城市可视化大屏的理想选择。山海鲸可视化的二三维…

【论文阅读】Position: What Can Large Language Models Tell Us about Time Series Analysis

原始题目:Position: What Can Large Language Models Tell Us about Time Series Analysis 中文翻译:立场:关于时间序列分析,大型语言模型能告诉我们什么 发表时间:2024-06-01 平台:ICML 文章链接:http://arxiv.org/abs/2402.02713 开源代码:NA摘要 时间序列分析对于理…

TDA4与Openvx技术开发示例

TDA4与Openvx技术开发示例 [TI TDA4 J721E]基于TDA4平台 FFmpeg / X264 的ARM平台移植 https://blog.csdn.net/AIRKernel/article/details/121483611 创建了开源的Demo演示案例库,后续会把Demo代码放到这个Gitee库里:TDA4/TI TDA4 https://gitee.com/tda4/ti-tda4欢迎大家加入…

遇到的线上问题之“动态数据源报错-recyle error java.lang.InterruptedException”

Druid出现DruidDataSource - recyle error - recyle error java.lang.InterruptedException: null异常排查与解决 一、线上的代码之前运行的都很平稳,突然就出现了一个很奇怪的问题,看错误信息是第三方框架Druid报出来了,连接池回收连接时出现的问题。[][ERROR][2024-07-01 …