首页
社区
课程
招聘
[原创]通过GDK8观察堆
发表于: 2021-10-11 18:40 6658

[原创]通过GDK8观察堆

2021-10-11 18:40
6658

主机:      Windows 10;64位;专业版

                调试工具为Nano Code;与GDK套件协同工作的集成调试工具,兼容WinDBG。

目标机:  GDK8  Ubuntu 18.04;64位  ArmV8架构

                Nano Debugger;用于支持符号化的Linux应用程序调试

        与GDK7不同的是,GDK8在与主机建立连接通过的不再是USB3.0电缆(DCI技术),而是通过远程连接的方式,将目标机与主机连接起来。同时GDK8也是一台可以直接使用的主机,大幅度的减少了准备调试环境所需的时间,省去各种因为软件和硬件不兼容所带来的烦恼。

       GDK8调试套件包含了不少东西,如遥控器、电源、HDMI电缆、GDK8、还有一根蓝色电缆用于刷固件、研究底层逻辑。

观察过程

       堆是什么?--》在程序运行过程中,堆可以提供动态分配的内存,允许程序申请大小未知的内存。堆其实就是程序虚拟地址空间的一块连续的线性区域,它由低地址向高地址方向增长。我们一般称管理堆的那部分程序为堆管理器;在知道堆是什么后,我们在GDK8中去观察堆及其相关的数据结构。

        首先我们需要在GDK8中下载libc符号[sudo apt install libc6-dbg],下载的符号一般会放在/usr/lib/debug/lib目录内;可以通过find命令查找libc符号所在位置[sudo find /usr/lib/debug -name “libc-x.xx.so”];找到libc符号之后,就可以将libc符号复制到主机C盘的temp目录[C:\temp]当中(目前仅支持这1种路径),并且还需要将libc-x.xx.so更名为类似于libc.so.6的形式;如下图所示。

       随后将GDK8和Nano Code建立连接,并加载libc符号及其他所需符号;如下图所示。

       接着获取main_arena全局变量(属于malloc_state结构体)的地址;main_arena中的next指针指向的是下一个arena的地址,最后1个arena中的next指针指向的是main_arena;通过dt查看数据类型的时候,直接显示数值大小的是全局变量,显示数值对应数据类型的是结构体;如下图所示。

       当获取main_arena的地址之后,根据next指针找出所有的arena(每个线程都有它专属的arena;主线程的arena称为main_arena,子线程的arena称为non_main_arena),并将它们显示出来;arena对应的是malloc_state结构体;malloc_state结构体中的flags字段记录分配区的一些状态,如比特0的位置记录了分配区是否有fast bin chunk,比特1 记录了分配区是否能返回连续的虚拟地址空间,比特3记录了分配区的内存是否损坏。

       在main_arena中,当程序第一次动态分配内存的时候,堆会被分成两块,一块是分配区,另一块是Top Chunk,Top Chunk的起始地址亦是top指针所指向的位置;在之后动态分配内存的过程中,只要Top Chunk的位置还够用,分配区就会从Top Chunk中划分空间供自己使用,但是假如Top Chunk中的空间不够用了,那么就会通过brk机制扩展空间。

       在main_arena中的top指针记录了分配区的结束位置的地址,top指针的上方为top chunk;而分配区的开始位置的地址,如下图所示。

        在GDK8中手工验证一下,这个地址是否正确;如下图所示,可以看到没有问题。

       在non_main_arena中的情况则会复杂一些;首先non_main_arena中如果分配内存数量不够大的话,那么分配区的开始位置的地址和结束位置的地址同main_arena中的情况是一样的;但是如果分配内存数量足够的大,那么在non_main_arena中的分配区会划分成1个又1个的子堆;不管non_main_arena是否划分了子堆,在主子堆中,分配区的开始位置永远是[bins指针+mchunk_size(去标志位;bins指针对应malloc_chunk中的mchunk_size)],而其他子堆分配区的起始位置的地址为[heap info的地址+0x20]。

       假如想要获取全部的子堆,就要遍历一下链表,不过这个链表并不是循环的,而且需要依靠perv指针找上一个),直到找到第1个为止;因此我们需要获取链表中的最后1个子堆,具体获取方式如下所示(以64位系统为例)。

    HEAP_MAX_SIZE = 0x4000000;

    heap_address = top & ~(HEAP_MAX_SIZE - 1);

* 需要注意的是主子堆的heap info内的prev指针永远是空(主子堆一般排在链表的第一个),此链表并不是循环的。

        在GDK8中手工验证一下,这个地址是否正确;如下图所示,可以看到没有问题。


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 2
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//