如何找出堆分配的大小?

逆向工程 调试 linux 数据库 记忆
2021-06-24 18:31:37

当我使用 GDB 进行调试时,我看到了位于堆上的缓冲区的地址。

我怎么知道这个缓冲区的大小是多少?或者(在代码中)这个缓冲区最初是在哪里分配的?

当我在new函数上放置断点以找出分配时,该过程会显着减慢,从而使调试变得困难。

1个回答

跟踪内存分配

跟踪是指在调用函数时工具将简单地写入日志行(通常带有一些附加数据)而不是停止跟踪通常比中断执行和将控制权交给用户来处理断点要快得多。

这对您来说可能是最简单的解决方案。您可以使用具有跟踪或脚本功能的调试器或使用特定的跟踪实用程序(例如ltrace.

跟踪设置并运行后,您可以搜索您有兴趣调查的已分配缓冲区的地址,以查找它涉及的所有调用。

ltrace手册页是非常有用的,但在你的情况下,简单地grep-ing的地址会做得很好。ltrace具有标准库 API 的定义,例如newmalloc

跟踪gdb需要一些 gdb 脚本,但应该执行以下操作:

(gdb) b malloc
Breakpoint 1 at XXXX
(gdb) commands 1
Type commands for when breakpoint 1 is hit, one per line.
End with a line saying just "end".
>silent       # don't stop on breakpoint being hit
>backtrace    # print current back-trace
>p $eax       # Pass the input to the call, should be the size allocation!
>fin          # execute till function's return
>p $eax       # print return value, should be chunk address!
>continue     # continue execution of the program
>end

分配区域上的内存访问断点

使用诸如 之类的调试器gdb,您可以在分配的地址上放置一个内存访问断点(也称为观察点)。因此,通过执行awatch <allocation address>,每次访问分配时都会遇到断点(不过有一个警告)。

这不会立即为您提供分配的大小,但通过一些逆向工程和回溯地址的来源,您可以找到导致该缓冲区的原始分配调用。一个有益的副作用是很容易看到分配的用途。

如前所述,使用内存断点可能有一个警告。如果您的硬件不支持有效实现内存断点所需的机制,则内存断点可能会在相当慢的软件中实现。

通过堆数据结构查找大小

这可能是理论上回答原始问题(如何找到已分配堆块的大小)的最直接方法,但最难实现。未来的读者可能仍然感兴趣。

由于堆旨在管理不同大小的分配,因此所有堆实现都维护有关所有已分配块大小的元数据。可以读取或检索元数据,堆可视化工具甚至会对此有所帮助。

一些堆实现在边界内保存块元数据,并在每个分配的块前面加上一个短标头,该标头直接指示其大小,或指向给定大小的“bin”分配,或两者兼而有之。dlmalloc就是这种实现的一个例子。

一些堆分配器实现包括:

dlmalloc - Doug Lea 的 malloc

jemalloc - Jason Evans 的 malloc

HeapAlloc - Visual Studio 的分配器


旁注:堆分配不一定来自new调用。例如,还有其他可能的 API 从堆中请求内存malloc您应该找到您可能感兴趣的最低 API。