
Heap-Analyse-Tooling für 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: