做网站推广 需要ftp,网站代运营性价比高,app开发软件开发公司,网店erp系统哪个好前言
一次意外执行了 malloc(0x5000)#xff0c;结构使用 gdb 调试发现其分配的位置在 TLS 区域#xff0c;这令我不解#xff08;#xff1a;最后去看了下 malloc 源码和 mmap 源码实现#xff0c;发现似乎可能是 gdb 插件的问题#xff0c;乐
场景复现
#include 结构使用 gdb 调试发现其分配的位置在 TLS 区域这令我不解最后去看了下 malloc 源码和 mmap 源码实现发现似乎可能是 gdb 插件的问题乐
场景复现
#include stdio.h
int main() {malloc(0x5000);return 0;
}调试 wtf 不应该啊这咋能分配到 tls 区域去了呢
问题探索
通过对 malloc 源码的查看发现其超过了 mmap_threshold所有最后走的 mmap 映射的一段私有匿名区域这里大家应该都很熟悉了所以不应该分配到 tls 区域去啊在与 henry 师傅交流后觉得可能与 mmap 的行为有关于是说干就干简单地去审了一下 mmap 的源码最后发现可能是插件的问题
mmap 源码分析就不写了我也没做笔记毕竟是开源的需要时自己去看看就行了而且我看完 mmap 源码时已经快凌晨 3 点了就简单说下 mmap 是如何分配虚拟内存的。在内核态为进程维护了一颗红黑树其根据地址组织了空闲的 vmastruct vm_area_struct 结构体表示而在我的 ubuntu 22.04 上文件与匿名映射区是从高地址往低地址增长的所以 mmap 在寻找虚拟内存区域时是从高地址往低地址扫描的
知道这之后一切都可以解释了简单调试一下啥也不执行看下内存布局把随机化关了方便调试 可以看到在 tls 前后存在 0x17e000 和 0x11000 的空间我们来验证一下
#include stdio.h
#include sys/mman.h
int main() {void *p0 malloc(0x17e000-0x1000);void *p1 mmap(0, 0x11000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);printf(p0: %p\np1: %p\n, p0, p1);return 0;
}我的机器上 mmap_threshold 128kb所以 malloc(0x11000-0x1000) 会走 brk所以这里我直接使用 mmap 关于这里 malloc 分配为啥要减去 0x1000 请自行审计 sysmalloc 源码大家应该比较熟悉
调试结果如下 可以看到 mmap 的 0x11000 区域就是 tls 下方的区域malloc 的 0x17e000 就是 tls 前面的那块区域这里的 gdb 插件把其当作 TLS 了似乎。当然这里可能就是 tls 预留的我没有具体调试内核仅仅静态分析了 mmap 的代码。当然我通过分配一些线程局部变量调整了一下 tls 区域的位置然后也是这样的
#include sys/mman.h
__thread char p0[0x17e0000x1000];
int main() {void *p1 mmap(0, 0x193000, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);printf(p0: %p\np1: %p\n, p0, p1);return 0;
}调试结果
所以当我们 malloc 超过 0x17e000-0x1000 时就会在 libc 前面映射一段空间也就是大家打 CTF 时说的 malloc 一块大堆块会使用 mmap 在 libc 前面分配一个堆块一般网上都是说分配 0x200000其实自己简单算一算就知道了验证一下
#include stdio.h
#include sys/mman.h
int main() {void *p0 malloc(0x17e000);printf(p0: %p\n, p0);return 0;
}调试结果如下 一切都是吻合的
一个有趣的 demo
这里给大家看一个 demo感兴趣的可以自行研究下背后的原因
#include stdio.h
#include stdint.h
#include string.hint show() {FILE *maps_file;char line[256];char pathname[256];maps_file fopen(/proc/self/maps, r);if (maps_file NULL) {perror(Failed to open /proc/self/maps);return 1;}while (fgets(line, sizeof(line), maps_file)) {if (sscanf(line, %*x-%*x %*s %*x %*x:%*x %*u %s, pathname) 1) {if (strcmp(pathname, [stack]) 0 || strcmp(pathname, /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2) 0) {printf(%s, line);}}}fclose(maps_file);return 0;
}int main() {uint64_t addr;show();puts();scanf(%llx, addr);*(uint64_t*)addr 0xdeadbeef;puts();show();return 0;
}
输出如下
总结
mmap 的源码还是比较复杂的我只是看了我感兴趣的部分有兴趣的同学可以自己去看看源码本来想说搭建环境调调 mmap 的最后还是算了太累了睡觉了
当然这个东西本身连技术都不算之所以记录一下也算是回答自己刚开始学习时的一些疑问记得当时看网上说 malloc 要分配 0x20000 才能够保证分配的空间在 libc 前面其实根本没有问过自己为什么也没有想过 mmap 底层是如何进行虚拟内存分配的
当然其实现在来解决这些问题也挺好的毕竟有一些基础如果最开始就去看 mmap 的源码相信我早就被劝退了