
Heap analysis tooling for mempool
사전 참고: 이 도구는 asatools의 일부로 사용할 것을 권장하지만, 독립적으로도 사용할 수 있습니다.
libmempool은 Python 스크립트이자 GDB 분석 도구로,
Cisco ASA 장치의 다양한 힙 내부에서 발견되는 mempool 관련 데이터 구조를
분석하는 데 도움을 줍니다. 보통 이 정보는 힙 청크에 내장되어 있거나,
dlmalloc mstate 구조와 같은 다른 일반적인 힙 구조의
사용자 지정 확장입니다.
Cisco는 일반 할당, DMA 등 다양한 목적으로 매핑된 메모리 영역을 설명하기 위해 mempool이라는 용어를 사용합니다. 이러한 영역에는 일반적으로 dlmalloc과 같은 자체 힙이 포함됩니다. 이러한 mempool에서의 할당 루틴은 일반적으로 기본 힙 할당자를 감싸는 래퍼를 사용하여 수행되며, 이 래퍼는 결과 할당에 mempool 특정 메타데이터를 주입합니다. 이 메타데이터를 줄여서 mempool 헤더 또는 mh라고 부릅니다. 마찬가지로 mempool을 mp라고 자주 부릅니다. mempool 헤더를 설명하는 데 mh를 사용하는 것은 Cisco ASA lina 바이너리에서 발견되는 다양한 문자열과도 일치합니다.
libmempool은 mempool 헤더 및 데이터 구조와 관련된 일부 항목을 분석하기 위한 독립 실행형 도구로 사용할 수 있지만, 가장 큰 가치는 libdlmalloc이나 libptmalloc과 같은 다른 라이브러리의 콜백으로 사용될 때 발휘됩니다.
사용 중인 청크를 추적하는 데 사용되는 bins와 같은 mempool의 특정 측면은 dlmalloc 2.8.x mstate 구조 내부에 포함되어 있으며 동일한 bin 크기 체계를 따릅니다. 이는 거의 필연적으로 mempool 데이터를 캡슐화하는 mstate를 libdlmalloc을 사용하여 최소한 살펴봐야 함을 의미합니다.
libmempool은 dlmalloc2.8 또는 glibc의 ptmalloc2 기반 할당자를 사용하는 32비트/64비트 Cisco ASA 버전(ASA5500-X 시리즈 및 GNS3 모두)에서 테스트되었습니다. 수많은 8.x.y 및 9.x.y 브랜치를 포함한 여러 ASA 버전에서 테스트되었습니다. 그러나 특정 버전에서는 작동하지 않을 가능성도 충분히 있습니다.
libmempool을 독립 실행형으로 사용하려면 프로젝트에 libmempool.py 파일을
import하기만 하면 됩니다. 이를 통해 mpcallback 객체를 등록하는 등의
몇 가지 제한된 작업을 수행할 수 있습니다. 이는 기록된 힙 기능에 대한
오프라인 분석을 수행할 때 유용할 수 있습니다.
GDB에 import하려면 스크립트에는 Python을 지원하는 GDB만 필요합니다. 대부분의 최신 GDB 버전은 Python 3로 전환했지만 일부는 여전히 2.7을 요구합니다. 이 스크립트는 둘 다에서 테스트되었지만, 주로 개발 및 테스트는 Python 3로 수행됩니다.
(gdb) source libmempool_gdb.py
GDB 관련 로직의 대부분을 libmempool.py에서 분리하여 libmempool_gdb.py에 넣은 이유는 추상화를 테스트하고 GDB 외부에서도 libmempool.py를 쉽게 사용할 수 있도록 하기 위해서입니다. 이는 향후 변경될 가능성이 높습니다. 결국 libheap이나 shadow와 같은 다른 힙 분석 도구에서 사용하는 것과 유사한 디버그 엔진 추상화를 구현하고 싶기 때문입니다.
libmempool의 가치 중 상당수는 노출된 mpcallback 콜백 함수에서 나오지만,
살펴볼 수 있는 여러 기본 제공 GDB 명령이 있습니다.
(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
어떤 mempool 헤더의 주소를 알고 있다고 가정하면 해당 데이터를 분석할 수 있습니다. 이 주소는 mempool 헤더 자체의 주소여야 하며, 코어 할당자의 청크 메타데이터 주소가 아니라는 점에 유의하세요. 따라서 다음과 같이 내용을 덤프할 수 있습니다:
(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 (-)
}
-x를 사용하여 청크의 16진수 내용을 덤프할 수도 있습니다.
(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
}
mempool에는 bins 개념이 있습니다. bins는 dlmalloc bins와 동일한 크기의
이중 연결 리스트이지만, 해제된(free) 청크가 아니라 사용 중인(inuse) 청크를
추적하는 데 사용됩니다. 이는 Cisco 장치에서 메모리 사용량을 기록하기 위한
것입니다. mempool 헤더를 포함하는 청크를 발견했지만 힙에서 mstate 구조가
어디에 있는지 아직 모르는 경우가 자주 있습니다. 위치를 파악하려면 mpbin
명령을 사용할 수 있습니다. 이 명령은 현재 사용 중인 청크가 들어 있는
mempool bin의 주소를 알려줍니다. 예를 들어:
(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
이 명령은 mempool mstate 부분의 시작을 찾았을 뿐만 아니라 이를 캐시하고
청크가 존재하는 특정 bin을 나열합니다. 선택적으로 mpbinwalk 명령을 사용하여
이 bin의 모든 0xd3 청크를 나열하거나 특정 개수까지만
나열할 수 있습니다:
(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
[...]
이제 mstate 구조의 mempool 부분 전체(즉, 모든 bins 및 관련 통계)를 덤프하려면
mpmstate 명령을 사용할 수 있습니다:
(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]
}
libmempool 내부에는 dlmalloc 및 ptmalloc "aware" 콜백을 구현합니다. 이 콜백은 libdlmalloc 또는 libptmalloc이 호출하여 청크 주석에 추가적인 mempool별 데이터를 보강하도록 설계되었습니다. 예를 들어 libdlmalloc을 사용하여 dlmalloc 청크의 상세 목록을 출력할 때 해당 청크 내부에 mempool 헤더가 포함되어 있으면 mempool 콜백이 이를 출력합니다.
다른 도구를 통해 사용할 때 이것이 어떻게 보이는지에 대한 정보는 libdlmalloc과 libptmalloc의 README 파일을 참조하세요.
앞으로 필요에 따라 또는 향후 Cisco ASA 연구를 수행하면서 libmempool에 기능을 추가할 가능성이 높습니다. 현재 계획된 추가 사항은 다음과 같습니다:
이 도구에 대한 피드백을 듣고 싶으며, pull request도 기꺼이 받겠습니다.
Aaron Adams
aaron<dot>adams<at>nccgroup<dot>trustCedric Halbronn
cedric<dot>halbronn<at>nccgroup<dot>trust