
libtalloc is a python script for use with GDB that can be used to analyse the "trivial allocator" (talloc)
libtalloc은 GDB와 함께 사용할 수 있는 파이썬 스크립트로, "trivial allocator"(talloc)를 분석하는 데 사용됩니다. talloc에 대한 소개는 다음에서 찾을 수 있습니다:
https://talloc.samba.org/talloc/doc/html/index.html
libtalloc은 unmask_jemalloc 및 libheap과 같은 힙 분석용 gdb 파이썬 스크립트에서 영감을 받았습니다. 일부 기본 기능은 이 프로젝트들과 동일합니다.
https://github.com/cloudburst/libheap
https://github.com/argp/unmask_jemalloc
참고로 저는 파이썬 전문가가 아니며 코드 품질이 이를 반영합니다. 마음에 들지 않는 부분이 있다면 패치를 보내거나 제안을 주시기 바랍니다. 모든 피드백을 환영합니다.
libtalloc은 다양한 talloc 2.x 릴리스에서 테스트되었으며, 버전 간의 다양한 구조적 차이를 극복하기 위해 동적 버전 감지를 지원합니다. 32비트 및 64비트에서 테스트되었지만 완전하지 않으므로 가끔 작동이 중단되더라도 놀라지 마십시오.
x86 및 x64에서 어느 정도 테스트되었습니다:
다른 버전에서 테스트해 보셨다면, 작동 여부나 무엇이 깨졌는지 알려주시면 그에 맞춰 스크립트 및/또는 문서를 업데이트하도록 하겠습니다.
이 스크립트는 파이썬 지원이 포함된 비교적 최신 버전의 GDB만 필요합니다.
Ubuntu 12.04와 같은 일부 LTS 배포판은 여전히 python 2.7용 GDB를 사용하는 반면, 14.04 같은 최신 버전은 python 3.0을 사용합니다. 두 환경 모두에서 작동하도록 스크립트를 만들었으므로 다음과 같이만 하면 됩니다:
(gdb) source libtalloc.py
대부분의 기능은 복잡한 스위치 집합 대신 별도의 GDB 명령을 제공하는 unmask_jemalloc의 방식을 모델로 합니다.
talloc 라이브러리 C 함수를 흉내 내도록 특별히 설계된 여러 메서드가 제공되므로, 이미 라이브러리에 익숙한 사람이 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는 실제 계층 구조를 탐색하는 대부분의 함수에 필요하며, 이를 찾기 위해 tchunk 등의 다른 대부분의 기능은 자동으로 null_context를 찾으려고 시도합니다.
tcprobe를 실행한 후 실제로 tchunk를 사용하기 전:
(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 - Member of a pool (POOLMEM flag)
P - Chunk is a pool (POOL flag)
F - Chunk is free (FREE flag)
L - Chunk is looped (LOOP flag)
상세 출력:
(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
tchildren은 지정된 청크의 모든 자식(및 손자 등)을 표시하는 데 사용할 수 있습니다:
(gdb) tcchildren 0x80a13c88
0x80a14088: struct netr_Authenticator
0x80a14048: librpc/gen_ndr/ndr_netlogon.c:10964
0x80a14008: librpc/gen_ndr/ndr_netlogon.c:10958
0x80a13fc8: librpc/gen_ndr/ndr_netlogon.c:10951
0x80a13f88: lib/charcnv.c:506
0x80a13ec8: lib/charcnv.c:506
0x80a13d48: librpc/gen_ndr/ndr_netlogon.c:10913
0x80a13e08:
0x80a13cd8: struct ndr_pull
0x80a13f48: struct ndr_token_list
0x80a13f08: struct ndr_token_list
0x80a13e88: struct ndr_token_list
0x80a13e48: struct ndr_token_list
0x80a13dc8: struct ndr_token_list
0x80a13d88: struct ndr_token_list
talloc에는 풀 청크(pool chunk) 개념이 있습니다. 기본적으로 일반적인 talloc 청크이지만 시스템의 기본 malloc() 구현에 의존하는 대신 새 청크를 할당하는 데 사용됩니다. 풀 청크는 사용되는 버전에 따라 헤더가 약간 다르며, 때로는 패딩을 사용하고 때로는 접두사/접미사 헤더를 사용합니다.
tcpool은 tcchunk와 유사하게 풀 청크 헤더를 분석하는 데 사용할 수 있습니다:
# First we find a pool to analyze
(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
(gdb) tcpool -v 0x80a13248
struct talloc_pool_hdr @ 0x80a13248 {
end = 0x80a14108
object_count = 0x19
poolsize = 0x2000
struct talloc_chunk @ 0x80a13258 {
next = 0x0
prev = 0x0
parent = 0x809fb680
child = 0x80a13c58
refs = 0x0
destructor = 0x80429aa0
name = 0x0 (UNNAMED)
size = 0x0
flags = 0xe8150c74 (POOL)
limit = 0x0
pool = 0x0
위의 경우 풀에는 접두사로 talloc_pool_hdr가 있었으며, 이는 표시된 대로입니다. tcpool에 -l 옵션을 전달하면 풀 내에 할당된 모든 청크를 나열할 수 있습니다:
(gdb) tcpool -l 0x80a13248
Pool summary -- objects: 0x19, total size: 0x2000, space left: 0x1180, next free: 0x80a14108
0x80a13258 sz:0x00000000, flags:.P.., name:UNNAMED
0x80a13288 sz:0x000007a3, flags:..p., name:char
0x80a13a68 sz:0x0000005c, flags:..p., name:struct smb_request
0x80a13af8 sz:0x00000008, flags:..p., name:struct pipe_write_andx_state
0x80a13b38 sz:0x00000038, flags:..p., name:struct tevent_req
0x80a13ba8 sz:0x00000028, flags:..p., name:struct tevent_immediate
0x80a13c08 sz:0x00000014, flags:..p., name:struct np_write_state
0x80a13c58 sz:0x00000000, flags:..p., name:talloc_new: ../lib/util/talloc_stack.c:147
0x80a13c88 sz:0x00000020, flags:..p., name:struct netr_ServerPasswordSet
0x80a13cd8 sz:0x00000038, flags:..p., name:struct ndr_pull
0x80a13d48 sz:0x00000001, flags:..p., name:librpc/gen_ndr/ndr_netlogon.c:10913
0x80a13d88 sz:0x00000010, flags:..p., name:struct ndr_token_list
0x80a13dc8 sz:0x00000010, flags:..p., name:struct ndr_token_list
0x80a13e08 sz:0x00000001, flags:..p., name:
0x80a13e48 sz:0x00000010, flags:..p., name:struct ndr_token_list
0x80a13e88 sz:0x00000010, flags:..p., name:struct ndr_token_list
0x80a13ec8 sz:0x00000008, flags:..p., name:lib/charcnv.c:506
0x80a13f08 sz:0x00000010, flags:..p., name:struct ndr_token_list
0x80a13f48 sz:0x00000010, flags:..p., name:struct ndr_token_list
0x80a13f88 sz:0x00000008, flags:..p., name:lib/charcnv.c:506
0x80a13fc8 sz:0x0000000c, flags:..p., name:librpc/gen_ndr/ndr_netlogon.c:10951
0x80a14008 sz:0x00000010, flags:..p., name:librpc/gen_ndr/ndr_netlogon.c:10958
0x80a14048 sz:0x0000000c, flags:..p., name:librpc/gen_ndr/ndr_netlogon.c:10964
0x80a14088 sz:0x0000000c, flags:..p., name:struct netr_Authenticator
0x80a140c8 sz:0x0000000b, flags:..p., name:/etc/samba
위 출력에서 P 플래그에 주목하세요. 맨 위 청크가 아래의 모든 청크를 보유하는 풀 청크입니다.
tcdump는 전체 트리의 모든 청크를 덤프하는 데 사용할 수 있습니다. 기본적으로 계층 순서로 표시되지만, -s 옵션을 사용하면 주소 순서로 출력을 정렬할 수 있습니다.
(gdb) tcdump -a 0x809f8300
0x809f8300 sz:0x00000000, flags:...., name:null_context
0x80a0b3f8 sz:0x0000000c, flags:...., name:struct handle_list
0x809ff178 sz:0x00000014, flags:...., name:struct security_token
0x80a089e8 sz:0x00000198, flags:...., name:lib/util_nttoken.c:50
0x80a07268 sz:0x00000188, flags:...., name:connection_struct
0x80a08bc8 sz:0x00000020, flags:...., name:struct fd_handle
0x80a00c30 sz:0x000000f0, flags:...., name:struct files_struct
0x80a083b8 sz:0x00000008, flags:...., name:struct fake_file_handle
0x80a08c20 sz:0x0000009c, flags:...., name:struct pipes_struct
0x80a07900 sz:0x00000760, flags:...., name:uint8_t
0x80a07428 sz:0x000000c0, flags:...., name:struct auth_serversupplied_info
0x80a06390 sz:0x00000001, flags:...., name:
0x80a06350 sz:0x00000007, flags:...., name:nobody
0x80a06158 sz:0x000000cc, flags:...., name:struct netr_SamInfo3
0x80a062d8 sz:0x00000044, flags:...., name:struct dom_sid
[SNIP]
tcreport는 tcdump와 유사한 명령이지만 출력을 다소 보기 좋게 정리하며 talloc 라이브러리 자체에서 제공하는 talloc_report_full() 디버그 함수를 모방하기 위한 것입니다.
(gdb) tcreport 0x80a0b3f8 -a
Full talloc report on 'null_context' (total 558651 bytes in 446 blocks)
struct handle_list contains 12 bytes in 1 blocks (ref 67) 0x80a0b3f8
struct security_token contains 428 bytes in 2 blocks (ref 66) 0x809ff178
lib/util_nttoken.c:50 contains 408 bytes in 1 blocks (ref 0) 0x80a089e8
connection_struct contains 531071 bytes in 36 blocks (ref 65) 0x80a07268
struct fd_handle contains 32 bytes in 1 blocks (ref 4) 0x80a08bc8
struct files_struct contains 529681 bytes in 22 blocks (ref 3) 0x80a00c30
struct fake_file_handle contains 529308 bytes in 19 blocks (ref 1) 0x80a083b8
struct pipes_struct contains 529300 bytes in 18 blocks (ref 0) 0x80a08c20
uint8_t contains 1888 bytes in 1 blocks (ref 2) 0x80a07900
struct auth_serversupplied_info contains 934 bytes in 10 blocks (ref 1) 0x80a07428
contains 1 bytes in 1 blocks (ref 4) 0x80a06390
nobody contains 7 bytes in 1 blocks (ref 3) 0x80a06350
struct netr_SamInfo3 contains 290 bytes in 4 blocks (ref 2) 0x80a06158
[SNIP]
검색에는 tcsearch와 tcfindaddr 두 가지 명령이 있습니다.
tcsearch는 제공된 16진수 값을 포함하는 청크를 찾는 데 사용할 수 있습니다. null_context(알려진 경우)에서 시작하거나 제공된 시작 청크에서 전체 트리 계층 구조를 탐색하는 방식으로 작동합니다.
(gdb) python set_destructor(tc_chunk(0x80a13c88), 0x41414141)
(gdb) tcsearch 0x41414141 0x809f8300
[libtalloc] 0x41414141 found in chunk at 0x80a1d218
[libtalloc] 0x41414141 found in chunk at 0x80a13c88
(gdb) tcchunk -v 0x80a1d218
struct talloc_chunk @ 0x80a1d218 {
next = 0x80a00158
prev = 0x809fb0c8
parent = 0x0
child = 0x0
refs = 0x0
destructor = 0x0
name = 0x8071fcbd (uint8_t)
size = 0x80050
flags = 0xe8150c70 ()
limit = 0x0
pool = 0x0
(gdb) tcchunk -x 0x80a1d218
0x80a1d218 sz:0x00080050, flags:...., name:uint8_t
Chunk data (524368 bytes):
0x80a1d248: 0x41414141 0x00000000 0x00000000 0x00000000
0x80a1d258: 0x00000001 0x00000000 0x00000001 0x00020000
0x80a1d268: 0x00000001 0x00000000 0x00000001 0xaaaa0000
[SNIP]
(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
tcfindaddr는 주소가 talloc 트리 내 청크의 경계 안에 속하는지 확인하는 데 사용할 수 있습니다. 예를 들어, 사용자가 제어하는 일부 데이터가 0x80a1d3280에 있다고 알고 있고, 그것이 청크 내에 속하는지 확인하려는 경우입니다. 두 번째 주소는 null_context이지만 힙의 최상위를 찾을 수 있는 청크라면 무엇이든 될 수 있습니다.
(gdb) tcfindaddr 0x80a1d328 0x809f8300
[libtalloc] address 0x80a1d328 falls within chunk @ 0x80a1d218 (size 0x80050)
트리 검색의 일부는 tcwalk 명령을 통해 노출한 재귀 함수를 사용하여 수행됩니다. 이 함수는 트리에서 발견된 모든 청크에 대해 호출될 파이썬 메서드를 지정할 수 있게 해주는 헬퍼 함수입니다.
아래 예에서는 힙의 모든 청크에 대해 힙 검증 메서드를 호출하여 손상된 부분이 있는지 확인합니다.
(gdb) tcwalk validate_chunk
0x80a13c88: Chunk has bad destructor pointer 0x41414141
null_context가 설정되지 않은 경우 두 번째 인수로 청크 주소를 전달해야 합니다.
작성자: Aaron Adams
이메일: aaron (dot) adams (at) nccgroup (dot) trust
트위터: @fidgetingbits