Created
October 11, 2023 11:25
-
-
Save Kazurin-775/b9a57e05e4e7617f40ba42d7278f7843 to your computer and use it in GitHub Desktop.
GDB-Python script to trace calls to `malloc()` and `free()`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # This script can print out the arguments and return values of `malloc()` and | |
| # `free()`, thanks to the power of GDB-Python. | |
| # | |
| # Usage: load the target program in gdb, wait until libc is loaded (e.g. by | |
| # executing `start`), then execute `source malloc-trace.py`, and enjoy! | |
| # | |
| # Note: this only works for Linux x86_64. Porting should not be difficult if | |
| # you are familiar with the C calling convention on the target platform. | |
| # | |
| # Also, feel free to extend this script with the functionalities you want (e.g. | |
| # printing out the callers of `malloc()`). | |
| # | |
| # Ref: https://stackoverflow.com/questions/4060565/how-to-script-gdb-with-python | |
| class MallocBp(gdb.Breakpoint): | |
| 'A breakpoint on malloc() which records the size argument for later use.' | |
| def stop(self): | |
| top = gdb.newest_frame() | |
| size = top.read_register('rdi') | |
| # Note: this is not thread safe (multithreading is not fully supported) | |
| self.state['size'] = size | |
| return False | |
| class MallocRetBp(gdb.Breakpoint): | |
| ''' | |
| A breakpoint set on `ret` instructions in malloc(). | |
| Prints out the addresses and sizes of the allocated chunks when hit. | |
| ''' | |
| def stop(self): | |
| top = gdb.newest_frame() | |
| ptr = top.read_register('rax') | |
| size = self.state['size'] | |
| self.state['size'] = None | |
| print(f'malloc({size}) = {int(ptr):#x}') | |
| return False | |
| class FreeBp(gdb.Breakpoint): | |
| 'A breakpoint on free() which simply prints its argument when hit.' | |
| def stop(self): | |
| top = gdb.newest_frame() | |
| ptr = top.read_register('rdi') | |
| print(f'free({int(ptr):#x})') | |
| return False | |
| # We need to find the offsets of 'ret's in 'malloc()' beforehand (there | |
| # # should be around 3 of them) | |
| malloc_disasm = gdb.execute('disassemble malloc', to_string=True) | |
| malloc_ret_addrs = [] | |
| for line in malloc_disasm.split('\n'): | |
| # 0x00007ffff7e1cd14 <+260>: ret | |
| if line.endswith(':\tret'): | |
| # print(line) | |
| malloc_ret_addrs.append(line.split()[0]) | |
| # Make sure we have found at least one of them (or this script won't work at all) | |
| assert malloc_ret_addrs | |
| # Set up breakpoints | |
| state = {'size': None} | |
| bp = MallocBp('malloc') | |
| bp.state = state | |
| for addr in malloc_ret_addrs: | |
| bp = MallocRetBp(f'*{addr}') | |
| bp.state = state | |
| FreeBp('free') | |
| print('Breakpoints have been set up') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment