بازیابی داده ها و انواع: ساختارها، آرایه ها، C ++ اشیاء و DWARF
a->balanceوقتی که یه میز تایپ داره و*(int *)(param_1 + 4)این درس جدول را از طریق دسترسی ها می سازد، جدول واقعی را از DWARF 5 می خواند و نشان می دهد که دو مورد کجا موافق هستند.Type: Build
Languages: Python, TypeScript, C, C++, x86-64 assembly
Prerequisites: Phase 13 · 33 (disassembly and control flow), Phase 13 · 34 (lifting to pseudocode)
Time: ~140 minutes
اهداف یادگیری
- دسترسی عضو ساختار را به عنوان ثبت پایه به علاوه جابجایی و دسترسی صف به عنوان مرحله زمان پایه به علاوه شاخص از قوانین مدیریت SDM اینتل بخوانید.
- قوانین تعادل سیستم V را به دست به
struct accountو هر بايت پوشاندن رو توضیح بده - یک گذرنامه بازیابی نوع بسازید که دسترسی های یک لیست را به تعویض میدان، عرض، نوع، قدم و اندازه تبدیل می کند، با شواهد برای هر یک.
- برای خواننده DWARF 5 بنویسید
.debug_info،.debug_abbrev،.debug_str_offsetsو.debug_addrاز یک شی قابل انتقال، و آن را باllvm-dwarfdump. . - طرح بازیابی شده را با طرح DWARF مقایسه کنید و هر تفاوت را به عنوان شواهد گمشده، دسترسی ادغام شده یا نوع غیر قابل اثبات نام ببرید.
- یک Itanium C ++ vtable، typeinfo و نام های خرابش را از نقل مکان ها بخوانید و یک تماس مجازی را به یک اسلات حل کنید.
مشکل
تو جمع آوری کردیaccount.cبدون-gو از يه مامور پرسيدم چيbumpمامور اين كسي رو به گيدرا 12.1.4 فرستاد و اينو به دست آورد:
void bump(long param_1,int param_2)
{
*(int *)(param_1 + 4) = param_2 + *(int *)(param_1 + 4);
return;
}همون منبع که با -gاین را از همان کمپایلر می دهد:
int bump(account *a,int by)
{
a->balance = by + a->balance;
return a->balance;
}دستورالعمل ها در هر دو شی یکسان است. تفاوت یک جدول نوع است. با جدول، تجزیه کننده می داند که پارامتر اول به یک ساختار 32 بیت اشاره دارد که میدان آن در آفسه 4 یک 4 بیت است.intنامشbalanceبدونش ، دستگاه تجزیه اطلاعات فقط مي داند که چيزي که 8 بايت عرض داره وارد شده%rdiو این 4 بایت در +4و نوشته شده بود.
همه چیز در این جدول دارای منبع است. اوفسیت 4 از بایت جابجایی ازaddl 0x4(%rax), %ecx. عرض 4 از lاسمش از یکDW_TAG_memberورود به .debug_infoاین درس دو بخش را تشکیل می دهد: یک پاس که آنچه را که دستورالعمل ها ثابت می کند را بازیابی می کند و یک خواننده که آنچه را که سازنده ثبت کرده است را رمزنگاری می کند. شکاف بین آنها هزینه نام نوع است.
دسترسی به عضو یک جابجایی است
بخش 3.7.5 اینتل SDM جلد 1، آدرس موثر را به عنوان تعریف می کند.Offset = Base + (Index * Scale) + Displacement، با مقیاس 1، 2، 4 یا 8 و جابجایی 8، 16 یا 32 بیت. بخش 3.7.5.1 فرم حالت 64 بیت را اضافه می کند RIP + Displacement. یک دسترسی عضو ساختار از فرم اول بدون شاخص استفاده می کند: رکورد پایه آدرس شی را دارد و جابجایی عضو را تعویض می کند. یک دسترسی صف عبارت شاخص را اضافه می کند و مقیاس یا ضرب جداگانه اندازه عنصر را دارد.
اینو بگیرbumpدر-O0. لیست ازllvm-objdump -d -rمیگه:
4: 48 89 7d f8 movq %rdi, -0x8(%rbp)
e: 48 8b 45 f8 movq -0x8(%rbp), %rax
12: 03 48 04 addl 0x4(%rax), %ecx
15: 89 48 04 movl %ecx, 0x4(%rax)رمزگشایی03 48 04با قوانین درس 3303.ADD r32, r/m32. ModRM48.01 001 000.mod = 01یک اپراند حافظه با 8 بیت تغییر مکان است. reg = 001.%ecx،r/m = 000.%raxبايت جابجا شدن04. دستورات 4 بايت در %rax + 4و آنها را به %ecx.lپس از آنllvm-objdumpچاپ ها اندازه اپراند است، پس میدان 4 بایت گسترده است.%raxاز سمت قاب اومده-0x8(%rbp)، و اون سوراخ رو گرفتم%rdi، اولین حجت عدد کامل در کنوانسیون تماس سیستم V است. سه واقعیت در پی است: پارامتر اول یک اشارهگر است، شی که به آن اشاره می کند دارای یک میدان 4 بایت در آفسه 4 است و bumpاون فيلد رو ميخواد و ميخواد بنويسه
هيچ چيز در اين بايت ها نميگهint،balance، یاstruct accountهيچ چيزي نميگه که اين جسم چقدر بزرگه باقي مانده 28 بايت ساختمون براي مردم نامرئي هستنbump. .
قوانین طرح سیستم V
x86-64 psABI، بخش 3.1.2 سه قانون را برای تجمعات می دهد. "ساختورها و اتحادیه ها موازی ترین بخش هماهنگ خود را فرض می کنند. هر عضو به پایین ترین تعادل موجود با موازی مناسب اختصاص داده می شود. اندازه هر شی همیشه چند برابر موازی آن شی است". شکل 3.1 از همان سند اندازه و موازی اسکالر را نشان می دهد: char1 و 1، short2 و 2int4 و 4double8 و 8، هر اشاره ای 8 و 8
قوانین را به ساختار درaccount.c:
struct account {
char kind;
int balance;
short code;
double rate;
const char *label;
};| Member | Size | Alignment | Lowest offset that fits | Padding before |
|---|---|---|---|---|
kind | 1 | 1 | 0 | 0 |
balance | 4 | 4 | 4 | 3 (bytes 1 to 3) |
code | 2 | 2 | 8 | 0 |
rate | 8 | 8 | 16 | 6 (bytes 10 to 15) |
label | 8 | 8 | 24 | 0 |
آخرین عضو در 32 پایان می یابد. سخت ترین خط 8 است و 32 ضرب 8 است، بنابراین اندازه 32 بدون پوشاندن دم است. 9 بایت از 32 پوشاندن است و psABI می گوید "محتوی هر پوشاندن تعریف نشده است".
ترتيب اعضای تصمیم به اندازه می گیرد.double, pointer, int, short, charتعویض پر کردن 0, 8, 16, 20, 22 و پایان دادن به 23, که تا 24 دور می شود. sysv_layout()درcode/main.pyاین سه قانون را اعمال می کند و هر دو نتیجه را به دست می آورد.ctypesماژول از طریق کنوانسیون سازنده میزبان قوانین مشابهی را اعمال می کند وctypes.sizeof،ctypes.alignmentو.offsetاز هر زمینه به 32، 8 و 0, 4, 8, 16, 24برای همان ساختار. برنامه خود نسخه سوم را چاپ می کند وقتی که آن را در این دستگاه بسازید و اجرا کنید:
sizeof 32 align 8
kind 0 balance 4 code 8 rate 16 label 24
sum 315 tag -3 projected 318.75 label_length 7این ساخت بومی arm64 است. آرم AAPCS64 قوانین جمع آوری مشابه را در بخش 5.10.1 خود بیان می کند و سند آپل arm64 این پنج اندازه مقیاس را حفظ می کند، بنابراین اعداد در دو هدف موافق هستند. اشیاء x86-64 درس با -target x86_64-unknown-linux-gnuتا خواننده ELF در درس 33 و خواننده DWARF زیر روی آنها کار کنند.
بقیه ساختار را دست به دست بخوانید
bumpیک میدان را می بیند. چهار عملکرد دیگر در account.cدر ادامه به نظر می رسد که هر کدام از این موارد، یک فرم دستورالعمل متفاوت را نشان می دهد.account-x86_64-O0-g.objdump.txt. .
sum_balancesشاخصه های یک سری از ساختار را نشان می دهد:
51: 48 8b 45 f8 movq -0x8(%rbp), %rax
55: 48 63 4d ec movslq -0x14(%rbp), %rcx
59: 48 c1 e1 05 shlq $0x5, %rcx
5d: 48 01 c8 addq %rcx, %rax
60: 8b 40 04 movl 0x4(%rax), %eax%rax.list،%rcx.iعلامت به 64 بیت گسترش یافته وshlq $0x5بعد از 32 ضرب می شودaddq،%rax.list + 32 * i، و بار 4 بايت در تعویض 4 عنصر را می خواند i. شمارش تغییر تنها جایی در این شی است که اندازه ساختار به عنوان یک عدد ظاهر می شود. حلقه ای که شامل این دستورالعمل ها است از 0x49بهjmp 0x49در0x72. .
tagدو رشته تنگ رو ميخواد:
8c: 0f be 00 movsbl (%rax), %eax
98: 0f bf 48 08 movswl 0x8(%rax), %ecxmovsblیک بایت را در آفسیت 0 بارگذاری می کند و آن را نشان می دهد، بنابراین میدان در 0 یک عدد کامل 1 بایت است. movswlدو بایت در آفسیت 8 با تمدید علامت بارگذاری می کند، بنابراین میدان در 8 یک عدد کامل 2 بایت امضا شده است.-O1اولین دسترسی به این موضوع می شودcmpb $0x6c, (%rdi)، که هنوز هم می گوید 1 بایت در تعویض 0 و هیچ چیز در مورد علامت می گوید.
projectedيه خط مي خواد:
cc: f2 0f 2a 40 04 cvtsi2sdl 0x4(%rax), %xmm0
d5: f2 0f 59 40 10 mulsd 0x10(%rax), %xmm0cvtsi2sdlتبدیل یک عدد کامل 32 بیت امضا شده از حافظه به دوگانه، که تایید می کند که آفسیت 4 دارای یک عدد کامل 4 بیت امضا شده است. mulsdضربش دو برابر از حافظه، پس آفسه 16 دارای یک 8 بایت نقطه شناور است.
label_lengthیک اشاره را می خواند و سپس آن را دنبال می کند:
f3: 48 8b 40 18 movq 0x18(%rax), %rax
f7: 48 63 4d f4 movslq -0xc(%rbp), %rcx
fb: 80 3c 08 00 cmpb $0x0, (%rax,%rcx).movqدر خود این یک عدد کامل 8 بایت یا یک اشاره است.cmpbیک مقدار که به عنوان یک آدرس استفاده می شود یک اشاره است.cmpb1 بايت در شاخص را می خواند %rcxاز آن، و در-O1اشاره به جلو با leaq 0x1(%rcx), %rcxدر داخل حلقه، بنابراین عناصر اشاره شده به 1 بایت فاصله دارند.
پنج تابع رو به هم ببندیم و ساختارش{ i8 at 0, i32 at 4, i16 at 8, f64 at 16, pointer at 24 }با قدم 32 فاصله های 1 تا 3 و 10 تا 15 دقیقا همان پوششی است که قوانین psABI پیش بینی می کنند. آنچه که دستورالعمل ها نمی گویند: نام اعضای، که اشاره کننده است const char *, نوع اعلام شده بايت امضا شده در 0 که منبع اش را نشان می دهد char، و این پنج تابع به همان نوع می رسند.main، که یک جسم قاب را به تمام پنج مورد منتقل می کند.-O0سایت های تماس این را قابل مشاهده می کنند: leaq -0x70(%rbp), %rdiبعدشaddq $0x20, %rdiقبل ازcallq bumpوaddq $0x40, %rdiقبل ازcallq tag. تعویضات 00x20و0x40این سه عنصر یک آرایه هستند وmemcpyاز0x60بائتهایی که آن را پر می کنند 96 بائتهایی است که سه عنصر از 32 است.
گذرنامه
trace_function()درcode/main.pyیک نگهدارنده یک ثبت 64 بت یا یک فولاد فریم مانند -0x8(%rbp)هر صاحب یکی از سه ارزش داره.Pointerیک ریشه به علاوه یک تعویض ثابت به علاوه مجموعه ای از گامها است، جایی که یک گام ثبت می کند که ضرب نامعلوم از این بسیاری از بایت ها اضافه شده است.Indexیک عدد کامل با ارزش ناشناخته است که ضرب یک مقیاس است. هر چیزی دیگر هیچ نیست.
دولت با داشتن شش ثبت کننده استدلال سیستم V شروع می کندPointer("param0", 0)از طریقPointer("param5", 0)پس هر دستورالعمل يه قاعده رو اعمال ميکنه
- A
movqبین یک register 64-bit و یک slot frame مقدار را کپی می کند. یک 32bit یا باریک تر برای یک register آن را یکIndexبا مقیاس 1 - A
leaqبا یک پایه اشاره ای، یک اشاره ای را با اضافه کردن جابجایی تولید می کند و با یک فهرست شاخص اضافه می کندscale * index_scaleبه قدم هاleaqکه پایه اش%rbpیا%rspنام یک جسم قاب را می دهد، که چگونه استmainگذرگاهrows. . shlq $nدر یکIndexمقیاس آن را با2^nوimulq $kضرب شده توسطk. .addqاز یک شاخص و یک شاخص نگه می دارد و مقیاس شاخص را به گام ها اضافه می کند.addq $kبه یک اشاره کننده، تعویض آن منتقل می شود و همچنین به عنوان یک افزونه از آن ثبت می شود.- یک تماس باعث می شود هر رکورد ذخیره شده توسط تماس گیر نامعلوم باشد و رکورد های استدلال که اشاره دارند به عنوان یک سایت تماس ثبت می شوند.
- هر آپراند حافظه ای که در رکورد پایه اش اشاره ای هست، تبدیل به یک
Accessبا ریشه و تعویض اشاره کننده، عرض و نوع از mnemonic، و پرچم فروشگاه.
عرض و نوعيت فقط از يادداشت ميادaccess_width()نقشه هاb،w،lوqپس از 1، 2، 4 و 8 بایت، movsblوmovswlبرای حمل های باریک امضا شده،movzblبه امضا نشده،sdوssدستورالعمل های SCE مقیاس برای شناور های 8 بایت و 4 بایتcvtsi2sdlبه یک منبع کامل 4 بایت امضا شده و 16 بایت movapsخانواده به یک متری. leaqوnopفرم ها دسترسی نیستند.
if memory.base in FRAME_REGISTERS:
return Pointer(f"frame({memory.base}{format_signed(memory.displacement) if memory.displacement else ''})", 0)
base = env.get(memory.base)
if not isinstance(base, Pointer):
return Unknown
strides = set(base.strides)
if memory.index is not None:
index_value = env.get(memory.index)
strides.add((index_value.scale if isinstance(index_value, Index) else 1) * memory.scale)
return Pointer(base.root, base.offset + memory.displacement, frozenset(strides), base.origin)بار 8 بایت از طریق یک اشاره دهنده یک اشاره دهنده جدید را تولید می کند که ریشه نام های زمینه ای را از آن گرفته است، مانند param0->0x18وقتی که این مقدار بعداً به عنوان پایه استفاده می شود یا با leaqاین قانون است که 24 را از "8 بایت" به "پاینتر" تبدیل می کند.
چهار نوع شواهد قدم
infer_layout()در هر مرحله از هر کار شاهد را ادغام می کند و قدم را از چهار منبع جمع می کند، هر کدام با دستورالعمل تولید کننده آن.
| Kind | Where it comes from | Build that shows it |
|---|---|---|
scale | an Index with scale above 1 reached a pointer, from shlq $0x5 or imulq | -O0, shlq $0x5, %rcx at 0x59 |
loop-step | a 64-bit increment of the base or index register inside a loop that also contains the access | -O1, addq $0x20, %rdx at 0x24 |
offset-delta | same-width accesses through one base register inside one loop, at offsets a constant apart | -O2, eight loads 32 apart |
call-site | the offsets at which one frame object is passed to the witness functions | -O0, main passes rows, rows+0x20, rows+0x40 |
یک حلقه یک عقب استjmpیا شاخه مشروط، و یک افزونه تنها زمانی محاسبه می شود که محدوده حلقه شامل هر دو افزونه و دسترسی است.addl $0x1, %eaxکه پیشرفت می کنهiدر-O0، چون این یک 32 بت اضافه شده است و چون دسترسی از طریق نمی رود %eax. مرحله نهایی بزرگترین تقسیم مشترک هر مقدار یافت شده است. اندازه مرحله زمانی است که مرحله حداقل به اندازه پایان آخرین میدان بزرگ است.
حلقه ویکتور در -O2
در-O2وکتورهای کلینگsum_balances. بدن حلقه از0x50به0xaaهشت عنصر در هر گذرنامه رو میخواد:
3b: 48 8d 87 e4 00 00 00 leaq 0xe4(%rdi), %rax
50: 66 0f 6e 50 80 movd -0x80(%rax), %xmm2
55: 66 0f 6e 98 60 ff ff ff movd -0xa0(%rax), %xmm3
61: 66 0f 6e 90 40 ff ff ff movd -0xc0(%rax), %xmm2
69: 66 0f 6e a0 20 ff ff ff movd -0xe0(%rax), %xmm4
7d: 66 0f 6e 10 movd (%rax), %xmm2
81: 66 0f 6e 58 e0 movd -0x20(%rax), %xmm3
8a: 66 0f 6e 50 c0 movd -0x40(%rax), %xmm2
8f: 66 0f 6e 60 a0 movd -0x60(%rax), %xmm4
a0: 48 05 00 01 00 00 addq $0x100, %rax
aa: 75 a4 jne 0x50 <sum_balances+0x40>%raxشروع می شهlist + 0xe4. هشت جابجا شدن بار ها رو به جاي تعويض ميذاره0x04, 0x24, 0x44, 0x64, 0x84, 0xa4, 0xc4, 0xe4ازlist، که4, 36, 68, 100, 132, 164, 196, 228. افزایش اینه0x100، که 256 است. یک گذر که به صورت مشاهده آنها را تعویض می کند، هشت زمینه و ساختار حداقل 232 بایت را گزارش می دهد. لیست درست است و نتیجه گیری اشتباه است.
fold_unrolled()در داخل یک حلقه، دسترسی های عرض مشابه را از طریق یک رکورد پایه بگیرید.dدر اینجا 32 است. اگر مرحله حلقهk * dو دقیقاً وجود دارهk.آفرین های متمایز ، حلقه باز شدkزمان ها و هر دسترسی همان عنصر دسترسی در آفسه(offset - lowest) mod d. اینجا256 = 8 * 32با هشت تعویض، پس تمام هشت بار بر روی تعویض 4 خم می شوند و مدارک ثابت قدم ثبت می شوندoffset-delta 32،unrolled 32وloop-step 256. حلقه بقيه اسکالري در0xe0اضافه می کندscale 32ازshlq $0x5, %rdxوloop-step 32ازaddq $0x20, %rdxبزرگترین تقسیم کننده مشترک از تمام پنج ارزش 32 است.
این شکل یه دکمه داره که خم کردن رو خاموش میکنه-O2این حالت چیزی است که یک خواننده می بیند که بدون حلقه به جابجایی اعتماد می کند.
DWARF 5 از بائیت ها
.-gساختaccount.cهشت بخش اصلاحات را در بر می گیرد..debug_info493 بایت است..debug_abbrev179, .debug_str230،.debug_str_offsets124, .debug_addr۱۱۲،.debug_line۳۱۲،.debug_line_str۱۲، و.debug_frame264 . معرفی لیست های استاندارد DWARF 5 .debug_str_offsets،.debug_addrو.debug_line_strبه عنوان تازه در نسخه 5 . خواننده در code/main.pyپنج تا اول رو ميخواد
سر و کله ی واحد
بخش 7.5.1.1 استاندارد، عنوان کامل واحد جمع آوری را می دهد: unit_length،version،unit_type،address_sizeوdebug_abbrev_offset. اولين 12 بايت از.debug_infoهستند:
e9 01 00 00 unit_length = 0x1e9 = 489
05 00 version = 5
01 unit_type = DW_UT_compile
08 address_size = 8
00 00 00 00 debug_abbrev_offset = 0.unit_typeبائیت در نسخه 5 جدید است و استاندارد در همان بخش می گوید. طول بائیت ها را پس از خود می شمرد، بنابراین واحد بعدی از 4 + 489 = 493، پایان بخشllvm-dwarfdump --debug-infoهمان پنج مقدار را روی آن چاپ می کند Compile Unit:خط، و اولین آزمایش آن خط را تجزیه و تحلیل می کند و مقایسه می کند.
اختصار
بخش 2.1 می گوید هر ورودی اطلاعات دیبگینگ "از یک برچسب شناسایی و یک سری از ویژگی ها تشکیل شده است". ورودی خود تنها یک کد اختصار را ذخیره می کند. کد یک بیانیه را در .debug_abbrev(قسم 7.5.3) که نام برچسب را می دهد، می گوید آیا کودکان پیروی می کنند و هر ویژگی را با شکل خود لیست می کند. این شی دارای 14 اعلامیه است. دو موضوع در اینجا:
[12] DW_TAG_structure_type DW_CHILDREN_yes
DW_AT_name DW_FORM_strx1
DW_AT_byte_size DW_FORM_data1
DW_AT_decl_file DW_FORM_data1
DW_AT_decl_line DW_FORM_data1
[13] DW_TAG_member DW_CHILDREN_no
DW_AT_name DW_FORM_strx1
DW_AT_type DW_FORM_ref4
DW_AT_decl_file DW_FORM_data1
DW_AT_decl_line DW_FORM_data1
DW_AT_data_member_location DW_FORM_data1parse_abbreviations()کد ULEB128، برچسب ULEB128، یک بایت بچه، سپس ویژگی و جفت فرم تا یک 0, 0بخش 7.5.6 کد های فرم را می دهد: data1یک بایت استref4یک تعویض 4 بایت نسبت به واحد است.strx1یک شاخص یک بایت در جدول تعویض رشته است.sec_offsetوdata44 بایت استaddrxیک شاخص ULEB128 در جدول آدرس است.exprlocطول ULEB128 و بايت هاي زيادي است وflag_presentاصلا بايت نداره
عضو ترازوی را دستاً رمزگذاری کنید
ورودی ساختار برای accountاز آفسه شروع میشه0x195با بايت ها0c 15 20 00 03کد اختصار 12، ساختارDW_AT_nameشاخص رشته ای است0x15،DW_AT_byte_size.0x20= 32DW_AT_decl_file0 است وDW_AT_decl_line3 است، که خط struct account {در منبع
دومین ورود عضو از 0x1a3با0d 10 88 01 00 00 00 05 04:
0dخلاصه 13، عضو است.10.DW_AT_nameمثلstrx1: شاخص رشته 1688 01 00 00.DW_AT_typeمثلref4: تعویض0x188از شروع واحد، کهDW_TAG_base_typeشماره شمارهint. .00.DW_AT_decl_fileو05.DW_AT_decl_line: خط 5 ازaccount.c. .04.DW_AT_data_member_location: تعویض 4.
بخش 5.7.6 می گوید یک ورودی عضو "می تواند یا یک ویژگی DW_AT_data_member_location یا یک ویژگی DW_AT_data_bit_offset داشته باشد" و ثابت عددی "از ابتدای موجودی حاوی بائتهای تعویض شده است". این بائته همان عدد است که در addl 0x4(%rax), %ecxوcompare_layouts()تابع است که دو را کنار هم قرار می دهد.
رشته ها و آدرس ها از طریق دو جدول عبور می کنند
شاخص رشته 16 تعویض نشده است.debug_str. خواننده میخوادDW_AT_str_offsets_baseاز ورودی واحد که 8 است، و ورودی 4 بیت را در 8 + 4 * 16در.debug_str_offsets. اين ورودي معادلات رو داره"balance"در.debug_str. همان غیر مستقیم برای آدرس ها هم صدق می کند.sum_balancesبا بايت شروع ميشه07 07: مختصره 7، یک فرعي برنامه، پسDW_AT_low_pcمثلaddrxشاخص 7.8 بايت وارد شدن در 8 + 8 * 7در.debug_addr.0x30, آدرس تابع در .text. شش ورودی اول این جدول آدرس های شش رشته حرفی هستند
در یک شی که قابل انتقال است هیچ یک از جدول ها مقادیر نهایی خود را نگه نمی دارد..debug_str_offsetsصفر در پرونده است و.rela.debug_str_offsets29R_X86_64_32نقل مکان ها در مقابل.debug_strبا تعویض واقعی در اضافه..debug_addr13 تاR_X86_64_64نقل مکان ها در مقابل.textو.rodata.str1.1..debug_infoچهار نقل مکان خودش را دارد: اختصار "کافش"DW_AT_str_offsets_baseمثل.debug_str_offsets+0x8،DW_AT_stmt_listوDW_AT_addr_baseمثل.debug_addr+0x8.ElfObject.relocated()اعمال می شودS + Aبرای نوع 32 و 64 بیت قبل از اینکه خواننده بخش را تجزیه و تحلیل کند. یک نماد بخش دارای ارزش 0 در یک فایل شی است، بنابراین میدان اضافه را دریافت می کند. llvm-dwarfdumpاین در نظر گرفته شده است که این در نظر گرفته شده است که در هر شاخص مشخص شده، نام و آدرس را نشان می دهد.
انواع، عملکردها و مکان ها
DwarfInfo.type_name()ادامه داردDW_AT_typeمرجع هاlabelاعضای یک گروهDW_TAG_pointer_type، که به یک نقطه اشاره می کندDW_TAG_const_type، که به charنوع پایه، بنابراین نام تشکیل شده به const char *.type_size()بازپرداختDW_AT_byte_sizeبرای انواع پایه و ساختار، 8 برای یک اشاره کننده و شمارش ضربات اندازه عنصر برای یک آرایه. type_kind()کاهشDW_AT_encodingاز نوع پایه تا intبا یک علامت یاfloat، و یک نوع اشاره به pointer، که این لغت است که مقایسه استفاده می کند.
DwarfInfo.functions()شش تا میخوادDW_TAG_subprogramنوشته هاbumpاز0x0به0x21، کهDW_AT_low_pcبه علاوهDW_AT_high_pcطولش. پايه قابشDW_OP_reg6 RBP، و پارامترشaعبارت موقعیت داره91 78.DW_OP_fbregبا ارزش SLEB128 0x78، که -8 است. این سوراخ قاب است.-0x8(%rbp)که لیست آن ها ذخیره شده است%rdiدر آدرس0x4اطلاعات و دستورالعمل های دیبگ از دو طرف هم همان نقطه را توصیف می کنند.
دو طرح را مقایسه کنید
compare_layouts()به دنبال یک میدان نتیجه گیری در همان تعویض است.no-evidence. یک میدان با اندازه ی متفاوتsize-mismatch. يه عضو اشاره دار که هرگز از رشته اش رد نشدهpointer-unproven. نوعی که با این موضوع مخالف است، مانند یک فلوتر که در آن دستورالعمل ها از حساب اعداد کامل استفاده می کنند،kind-mismatch. حاشیه های زیر که از داخل عضو شروع می شوند یا داخل پوششی هستندunexpectedاندازه ي آن در برابر قدمDW_AT_byte_size. .
اعداد سه ساخت، ازpython3 code/main.py:
| Build | Accesses through the first parameter | Fields | Stride evidence | Members matched | Size |
|---|---|---|---|---|---|
-O0 | 11 | 5 | scale 32, call-site 32, call-site 64 | 5 of 5 | 32, match |
-O1 | 9 | 5 | loop-step 32 | 5 of 5 | 32, match |
-O2 | 17 | 5 after folding, 12 before | offset-delta 32, unrolled 32, loop-step 256, scale 32, loop-step 32 | 5 of 5 | 32, match |
شکافهای نتیجه گیری شده باایت های 1 تا 3 و 10 تا 15 در هر ساخت هستند و این دقیقا باایت های پوشاندن طرح DWARF است. هر میدان نتیجه گیری شده در ضربات اندازه خود قرار دارد، که این چیزی است که قوانین psABI نیاز دارد، و natural_alignment_ok()اون رو ضبط کن
تابع ها را به صورت یک به یک اضافه کنید و تصویر تغییر می کند.bumpتنها طرح یک میدان در آفسه 4 است، اندازه ناشناخته است و مرز پایین 8 است. sum_balancesقدم به قدم اضافه می کنهtagاضافه کردن تعادل 0 و 8projectedاضافه می کنه که شناور به 16 باشهlabel_lengthبه 24 اضافه می کند و ثابت می کند که یک شاخص است. mainسایت های تماس را در اضافه می کند-O0و هيچ چيز رو به هم نميده-O1و-O2، چون کامپایلر هر تماس را در خط قرار داد.
سه تا چيز در دسترس نيست اسم ها در دستورالعمل ها نيستconstدر دستورالعمل ها وجود ندارهlabelفقط "عنصرها 1 بایت جدا" از leaq 0x1(%rcx), %rcxدر حلقه رشته ای که با charو باunsigned charو_Bool. در-O1علامت بایت در آفسه 0 ناشناخته است، زیرا cmpb $0x6c, (%rdi)بدون تمدید مقایسه می کنه.int8اونجا وi8در-O0، کجاmovsblاينها حقيقتهاي درس 35 ثبت شده به عنوان ناشناخته
C++ اشیاء
shapes.cppتعریف می کندShapeبا یک نابود کننده مجازی، یک مجازی خالصarea()، و یک مجازیname()وRect، که ازShapeو دوتا اضافه می کنهintABI C++ Itanium نحوه تنظیم این موارد را در x86-64 لینوکس تنظیم می کند.
نام های مخرب
بخش 5.1.2 از قانون ABI میگه "یک اسم با پیشگویی خراب می شه_Zاین شی دارای 19 نماد است._ZNK4Rect4areaEvبه نظر مياد_Z، پسNبرای نام پوشیده شدهKبرای یک تابع عضو const، 4Rectو4areaبه عنوان نام های منبع با پیش فرض طول، Eبرای بسته شدن نام پوشیده شده وvبرای یک لیست پارامترهای خالی. llvm-cxxfiltچاپRect::area() const._Z8describeRK5Shapei.describe(Shape const&, int).Rیک مرجع استKاست const5Shapeکلاس استi.int. نام های ویژه در بخش 5.1.4 عبارتند از:_ZTVبرای یک میز_ZTIبرای اطلاعات نوع، و_ZTSبرای رشته نام typeinfo. ساختگان و تخریب کنندگان هستند C1،C2،D0،D1وD2، پس_ZN4RectD0Evو_ZN4RectD2Evهر دو چاپ شدهRect::~Rect(): تخریب کننده حذف کننده و تخریب کننده شی پایه یک نام منبع مشترک دارند.
Demanglerدرcode/main.pyاین زیر مجموعه را به همراه کد های نوع ساخت و ساز بخش 5.1.5.2 و S_جایگزین بخش 5.1.10 است. آزمایش آن را در تمام 19 نام انجام می دهد و با نام های ضبط شده مقایسه می کند.llvm-cxxfilt --no-strip-underscoreoutput. پرچم مهم است در macOS، جایی که ابزار به طور پیش فرض یک زیرنویس برجسته را حذف می کند و سپس نمی تواند تشخیص دهد Z8describe.... .
وبلد از نقل مکان هایش
بخش 2.5.2 ABI، اجزای یک جدول مجازی را به ترتیب: معادلات تماس مجازی، معادلات پایه مجازی، معادلات به بالا، اشاره کننده typeinfo و سپس اشاره کنندگان عملکرد مجازی را فهرست می کند. نقطه آدرس، ارزش vptr یک شی، آدرس اولین اشاره کننده عملکرد است (قسم 2.6.2). Rectهیچ پایه مجازی ندارد، بنابراین جدول آن به بالا، typeinfo و عملکردها تعویض شده است.
نماد_ZTV4Rectدر این شی هر بایت صفر است. محتوای آن در .rela.data.rel.ro._ZTV4Rect:
0000000000000008 R_X86_64_64 _ZTI4Rect
0000000000000010 R_X86_64_64 _ZN4RectD2Ev
0000000000000018 R_X86_64_64 _ZN4RectD0Ev
0000000000000020 R_X86_64_64 _ZNK4Rect4areaEv
0000000000000028 R_X86_64_64 _ZNK4Rect4nameEvSlot 0 نگه می دارد آفست به بالا، که 0 است و نیازی به نقل مکان ندارد. Slot 1 نوع اطلاعات است. نقطه آدرس در +16از اونجا، قسمت 0 نابود کننده کامل اجسام، قسمت 1 نابود کننده حذف، قسمت 2area، قسمت 3name.read_vtable()اندازه نماد، نقل مکان در محدوده اش و صفر را در تعویض 0 می خواند. Shapeمیزش شکل مشابه اونه__cxa_pure_virtualدر اسلايت 2، که تابع زمان اجرا است که یک تماس مجازی خالص به آن می رسد.
RTTI
بخش 2.9.4 شرح میدهدstd::type_infoبه عنوان vptr که توسط یک اشاره به نام نوع mangled دنبال می شود، و کلاس های مشتق را فهرست می کند. _ZTI4Rectسه بار نقل مکان کرده:_ZTVN10__cxxabiv120__si_class_type_infoE+0x10،_ZTS4Rectو_ZTI5Shapeاولين اين است که__si_class_type_info, کلاس ABI برای یک نوع با یک پایه عمومی غیر مجازی در صفر تعویض استفاده می کند و +0x10نقطه آدرس این vtable است. نقطه دوم در رشته4Rect, که نام نوع خراب شده است وname()در یکtype_infoدرست همان رشته را باز می گرداند. سومین پایه است._ZTI5Shapeاستفاده__class_type_infoو هیچ شاخص پایه ای ندارد.read_typeinfo()این سه واقعیت را باز می گرداند.
تماس مجازی
describeدر-O0يه تماس مجازی ميکنه:
f: 48 8b 7d f8 movq -0x8(%rbp), %rdi
13: 48 8b 07 movq (%rdi), %rax
16: ff 50 10 callq *0x10(%rax)اولينmovqبارگذاری اشاره کننده شی. دوم 8 بایت در آفسه 0 از شی را می خواند، که vptr است، زیرا ABI آن را در درجه اول در یک کلاس با عملکردهای مجازی قرار می دهد.callqاز طریق0x10و بعد از اون0x10 / 8در قسمت 2 هستfind_virtual_calls()با الگوی مشابه و گزارش هر دو نامزد: Rect::area() constاز_ZTV4Rectو__cxa_pure_virtualاز_ZTV5Shape. کدام یک از این ها به این موضوع بستگی داره که چرا تماس غیرمستقیمه
DWARF همون قسمت رو ضبط ميکنهDW_TAG_subprogramنوشته ها برای areaدر هر دو کلاس حملDW_AT_vtable_elem_location (DW_OP_constu 0x2)،nameدر قسمت سوم، و در قسمت صفر، در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، و در قسمت سوم، در قسمت سوم، و در قسمت سوم، در قسمت سوم، و در قسمت سوم، در قسمت سوم، در قسمت سوم، در قسمت سوم، در قسمت سوم، در قسمت سوم، در قسمت سوم، در قسمت سوم، در قسمت سوم، در قسمت سوم، در قسمت سوم، در قسمت سوم، در قسمت سوم، در قسمت سوم، در قسمت سوم، در قسمت سوم، در قسمت سوم، در قسمت سوم، در قسمت سوم، در قسمت سوم، در قسمت، در قسمت سوم، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در قسمت، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد، در مورد،RectیکDW_TAG_structure_type16 بایت با یک DW_TAG_inheritanceثبت نام برای Shapeدر تعویض 0 و اعضای wساعت 8 وhساعت 12Shape8 بايت با يک عضو مصنوعي است_vptr$Shapeدو مشتق مستقل به سمت 2area: جدول نقل مکان و ورودی های دیبگ
در-O1این شی اصلاً قابل استفاده نیست.mainساخت و سازRectبه عنوان یک محلی، بنابراین کامپایلر حل شد r.name()وdescribe(r, 2)هیچ چیز آدرس میز را نمی گیرد، بنابراین جدول منتشر نمی شود. describeهنوز هم شاملmovq (%rdi), %raxوcallq *0x10(%rax)، چون پارامترش مي تونه هرچي باشهShape. یک جدول گمشده یک واقعیت در مورد یک فایل شی و آزمون استtest_the_optimised_object_keeps_the_call_pattern_but_drops_the_vtablesهر دو قسمت رو چک کنه
ABI مایکروسافت در هر یک از این جزئیات متفاوت است. نام های تزئین شده با شروع می شوند ?و کد کنوانسیون تماس رو کدگذاری کنم، پسvoid test(void)به صورت C++ جمع آوری شده است__cdecl.?test@@ZAXXZ. مقادیر RTTI درvfptr[-1]به عنوان یک_RTTICompleteObjectLocator، و نماداشون??_7برای یک vftable و??_R4در اسناد مایکروسافت گفته شده است که "هیچ استاندارد برای نامگذاری C++ بین فروشندگان کامپیلر وجود ندارد".
چگونه ابزارها انواع بازیابی شده را نشان می دهد
هر سطری که زیرش نام گذاری شده است در این دستگاه تولید شده و به code/fixtures/tools/. صف هایی برای ابزار هایی که در اینجا نصب نشده اند، اسناد خود را توصیف و بیان می کنند.
| Tool | Version, licence | Where types live | On our fixtures |
|---|---|---|---|
| Ghidra | 12.1.4, 2026-09-21, Apache-2.0 | Data Type Manager with built-in, user-defined, and derived types; .gdt archives; types used by a program are copied into it | With DWARF: /DWARF/account.c/account size=32 align=8 and a->balance = by + a->balance;. Without: *(int *)(param_1 + 4) with long param_1, tag(char *param_1), and *(char **)(param_1 + 0x18) |
| IDA Pro | 9.4, 2026-07-13, commercial; IDA Free is non-commercial with a cloud decompiler for x86 only | Local Types window, .til type libraries, DWARF and PDB import into a dwarf or pdb folder | Not installed. Documentation only |
| Binary Ninja | 6.0, 2026-09-03, commercial with a Free edition | Type editing, header import and export, type archives, debug info from DWARF and PDB, type libraries, platform types | Not installed. Documentation only |
| radare2 | 6.2.4, 2026-10-05, LGPLv3 | ts, tsc, afvd | ts and tsc account printed nothing for the relocatable object with bin.dbginfo=true; pdf shows mov dword [rax + 4], ecx with source line comments from DWARF |
| rizin | 0.9.1, 2026-06-29, LGPL core | ts, tsc, afvd | tsc account printed struct account { char kind; int balance; short code; double rate; const char *label; }; from DWARF; the build without -g lists only its bundled header types |
| lldb | lldb-1703.0.236.21 | image lookup -t | byte-size = 32 and the full compiler_type text from the ELF object |
| gdb | not installed here | ptype /o prints "the offsets and sizes of fields in a struct" with holes marked | Documentation only |
| pyelftools | 0.33, public domain | Python API over .debug_info | A 43-line script prints the same five members and unit version 5 |
| llvm-dwarfdump | Apple LLVM 17.0.0 | --debug-info, --debug-abbrev, --debug-str-offsets | The oracle for every reader test |
تولید Ghidra بدون DWARF نشان می دهد که دو قانون مشابه در کار است.char *به پارامتر tagچون تابع با یک بایت در آن مقایسه می کند وchar **به میدان0x18چونlabel_lengthارزش بارگذاری را حذف می کند و باایت ها را مقایسه می کند. این دو قانون مشابهی است که در این درس به آن اشاره می شود: عرض از دستورالعمل، اشاره از اشاره. اسناد decompiler Ghidra می گوید که p-code varnodes "ضروری نیست که یک نوع داده مرتبط با آنها داشته باشد" و decompiler "در نهایت یک نوع داده رسمی را به واسطه گسترش اختصاص می دهد". گزینه "تضمين شاخص های ثابت" همان گسترش را به ثابت ها گسترش می دهد. Ghidra 12.1 بازیابی بیتفیلد را به decompiler اضافه کرد که در این درس نصب ها استفاده نمی کنند.
جدول دست ساخت دیگر نیمی از داستان است. مدیر نوع داده های Ghidra، انواع محلی IDA، آرشیو های نوع Binary Ninja و رزین.tdهمه اجازه می دهد شما ساختار را از طرح بازیابی و مشاهده کردن decompiler بازگردانده. گذر به شما می گوید چه تایپ و چه زمینه هایی شما حدس می زنید.
rea 6.5.0 با ارائه دهنده Ghidra متن تجزیه کننده را به عنوان یک رکورد باز می گرداند. همه چیز در ردیف Ghidra به آن اعمال می شود و رکورد نام های نوع را بدون شواهد تولید کننده آنها دارد. اجرا python3 code/main.py inferدر همان لیست به شما این شواهد می دهد.
تحقیق در مورد نوع تعصب
گذر در اینجا نیمی از تولید محدودیت یک سیستم نتیجه گیری نوع است، محدود به یک ساختار از یک پارامتر به دست آمده است. میدان فراتر رفته است.
- TIE (Lee, Avgerinos, Brumley, NDSS 2011) بازسازی نوع را به عنوان تولید محدودیت و حل بیش از یک IR دوگانه، محافظه کارایی و دقت را اندازه گیری کرد و با یک decompiler تجاری در 87 برنامه اصلی مقایسه کرد.
- Retypd (Noonan, Loginov, Cok, PLDI 2016) اضافه کرد زیرنویس، پولیمورفیسم و انواع تکراری، و گزارش بازسازی اشاره
constاین برنامه در حال اجرا با نام GrammaTech است. - OSPREY (Zhang و دیگران، IEEE S&P 2021) هر مکان حافظه را به عنوان یک متغیر تصادفی در نظر می گیرد و احتمالات احتمالی را نتیجه می دهد.
- DIRTY (Chen و دیگران، USENIX Security 2022) و ReSym (Xie و دیگران، CCS 2024) تولید پس از فرآیند تجزیه کننده با مدل های آموخته شده، و ReSym گزارش 72.9٪ دقت در طرح های ساختمانی کامل.
- RecStruct (Chen و دیگران، USENIX Security 2026) ضربات سرسبز را بدون یک تجزیه کننده بازیابی می کند، با اجرای نمادایی بی حس جریان و union-find over stack slots.
ساختار ستک اولین چیزیه که از گذرگاه این درس رد میشه-O1،mainنگه دارهrowsروی سکه و کد داخل نوشته شده0x24(%rsp)،0x40(%rsp)،0x48(%rsp)،0x30(%rsp)و0x38(%rsp).این تعویضات ماژول 32 4، 0، 8، 16 و 24 هستند: همان ساختار، بدون پارامتر برای تخم ریزی.
آن را بسازید
نمایش نمایش را اجرا کن:
cd phases/13-tools-and-protocols/41-recovering-data-and-types
python3 code/main.py
npx tsx code/main.tsمحصول یک گزارش JSON با پنج بخش است. dwarfعنوان واحد، ساختار و عملکردهای -O0هدفoraclesدر این قسمت، طرح قاعده psABI،ctypesطرح، طرح DWARF، راه اندازی بومی و یک agreeپرچمbuildsدر این بخش، دسترسی ها، طرحی که نتیجه گرفته و مقایسه هر یک از سه شی را در اختیار دارد.cxxنام های باز شده، هر دو vtables، هر دو typeinfo ریکاردها، تماس مجازی و نقاط DWARF را نگه دارد. toolsفهرست محصولات ابزار ضبط شده است.
شکل انتظار می رود:
{
"dwarf": {"unit": {"unitLength": 489, "version": 5, "unitType": "DW_UT_compile", "addressSize": 8, "abbrevOffset": 0}, "...": "..."},
"oracles": {"agree": true, "psabiRules": {"size": 32, "align": 8, "...": "..."}, "...": "..."},
"builds": {
"O0": {"layout": {"stride": 32, "size": 32, "gaps": [1, 2, 3, 10, 11, 12, 13, 14, 15], "...": "..."}, "comparison": {"matches": 5, "members": 5, "...": "..."}},
"O1": {"...": "..."},
"O2": {"...": "..."}
},
"cxx": {"virtualCalls": [{"slot": 2, "candidates": {"Shape": "__cxa_pure_virtual", "Rect": "_ZNK4Rect4areaEv"}, "...": "..."}], "...": "..."}
}بخونcode/main.pyدر این ترتیب:
ElfObjectبخش ها، نمادها و نقل مکان ها را می خواند وrelocated()اعمال می شودS + Aبه بخش تخريبparse_abbreviations()وDwarfInfo._parse_unit()سر و قسمت، جدول اختصار و نوشته ها را بخوانید و سپس حل کنیدstrxوaddrxاز طریق دو میزDwarfInfo.struct_layouts()وfunctions()به عنوانStructLayoutوFunctionInfoسوابق باtype_name()،type_size()وtype_kind(). .parse_listing()،parse_memory()وaccess_width().llvm-objdumpمتنtrace_function()ماشین حالت انتزاعی است وfold_unrolled()وinfer_layout()دسترسی ها رو به طرح تبدیل کنcompare_layouts()خطوط وضعیت را تولید می کند وsysv_layout()وctypes_layout()دو تا دیگر از آن ها است.Demangler،read_vtable()،read_typeinfo()وfind_virtual_calls()پوشش دادن اجزای C ++
چهار فرمان ديگه وجود دارهpython3 code/main.py dwarf FILE.oساختار و عملکرد هر شی قابل انتقال DWARF 5 را چاپ می کند. python3 code/main.py infer LISTING.txt [FILE.o]در یک لیست، گذر را اجرا می کند و با یک شی با DWARF آن مقایسه می کند. python3 code/main.py demangle NAME...وpython3 code/main.py vtable FILE.oنصف C++ را پوشش دهید.
ازش استفاده کن
بر روي يه شيوه جديد که خودت ساختي:
- دستگیرش کن
llvm-objdump -d -rاز این موضوع و نگهش در کنار آن موضوع نگه دارید. - فرار کن
python3 code/main.py inferدر لیست. اول خط شاهدان را بخوانید: این نام عملکرد هایی را می نامد که پارامتر اول آن به عنوان پایگاه حافظه استفاده شده است. اگر یک تابع از دست رفته باشد، پارامتر آن هرگز به یک اپراند حافظه نرسد، یا از طریق مسیر که عبور دنبال نمی کند به یکی رسیده است. - هر فيلد رو با تعداد دسترسی اش بخوان. يک دسترسی يک شاهد است. يک فيلد با يک شاهد را به عنوان نتيجه ثبت کن.
- شواهد قدم رو مطالعه کن
call-siteیاscaleورود قویه.loop-stepورود بدون نوع دیگری ضعیف است، زیرا یک حلقه بدون چرخاندن نشان دهنده مرحله چندین عنصر است. اگرunrolledظاهر ميشه، دست به دست بکشيد - اگر
-gساخت وجود دارد، مقایسه را اجرا کنید و هر ردیف که وجود ندارد را بخوانیدmatch.no-evidenceخط نام عضو را نام می دهد هیچ تابع لمس نشده است.unexpectedنام خطي که پاسي نمي تونست داخلش کنه - طرح را به عنوان یک ساختار در مدیر نوع ابزار خود تایپ کنید و ثبت کنید که چه زمینه ها، نام ها و انواع حدس است.
سه تا روحاني رو در دسترس نگه دارctypes، و DWARF در یک طرح شما نوشتند اختلاف نظر، شما پیدا کرده اید یک ساختار بسته بندی شده، یک میدان بیت، یا یک هدف با قوانین مختلف.
آزمایشگاه تعاملی
لابراتوار A: رمزگذاری ورودی عضو
ازcodeدایرکتوری، پیتون باز:
cd phases/13-tools-and-protocols/41-recovering-data-and-types/code
python3 -qراه رفتن:
from main import *
elf = load_object("account-x86_64-O0-g.o")
dwarf = DwarfInfo(elf)
dwarf.header()
elf.section(".debug_info").data[0x1a3:0x1ac].hex(" ")
dwarf.dies[0x1a3].attributes
dwarf.type_name(dwarf.type_of(dwarf.dies[0x1a3]))نو بايت ها رو در کاغذ با اختصار 13 رمزگشایی کن قبل از اينکه به فرهنگ لغت نگاه کني.elf.section(".debug_str_offsets").data[8:16].hex()و اوني که از اينجا نقل مکان شدهelf.relocated(".debug_str_offsets")[8:16].hex()، و تفاوت رو توضیح بده
لابراتوار ب: ردیابی یک تابع
listing = load_listing("account-x86_64-O0-g.objdump.txt")
trace = trace_function("label_length", listing["label_length"])
[(hex(a.address), a.root, a.offset, a.width, a.kind) for a in trace.accesses]
trace.pointer_uses
trace_function("sum_balances", listing["sum_balances"]).loopsقبل از اجرا، به خاطر بسپارید که کدام دسترسی ثابت می کند که اوفسیت 24 یک اشاره کننده است. سپس همان خطوط را روی -O1لیست و پیدا کردن leaq 0x1(%rcx), %rcxدرtrace.increments. .
آزمایشگاه C: حلقه ویکتور را بکشید
o2 = load_listing("account-x86_64-O2-g.objdump.txt")
trace = trace_function("sum_balances", o2["sum_balances"])
sorted({a.offset for a in trace.accesses})
folded, evidence = fold_unrolled(trace, trace.accesses)
sorted({a.offset for a in folded}), [(e.kind, e.value, e.text) for e in evidence]پس فرار کنanalyze_build("O2", fold=False)["comparison"]و شمارش رو بکنunexpectedخط ها. قانون خم کردن را تغییر دهید تا نیاز به مرحله حلقه باشدk * dباkحداقل 2 و تایید کنید که -O1حلقه، با یک دسترسی و مرحله 32، تنها باقی می ماند.
آزمایشگاه D: جدول و نام ها
shapes = load_object("shapes-x86_64-O0-g.o")
read_vtable(shapes, "_ZTV4Rect")
read_typeinfo(shapes, "_ZTI4Rect")
demangle("_ZNK4Rect4areaEv"), demangle("_Z8describeRK5Shapei")
find_virtual_calls(load_listing("shapes-x86_64-O0-g.objdump.txt"), [read_vtable(shapes, "_ZTV5Shape"), read_vtable(shapes, "_ZTV4Rect")])از فاصله را محاسبه کنcallq *0x10(%rax)در کاغذ، پس از آن آن را تایید کنیدDwarfInfo(shapes).struct("Rect").virtual_methodsپس بارش رو بردارshapes-x86_64-O1-g.oو سعی کنread_vtable. در موردش بخونيدElfErrorو توضیح بده که چرا نماد غائب است.
آزمایشگاه تمرین
ساختار سکه رو بازپس کنmainدر-O1. .
الزامات:
- طولاني
trace_function()تا منطقه ی فولاد فریم بتواند ریشه باشد.movapsفروشگاه هایی که پر می شوند0x0(%rsp)به0x5f(%rsp)از.data.rel.ro، که 96 بايت شروع کننده ازrows. . - دسترسی های داخل را اختصاص دهید
0x24(%rsp)،0x40(%rsp)،0x48(%rsp)،0x30(%rsp)و0x38(%rsp)به اون ریشه و گزارش تعویضات خود را به عنوان یک گام نامزد. - قدم 32 رو از حلقه در
0x120به0x12c، کجا%r14از 4 شروع میشه و تا 4 ادامه داره0x20تا0x64، و نشان بده که حلقه بسته شده0x643 عنصر + تعویض میدان - نتیجه را با طرح DWARF مقایسه کنید و به پنج تطابق برسید و ثبت کنید که کدام نوع باقی مانده است.
- یک آزمایش را اضافه کنید که وقتی ریشه فریم به یک پارامتر نسبت داده می شود شکست می خورد.
موفقیت به معنیpython3 code/main.py inferدر-O1لیست های لیستmainو مقایسه نشان می دهد پنج تعادل ازmainتنها
درس هنرپیشه
این درس شاملoutputs/skill-recovering-data-and-types.md. از آن برای تبدیل یک لیست و یک تعمیر دیبگ اختیاری به یک طرح ساختاری با شواهد در هر زمینه، یک قدم با منابع آن، مقایسه با DWARF و یک لیست از آنچه نامعلوم باقی مانده است استفاده کنید. این نیاز به جدول دسترسی، شواهد قدم، چک خم، Oracle DWARF زمانی که وجود دارد و بیانیه ای از نام ها و واجد شرایط است که هیچ دستورالعمل نمی تواند ارائه دهد.
بررسی کنید
نمایش و مجموعه تعیین کننده را اجرا کنید:
cd phases/13-tools-and-protocols/41-recovering-data-and-types
python3 code/main.py
npx tsx code/main.ts
python3 -m unittest discover -s code/tests -vبررسی باید ثابت کند:
- عنوان واحد، 14 اختصار و 5 عضو تعویض با
llvm-dwarfdump - نام ها از طریق افراد نقل مکان میرسند
.debug_str_offsetsمیز و واحدstr_offsets_baseوaddr_base8 - شش تابع دامنه ها را حمل می کنند
DW_OP_reg6 RBPوDW_OP_fbregمکان هایی که در اردوگاه نشان داده می شود و آدرس های پایین آنها برابر با عملکرد لیست شروع می شود - یک شی بدون
.debug_infoو دستاوردهاي کدگر LEB1280x78به عنوان -8 pyelftoolsوlldbطرحی مشابه خواننده را چاپ کنید- قوانین psABI
ctypes, DWARF و کار بومی تعویضات را ارائه می دهند0, 4, 8, 16, 24و اندازه 32، و ساختار تنظیم شده 24 بایت است - در فهرست بازرسي کننده 13 دستور در
bump، متصل کننده هاR_X86_64_PLT32 bump-0x4به تماس در0x194، و به عقب پرش رو پيدا ميکنه0x72 - عملیات حافظه، عرض، نوع و نشانه ها از متن AT&T و mnemonic رمزگذاری می شوند
bumpدر-O0اشاره اش را از سمت عقب از فانوس فریم می خواند و سه بار در آفسیت 4 وارد می شود- قدم از کجا مياد؟
shlq $0x5در-O0، ازaddq $0x20, %rdxدر-O1، و از هشت بار که با یک مرحله از 256 در-O2 label_lengthثابت کند که اشاره در آفسه 24 با حذف آن استmainیک فريم از یک فريم با تعویض 0 عبور می کند.0x20و0x40در-O0و فقط به اسم "printfدر-O1- هر ساخت 5 تا از 5 عضو با اندازه 32 و شکاف برابر با پوششی DWARF مطابقت دارد
- بدون اینکه بغل بکشیم
-O2گزارش های ایجاد شده هفت میدان گذشته بایت 32 و هیچ اندازه - و بازتوليد کننده همه 19
llvm-cxxfiltرشته ها - هر دو vtables، هر دو typeinfo ثبت و تماس مجازی در
0x16به سمت سمت 2 قرار گرفت و DWARF موافق بود - .
-O1C ++ آبجیکت الگوی تماس را حفظ می کند و هیچ vtable ندارد - ضبط های Ghidra، rizin و radare2 نشان می دهد که ساختار با DWARF و تعویض خام بدون آن
- این ارقام تعاملی همان داده هایی را که تجزیه و تحلیل تولید می کند، در خود جای می دهد.
روش های شکست تولید
| Failure | What the weak tool reports | What the analysis must prove |
|---|---|---|
| Unrolled loop | Eight fields 32 bytes apart and a 232-byte struct | Loop step 256 equals 8 times the offset delta 32, so one field at offset 4 |
| Stride read from one increment | sizeof 256 from addq $0x100 | The greatest common divisor of every stride source, with the scalar tail's addq $0x20 |
| 8-byte field called a pointer | void * at offset 24 | A later access uses the loaded value as a base, or the kind stays int64 |
| Pointer called an integer | long at offset 24 | The dereference at 0xfb, recorded as pointer proof |
| Narrow compare | char with a sign | cmpb carries no sign; only movsbl or movzbl does |
| Five functions assumed to share a type | One struct with no evidence of aliasing | Call sites in main that pass one frame object, or an explicit analyst assertion |
Strings read from .debug_str by index | Garbage names from a relocatable object | str_offsets_base plus the relocated .debug_str_offsets entry |
| Addresses read as numbers | low_pc of 0 for every function | addrx through the relocated .debug_addr table |
| DWARF 4 object | A reader that misparses the header | Version check on the second header field; 4 has no unit_type byte |
| Missing vtable | "the class has no virtual functions" | The -O1 object inlined every call; the -O0 object has the table and its relocations |
| Vtable slot read from the wrong base | Off-by-two slot numbers | The address point is 16 bytes past the symbol, after offset-to-top and typeinfo |
| Mangled name stripped | Z8describeRK5Shapei left as is | --no-strip-underscore on macOS, or a demangler that checks for _Z |
| Struct typed from one tool's guess | char *param_1 taken as the field type | The width and the dereference, written as evidence, and the name marked as a guess |
قاعده عملیاتی
یک زمینه را از یک دسترسی و یک قدم را از یک حلقه بازیافت کنید و دستورالعمل را به هر یک از آنها متصل کنید. قبل از اينکه به يک تعويض پشت قدم اعتماد کني، حلقه هاي باز را بکش. DWARF را از طریق اختصار و جدول های نقل مکان شده آن بخوانید، هرگز به عنوان یک بخش رشته ای فهرست نکنید. با هر رشته ای مقایسه کنید و هر عضو را بدون شواهد، هر اشاره ای که اثبات نشده و هر نام را به عنوان ناشناخته بنویسید. در یک شی C ++، vtable را از محل های خود و اسلات را از تماس بخوانید، سپس از DWARF برای تأیید اسلات که خوانده اید استفاده کنید.
خواندن بیشتر
- System V Application Binary Interface, AMD64 Architecture Processor Supplement, section 3.1.2: Data Representation
- Procedure Call Standard for the Arm 64-bit Architecture (AAPCS64), section 5.10: Composite Types
- Writing ARM64 code for Apple platforms
- Intel 64 and IA-32 Architectures Software Developer's Manual, Volume 1, section 3.7.5: Specifying an Offset
- DWARF Debugging Information Format Version 5, sections 2.1, 5.7.6, 7.5.1.1, 7.5.3, 7.5.6
- Itanium C++ ABI, sections 2.5 (Virtual Table Layout), 2.9 (Run-Time Type Information), 5.1 (External Names)
- Microsoft Learn: Decorated namesوName Decoration
- llvm-dwarfdump command guideوllvm-cxxfilt command guide
- Python ctypes: Structure layout, sizeof, alignment
- Ghidra Data Type Manager،Decompiler Concepts،Decompiler OptionsوGhidra 12.1.4 release
- IDA 9.1 user guide: Types،IDA 9.4 release postوIDA Free
- Binary Ninja user guide: TypesوBinary Ninja 6.0 Krypton
- radare2 6.2.4 releaseوrizin v0.9.1 release
- GDB manual: Examining the Symbol Table, ptype /o
- pyelftools on PyPI
- Lee, Avgerinos, Brumley, "TIE: Principled Reverse Engineering of Types in Binary Programs" (NDSS 2011)
- Noonan, Loginov, Cok, "Polymorphic Type Inference for Machine Code" (PLDI 2016)وthe retypd repository
- Zhang et al., "OSPREY: Recovery of Variable and Data Structure via Probabilistic Analysis for Stripped Binary" (IEEE S&P 2021)
- Chen et al., "Augmenting Decompiler Output with Learned Variable Names and Types" (USENIX Security 2022)
- Xie et al., "ReSym: Harnessing LLMs to Recover Variable and Data Structure Symbols from Stripped Binaries" (CCS 2024)
- Chen et al., "RecStruct" (USENIX Security 2026)
This free lesson is part of the AI Engineering from Scratch curriculum. Read the full explanation, run the lesson code, and verify the result in the interactive reader or from the repository source.
Browse the complete course catalog or open this lesson on GitHub.