
libtalloc is a python script for use with GDB that can be used to analyse the "trivial allocator" (talloc)
libtalloc هو سكربت بايثون يُستخدم مع GDB ويمكن استخدامه لتحليل "المُخصِّص البسيط" (talloc). يمكن العثور على مقدمة حول talloc هنا:
https://talloc.samba.org/talloc/doc/html/index.html
استُلهم libtalloc من سكربتات gdb الأخرى المكتوبة بلغة بايثون لتحليل الأكوام مثل unmask_jemalloc و libheap. بعض الوظائف الأساسية مطابقة لهذه المشاريع.
https://github.com/cloudburst/libheap
https://github.com/argp/unmask_jemalloc
يُرجى ملاحظة أنني لست خبيرًا في بايثون وجودة الكود تعكس ذلك. إذا رأيت شيئًا يزعجك، فلا تتردد في إرسال تصحيح أو تقديم بعض الاقتراحات. كل الملاحظات مرحب بها.
تم اختبار libtalloc على مجموعة متنوعة من إصدارات talloc 2.x ويدعم كشف الإصدار ديناميكيًا لمحاولة التغلب على الاختلافات البنيوية المختلفة بين الإصدارات. تم اختباره على أنظمة 32-bit و 64-bit، ولكن ليس بشكل شامل، لذا لا تتفاجأ إذا تعطل من وقت لآخر.
تم اختباره إلى حد ما على x86 و x64:
إذا اختبرته على إصدار آخر، فيُرجى إخباري إن كان يعمل، أو بما تعطل وسأحاول تحديثه و/أو الوثائق وفقًا لذلك.
يتطلب السكربت فقط إصدارًا حديثًا نسبيًا من GDB مع دعم بايثون.
بعض توزيعات LTS، مثل Ubuntu 12.04، لا تزال تستخدم GDB مع بايثون 2.7، بينما تستخدم الإصدارات الأحدث مثل 14.04 بايثون 3.0. لقد حاولت جعل هذا السكربت يعمل مع كليهما، لذا تحتاج فقط إلى:
(gdb) source libtalloc.py
معظم الوظائف مستوحاة من نهج unmask_jemalloc، حيث يتم توفير أمر GDB منفصل بدلاً من مجموعة معقدة من الخيارات.
يتوفر عدد من الطرق المصممة خصيصًا لمحاكاة دوال مكتبة 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 وغيرها، العثور عليه تلقائيًا.
بعد تشغيل 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
يمكن استخدام tcchildren لعرض جميع أطفال (وأحفاد، وما إلى ذلك) القطعة المقدمة:
(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 مفهوم قطع التجمعات. وهي في الأساس قطع 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 بادئ، وهو ما يظهر. يمكن تمرير الخيار -l إلى tcpool لعرض جميع القطع المخصصة داخل التجمع:
(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_report_full() التي توفرها مكتبة talloc نفسها.
(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 للعثور على القطع التي تحتوي على القيمة الست عشرية المقدمة. يعمل عن طريق اجتياز التسلسل الهرمي للشجرة بأكمله بدءًا من 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