
libtalloc — это Python-скрипт для использования с GDB, который можно применять для анализа «тривиального аллокатора» (talloc).
libtalloc — это python-скрипт для использования с GDB, который можно применять для анализа «тривиального аллокатора» (talloc). Введение в talloc можно найти здесь:
https://talloc.samba.org/talloc/doc/html/index.html
libtalloc был вдохновлён другими gdb python-скриптами для анализа куч, такими как unmask_jemalloc и libheap. Некоторая базовая функциональность идентична этим проектам.
https://github.com/cloudburst/libheap
https://github.com/argp/unmask_jemalloc
Обратите внимание, что я не гуру python, и качество кода это отражает. Если вы видите что-то, что вас раздражает, не стесняйтесь отправлять патч или давать мне предложения. Любая обратная связь приветствуется.
libtalloc тестировался на различных версиях talloc из линейки 2.x и поддерживает динамическое определение версии, чтобы попытаться преодолеть различные структурные различия между версиями. Он тестировался на 32-битных и 64-битных системах, однако не исчерпывающе, так что не удивляйтесь, если иногда он ломается.
Он в некоторой степени протестирован на x86 и x64:
Если вы протестируете его на другой версии, пожалуйста, сообщите мне, сработал ли он, или что сломалось, и я постараюсь обновить его и/или документацию соответствующим образом.
Скрипту требуется относительно современная версия GDB с поддержкой python.
Некоторые LTS-дистрибутивы, например Ubuntu 12.04, всё ещё используют GDB с python 2.7, тогда как более новые версии, такие как 14.04, используют python 3.0. Я постарался сделать так, чтобы этот скрипт работал с обоими, так что вам нужно только:
(gdb) source libtalloc.py
Большая часть функциональности смоделирована по подходу unmask_jemalloc, где предоставляется отдельная команда GDB, а не сложный набор переключателей.
Доступен ряд методов, специально разработанных для имитации C-функций библиотеки talloc, чтобы помочь людям, пытающимся расширить libtalloc, если они уже знакомы с библиотекой.
Чтобы увидеть полный список команд, вы можете выполнить команду tchelp:
(gdb) tchelp
[libtalloc] talloc commands for gdb
[libtalloc] tcchunk -v -x <addr> : show chunk contents (-v for verbose, -x for data dump)
[libtalloc] tcvalidate -a <addr> : validate chunk (-a for whole heap)
[libtalloc] tcsearch <addr> : search heap for hex value or address
[libtalloc] tcwalk <func> : walk whole heap calling func on every chunk
[libtalloc] tcreport <addr> : give talloc_report_full() info on memory context
[libtalloc] tcdump -s <addr> : dump chunks linked to memory context (-s for sorted by addr)
[libtalloc] tcparents <addr> : show all parents of chunk
[libtalloc] tcchildren <addr> : show all children of chunk
[libtalloc] tcinfo : show information known about heap
[libtalloc] tcprobe : try to collect information about talloc version
[libtalloc] tchelp : this help message
Одна из самых важных команд — tcprobe. Её необходимо запустить, чтобы выяснить, какая версия talloc фактически установлена. Структуры для разных версий могут значительно различаться, поэтому для работы большинства функций версия должна быть известна.
Если команда работает, она должна сообщить обнаруженную версию:
(gdb) tcprobe
Version: 2.1.1
File: /usr/lib/libtalloc.so.2.1.1
Команда tcinfo предназначена для отображения как можно большего объёма собранной информации о куче, такой как информация от tcprobe, структура null_context, если она найдена, и многое другое. На данный момент она показывает только версию и установлен ли null_context. null_context требуется для большинства функций, которые обходят фактическую иерархию, и чтобы найти его, большинство других функций, таких как tcchunk и т.п., попытаются автоматически найти его.
После запуска tcprobe, но до фактического использования tcchunk:
(gdb) tcinfo [libtalloc] null_context not yet found yet [libtalloc] Version: 2.0.7 [libtalloc] File: /usr/lib/i386-linux-gnu/libtalloc.so.2.0.7
Затем после анализа чанка, например так:
(gdb) tcchunk 0xb94a52b0 WARNING: 0xb94a52b0 not a talloc_chunk. Assuming ptr to chunk data 0xb94a5280 sz:0x0000003c, flags:...., name:struct tevent_context
Вы можете подтвердить, что он был найден, с помощью tcinfo.
(gdb) tcinfo [libtalloc] null_context: 0xb94a5028 [libtalloc] Version: 2.0.7 [libtalloc] File: /usr/lib/i386-linux-gnu/libtalloc.so.2.0.7
Теперь, когда null_context установлен, вы можете запускать другие команды, которые обычно жаловались бы, что он не установлен, например команду tcsearch.
tcchunk может предоставить вам сводку по чанку, более подробный вывод по каждому полю или чрезвычайно подробную информацию о каждом окружающем чанке.
ПРИМЕЧАНИЕ: Важно отметить, что внутри tcchunk использует метод tc_chunk(), который пытается исправить ошибки, допущенные при передаче адреса чанка. В частности, если вы передаёте адрес самих данных чанка, и он не находит ожидаемого магического значения talloc, он будет искать легитимный заголовок чанка чуть раньше в памяти. Это может помешать вам в сценариях с повреждёнными данными, поэтому всегда убедитесь, что вы передаёте явный адрес, если только вы не проводите поверхностный анализ.
Сводный вывод:
(gdb) tcchunk 0x80a13c88
0x80a13c88 sz:0x00000020, flags:..p., name:struct netr_ServerPasswordSet
Ниже приведена легенда для чанков в сводном выводе:
p - Член пула (флаг POOLMEM)
P - Чанк является пулом (флаг POOL)
F - Чанк свободен (флаг FREE)
L - Чанк зациклен (флаг LOOP)
Подробный вывод:
(gdb) tcchunk -v 0x80a13c88
struct talloc_chunk @ 0x80a13c88 {
next = 0x0
prev = 0x80a140c8
parent = 0x0
child = 0x80a14088
refs = 0x0
destructor = 0x0
name = 0x807d9f2f (struct netr_ServerPasswordSet)
size = 0x20
flags = 0xe8150c78 (POOLMEM)
limit = 0x0
pool = 0x80a13248
Чанки talloc содержат некоторые магические значения, которые можно использовать для проверки их корректности. Команда tcvalidate проанализирует чанк, чтобы убедиться, что магия чанка соответствует ожидаемой. Кроме того, она анализирует все остальные указатели-члены, чтобы убедиться, что они фактически попадают в диапазоны памяти (известные GDB), корректен ли размер и т.д.
(gdb) tcvalidate 0x80a13c88
Chunk header is valid
Мы воспользуемся встроенным методом, чтобы изменить значение и показать, как проверка может завершиться неудачей:
(gdb) python set_destructor(tc_chunk(0x80a13c88), 0x41414141)
(gdb) tcchunk -v 0x80a13c88
struct talloc_chunk @ 0x80a13c88 {
next = 0x0
prev = 0x80a140c8
parent = 0x0
child = 0x80a14088
refs = 0x0
destructor = 0x41414141
name = 0x807d9f2f (struct netr_ServerPasswordSet)
size = 0x20
flags = 0xe8150c78 (POOLMEM)
limit = 0x0
pool = 0x80a13248
(gdb) tcvalidate 0x80a13c88
Chunk header is invalid:
0x80a13c88: Chunk has bad destructor pointer 0x41414141
tcparents можно использовать для просмотра всех родителей указанного чанка:
(gdb) tcparents 0x80a13c88
0x809f8300: null_context
0x80a08660: TALLOC_CTX *
0x809f8370: talloc_new: ../lib/util/talloc_stack.c:147
0x809fb680: talloc_new: ../lib/util/talloc_stack.c:147
0x80a13258: UNNAMED
0x80a13c58: talloc_new: ../lib/util/talloc_stack.c:147
0x80a13c88: struct netr_ServerPasswordSet
tcchildren можно использовать для просмотра всех детей (и внуков и т.д.) указанного чанка: