
Heap analysis tooling for mempool
Vorabhinweis: Wir empfehlen, dieses Tool als Teil von asatools zu verwenden, es kann aber auch eigenständig genutzt werden.
libmempool ist ein Python-Skript und ein GDB-Analysewerkzeug, das bei der Analyse
von Mempool-bezogenen Datenstrukturen in verschiedenen Heaps auf Cisco-ASA-Geräten
hilft. Normalerweise sind diese Informationen in einem Heap-Chunk eingebettet oder eine
benutzerdefinierte Erweiterung einer anderen gängigen Heap-Struktur wie einer dlmalloc-
mstate-Struktur.
Cisco verwendet den Begriff Mempool, um Speicherbereiche zu beschreiben, die für verschiedene Zwecke wie allgemeine Zuweisungen, DMA usw. abgebildet werden. Diese Bereiche enthalten typischerweise ihren eigenen Heap, z. B. dlmalloc. Zuweisungsroutinen für diese Mempools werden normalerweise über Wrapper um den zugrunde liegenden Heap-Allokator implementiert, und diese Wrapper fügen den resultierenden Zuweisungen mempool-spezifische Metadaten hinzu. Wir bezeichnen diese Metadaten als Mempool-Header oder kurz mh. Ebenso bezeichnen wir einen Mempool regelmäßig als mp. Die Verwendung von mh zur Beschreibung eines Mempool-Headers steht auch im Einklang mit verschiedenen Zeichenketten, die in den Cisco-ASA-lina-Binärdateien gefunden werden.
Obwohl libmempool als eigenständiges Werkzeug zur Analyse einiger Aspekte von Mempool-Headern und Datenstrukturen verwendet werden kann, liegt sein größter Wert darin, als Callback aus anderen Bibliotheken wie libdlmalloc oder libptmalloc verwendet zu werden.
Es ist erwähnenswert, dass bestimmte Aspekte des Mempools, wie die Bins zur Verfolgung verwendeter Chunks, in einer dlmalloc-2.8.x-mstate-Struktur eingebettet sind und derselben Bin-Größenlogik folgen. Das bedeutet, dass Sie fast unweigerlich zumindest die den Mempool-Daten umschließende mstate-Struktur mit libdlmalloc untersuchen müssen.
libmempool wurde mit 32-Bit-/64-Bit-Cisco-ASA-Versionen (sowohl ASA5500-X-Serie als auch GNS3) getestet, die dlmalloc2.8 oder den auf ptmalloc2 basierenden Allokator von glibc verwenden. Es wurde mit zahlreichen ASA-Versionen getestet, einschließlich zahlreicher 8.x.y- und 9.x.y-Zweige. Es ist jedoch durchaus möglich, dass es bei einer bestimmten Version nicht funktioniert.
Um libmempool eigenständig zu verwenden, müssen Sie lediglich die Datei libmempool.py
in Ihr Projekt importieren. Dies ermöglicht einige begrenzte Aktionen, wie das
Registrieren des mpcallback-Objekts usw. Das kann nützlich sein, wenn Sie eine
Offline-Analyse protokollierter Heap-Funktionalität durchführen.
Um das Skript in GDB zu importieren, wird lediglich GDB mit Python-Unterstützung benötigt. Obwohl die meisten modernen GDB-Versionen auf Python 3 umgestellt haben, erwarten einige weiterhin 2.7. Das Skript wurde mit beiden getestet, aber Entwicklung und Tests erfolgen hauptsächlich mit Python 3.
(gdb) source libmempool_gdb.py
Wir haben den Großteil der GDB-bezogenen Logik aus libmempool.py in libmempool_gdb.py ausgelagert, um Abstraktionen zu testen und damit Sie libmempool.py problemlos außerhalb von GDB verwenden können. Dies wird sich in Zukunft wahrscheinlich ändern, da wir schließlich ähnliche Debug-Engine-Abstraktionen implementieren möchten, wie sie von anderen Heap-Analysetools wie libheap und shadow verwendet werden.
Obwohl ein Großteil des Werts von libmempool aus der bereitgestellten Callback-Funktion
mpcallback stammt, gibt es eine Reihe integrierter GDB-Befehle, die wir uns ansehen können.
(gdb) mphelp
[libmempool] mempool commands for gdb
[libmempool] mpheader -v -x <addr> : show chunk contents (-v for verbose, -x for data dump)
[libmempool] mpbinwalk [-v] [-p <addr>] <sz> : walk an mpbin and operate on each chunk in a bin
[libmempool] mpbin <addr> : determine to which bin an mp_header is associated to
[libmempool] mpmstate <addr> : display and cache a mempool mstate address
[libmempool] mphelp
Angenommen, wir kennen die Adresse eines Mempool-Headers, können wir dessen Daten analysieren. Beachten Sie, dass dies die Adresse des Mempool-Headers selbst sein muss, nicht die Adresse der Chunk-Metadaten des Kern-Allokators. Wir können die Inhalte also wie folgt ausgeben:
(gdb) mpheader 0x7fffbc1c1ca0
struct mp_header @ 0x7fffbc1c1ca0 {
mh_magic = 0xa11c0123
mh_len = 0x3
mh_refcount = 0x10000
mh_unused = 0x0
mh_fd_link = 0x7fffbc1c19e0 (OK)
mh_bk_link = 0x7ffff7ff7540 (-)
alloc_pc = 0x55555849e260 (-)
free_pc = 0x0 (-)
Wir können die Hex-Inhalte des Chunks auch mit -x ausgeben.
(gdb) mpheader -x 0x7fffbc1c1ca0
struct mp_header @ 0x7fffbc1c1ca0 {
mh_magic = 0xa11c0123
mh_len = 0x3
mh_refcount = 0x10000
mh_unused = 0x0
mh_fd_link = 0x7fffbc1c19e0 (OK)
mh_bk_link = 0x7ffff7ff7540 (-)
alloc_pc = 0x55555849e260 (-)
free_pc = 0x0 (-)
0x3 bytes of chunk data:
0x7fffbc1c1cd0: 0x55 0x04 0x03
Mempools haben das Konzept von Bins, also doppelt verketteten Listen, die dieselbe
Größe wie dlmalloc-Bins haben, aber zur Verfolgung verwendeter Chunks anstelle freier Chunks
dienen. Dies dient der Speichernutzungs-Buchhaltung auf Cisco-Geräten. Oft findet man einen
Chunk, der einen Mempool-Header enthält, weiß aber noch nicht, wo sich die mstate-Struktur
im Heap befindet. Zur Orientierung können Sie den Befehl mpbin verwenden, der Ihnen die
Adresse des Mempool-Bins liefert, in dem sich ein verwendeter Chunk derzeit befindet.
Zum Beispiel:
(gdb) mpbin 0x7fffbc1c1ca0
[libmempool] Found bin start at 0x7ffff7ff7540
[libmempool] Cached new mp_mstate @ 0x7ffff7ff73c0
[libmempool] mp_smallbin[08] - sz: 0x00000040 cnt: 0x00d3, mh_fd_link: 0x7fffbc1c1ca0
Dieser Hinweis hat nicht nur den Anfang des Mempool-Teils der mstate-Struktur gefunden,
sondern sie auch zwischengespeichert, und listet das spezifische Bin, in dem sich der Chunk
befindet. Optional könnten wir den Befehl mpbinwalk verwenden, um alle 0xd3 Chunks aus
diesem Bin aufzulisten, oder nur bis zu einer bestimmten Anzahl:
(gdb) mpbinwalk 0x40
[libmempool] mp_header @ 0x7ffff7ff7540 - mh_len: 0x00000000, alloc_pc: 0x00000000 [BIN HEAD]
[libmempool] mp_header @ 0x7fffbc1c1ca0 - mh_len: 0x00000003, alloc_pc: 0x55555849e260
[libmempool] mp_header @ 0x7fffbc1c19e0 - mh_len: 0x00000003, alloc_pc: 0x55555849e260
[libmempool] mp_header @ 0x7fffbc1c1750 - mh_len: 0x00000003, alloc_pc: 0x55555849e260
[libmempool] mp_header @ 0x7fffbc1bffa0 - mh_len: 0x00000003, alloc_pc: 0x55555849e260
[libmempool] mp_header @ 0x7fffbc1bff60 - mh_len: 0x00000003, alloc_pc: 0x5555584a1288
[libmempool] mp_header @ 0x7fffbc1c0050 - mh_len: 0x00000003, alloc_pc: 0x55555849e260
[...]
Wenn wir nun den gesamten Mempool-Teil der mstate-Struktur ausgeben möchten (also alle Bins
und zugehörige Statistiken), können wir den Befehl mpmstate verwenden:
(gdb) mpmstate 0x7ffff7ff73c0
struct mp_mstate @ 0x7ffff7ff73c0 {
mp_smallbin[00] - sz: 0x00000000 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[01] - sz: 0x00000008 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[02] - sz: 0x00000010 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[03] - sz: 0x00000018 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[04] - sz: 0x00000020 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[05] - sz: 0x00000028 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[06] - sz: 0x00000030 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[07] - sz: 0x00000038 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[08] - sz: 0x00000040 cnt: 0x00d3, mh_fd_link: 0x7fffbc1c1ca0
mp_smallbin[09] - sz: 0x00000048 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[10] - sz: 0x00000050 cnt: 0x0429, mh_fd_link: 0x7fffa4000d10
mp_smallbin[11] - sz: 0x00000058 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[12] - sz: 0x00000060 cnt: 0x335d, mh_fd_link: 0x7fffb80064b0
mp_smallbin[13] - sz: 0x00000068 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[14] - sz: 0x00000070 cnt: 0x073a, mh_fd_link: 0x7fffbc1c7260
mp_smallbin[15] - sz: 0x00000078 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[16] - sz: 0x00000080 cnt: 0x0301, mh_fd_link: 0x7fffbc1c72d0
mp_smallbin[17] - sz: 0x00000088 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[18] - sz: 0x00000090 cnt: 0x0c98, mh_fd_link: 0x7fffc9488920
mp_smallbin[19] - sz: 0x00000098 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[20] - sz: 0x000000a0 cnt: 0x0161, mh_fd_link: 0x7fffa40009a0
mp_smallbin[21] - sz: 0x000000a8 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[22] - sz: 0x000000b0 cnt: 0x0092, mh_fd_link: 0x7fffa4000a40
mp_smallbin[23] - sz: 0x000000b8 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[24] - sz: 0x000000c0 cnt: 0x0120, mh_fd_link: 0x7fffbc1c1270
mp_smallbin[25] - sz: 0x000000c8 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[26] - sz: 0x000000d0 cnt: 0x00ba, mh_fd_link: 0x7fffbc1b51a0
mp_smallbin[27] - sz: 0x000000d8 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[28] - sz: 0x000000e0 cnt: 0x005b, mh_fd_link: 0x7fffc9485020
mp_smallbin[29] - sz: 0x000000e8 cnt: 0x0000, mh_fd_link: 0x0
mp_smallbin[30] - sz: 0x000000f0 cnt: 0x016e, mh_fd_link: 0x7fffbc1c7040
mp_smallbin[31] - sz: 0x000000f8 cnt: 0x0000, mh_fd_link: 0x0
mp_treebin[00] - sz: 0x00000180 cnt: 0x050f, mh_fd_link: 0x7fffbc1c7130
mp_treebin[01] - sz: 0x00000200 cnt: 0x00ce, mh_fd_link: 0x7fffbc1c2610
mp_treebin[02] - sz: 0x00000300 cnt: 0x011a, mh_fd_link: 0x7fffa40053c0
mp_treebin[03] - sz: 0x00000400 cnt: 0x013e, mh_fd_link: 0x7fffa4000f80
mp_treebin[04] - sz: 0x00000600 cnt: 0x016f, mh_fd_link: 0x7fffbc1c2a80
mp_treebin[05] - sz: 0x00000800 cnt: 0x009a, mh_fd_link: 0x7fffbc1b4880
mp_treebin[06] - sz: 0x00000c00 cnt: 0x0081, mh_fd_link: 0x7fffbc1b38b0
mp_treebin[07] - sz: 0x00001000 cnt: 0x002b, mh_fd_link: 0x7fffbc1b2330
mp_treebin[08] - sz: 0x00001800 cnt: 0x0326, mh_fd_link: 0x7fffc9368e10
mp_treebin[09] - sz: 0x00002000 cnt: 0x0038, mh_fd_link: 0x7fffc92e9c60
mp_treebin[10] - sz: 0x00003000 cnt: 0x00b9, mh_fd_link: 0x7fffa4003380
mp_treebin[11] - sz: 0x00004000 cnt: 0x006d, mh_fd_link: 0x7fffc9457720
mp_treebin[12] - sz: 0x00006000 cnt: 0x023b, mh_fd_link: 0x7fffbc1ac000
mp_treebin[13] - sz: 0x00008000 cnt: 0x0018, mh_fd_link: 0x7fffc946f850
mp_treebin[14] - sz: 0x0000c000 cnt: 0x0030, mh_fd_link: 0x7fffc935fcc0
mp_treebin[15] - sz: 0x00010000 cnt: 0x0019, mh_fd_link: 0x7fffb44262b0
mp_treebin[16] - sz: 0x00018000 cnt: 0x0073, mh_fd_link: 0x7fffc93fe020
mp_treebin[17] - sz: 0x00020000 cnt: 0x0018, mh_fd_link: 0x7fffc94368f0
mp_treebin[18] - sz: 0x00030000 cnt: 0x000c, mh_fd_link: 0x7fffb0665010
mp_treebin[19] - sz: 0x00040000 cnt: 0x001c, mh_fd_link: 0x7fffa836d010
mp_treebin[20] - sz: 0x00060000 cnt: 0x000d, mh_fd_link: 0x7fffa8c55010
mp_treebin[21] - sz: 0x00080000 cnt: 0x001d, mh_fd_link: 0x7fffa9009010
mp_treebin[22] - sz: 0x000c0000 cnt: 0x0006, mh_fd_link: 0x7fffa9081010
mp_treebin[23] - sz: 0x00100000 cnt: 0x000a, mh_fd_link: 0x7fffb025f010
mp_treebin[24] - sz: 0x00180000 cnt: 0x000b, mh_fd_link: 0x7fffa8b38010
mp_treebin[25] - sz: 0x00200000 cnt: 0x000e, mh_fd_link: 0x7fffb008a010
mp_treebin[26] - sz: 0x00300000 cnt: 0x0007, mh_fd_link: 0x7fffb0323010
mp_treebin[27] - sz: 0x00400000 cnt: 0x0002, mh_fd_link: 0x7fffa87f5010
mp_treebin[28] - sz: 0x00600000 cnt: 0x0003, mh_fd_link: 0x7fffa83e9010
mp_treebin[29] - sz: 0x00800000 cnt: 0x0001, mh_fd_link: 0x7fffab328010
mp_treebin[30] - sz: 0x00c00000 cnt: 0x0001, mh_fd_link: 0x7fffd706d010
mp_treebin[31] - sz: 0xffffffff cnt: 0x0003, mh_fd_link: 0x7fffa9326010 [UNSORTED]
Wir implementieren in libmempool einen dlmalloc- und ptmalloc-„bewussten“ Callback, der von libdlmalloc oder libptmalloc aufgerufen werden soll, um deren Chunk-Annotationen um zusätzliche mempool-spezifische Daten zu ergänzen. Wenn beispielsweise libdlmalloc verwendet wird, um eine ausführliche Auflistung eines dlmalloc-Chunks auszugeben, und dieser Chunk einen Mempool-Header enthält, gibt der Mempool-Callback diesen aus.
Informationen dazu, wie dies bei der Verwendung über andere Tools aussieht, finden Sie in den README-Dateien von libdlmalloc und libptmalloc.
Wir werden libmempool wahrscheinlich nach Bedarf oder im Rahmen zukünftiger Cisco-ASA-Forschung um Funktionen erweitern. Derzeit geplante Ergänzungen sind:
Wir freuen uns über Feedback zu diesem Tool und nehmen auch gerne Pull Requests entgegen.
Aaron Adams
aaron<dot>adams<at>nccgroup<dot>trustCedric Halbronn
cedric<dot>halbronn<at>nccgroup<dot>trust