Phase 13: Tools & Protocols

بازیابی داده ها و انواع: ساختارها، آرایه ها، 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 فرستاد و اينو به دست آورد:

c
void bump(long param_1,int param_2)
{
  *(int *)(param_1 + 4) = param_2 + *(int *)(param_1 + 4);
  return;
}

همون منبع که با -gاین را از همان کمپایلر می دهد:

c
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میگه:

output
       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:

c
struct account {
    char kind;
    int balance;
    short code;
    double rate;
    const char *label;
};
MemberSizeAlignmentLowest offset that fitsPadding before
kind1100
balance4443 (bytes 1 to 3)
code2280
rate88166 (bytes 10 to 15)
label88240

آخرین عضو در 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برای همان ساختار. برنامه خود نسخه سوم را چاپ می کند وقتی که آن را در این دستگاه بسازید و اجرا کنید:

output
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شاخصه های یک سری از ساختار را نشان می دهد:

output
      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دو رشته تنگ رو ميخواد:

output
      8c: 0f be 00                     	movsbl	(%rax), %eax
      98: 0f bf 48 08                  	movswl	0x8(%rax), %ecx

movsblیک بایت را در آفسیت 0 بارگذاری می کند و آن را نشان می دهد، بنابراین میدان در 0 یک عدد کامل 1 بایت است. movswlدو بایت در آفسیت 8 با تمدید علامت بارگذاری می کند، بنابراین میدان در 8 یک عدد کامل 2 بایت امضا شده است.-O1اولین دسترسی به این موضوع می شودcmpb $0x6c, (%rdi)، که هنوز هم می گوید 1 بایت در تعویض 0 و هیچ چیز در مورد علامت می گوید.

projectedيه خط مي خواد:

output
      cc: f2 0f 2a 40 04               	cvtsi2sdl	0x4(%rax), %xmm0
      d5: f2 0f 59 40 10               	mulsd	0x10(%rax), %xmm0

cvtsi2sdlتبدیل یک عدد کامل 32 بیت امضا شده از حافظه به دوگانه، که تایید می کند که آفسیت 4 دارای یک عدد کامل 4 بیت امضا شده است. mulsdضربش دو برابر از حافظه، پس آفسه 16 دارای یک 8 بایت نقطه شناور است.

label_lengthیک اشاره را می خواند و سپس آن را دنبال می کند:

output
      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)پس هر دستورالعمل يه قاعده رو اعمال ميکنه

  • Amovqبین یک register 64-bit و یک slot frame مقدار را کپی می کند. یک 32bit یا باریک تر برای یک register آن را یک Indexبا مقیاس 1
  • Aleaqبا یک پایه اشاره ای، یک اشاره ای را با اضافه کردن جابجایی تولید می کند و با یک فهرست شاخص اضافه می کند 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فرم ها دسترسی نیستند.

python
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()در هر مرحله از هر کار شاهد را ادغام می کند و قدم را از چهار منبع جمع می کند، هر کدام با دستورالعمل تولید کننده آن.

KindWhere it comes fromBuild that shows it
scalean Index with scale above 1 reached a pointer, from shlq $0x5 or imulq-O0, shlq $0x5, %rcx at 0x59
loop-stepa 64-bit increment of the base or index register inside a loop that also contains the access-O1, addq $0x20, %rdx at 0x24
offset-deltasame-width accesses through one base register inside one loop, at offsets a constant apart-O2, eight loads 32 apart
call-sitethe 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هشت عنصر در هر گذرنامه رو میخواد:

output
      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هستند:

output
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 اعلامیه است. دو موضوع در اینجا:

output
[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_data1

parse_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:

  1. 0dخلاصه 13، عضو است.
  2. 10.DW_AT_nameمثلstrx1: شاخص رشته 16
  3. 88 01 00 00.DW_AT_typeمثلref4: تعویض0x188از شروع واحد، که DW_TAG_base_typeشماره شمارهint. .
  4. 00.DW_AT_decl_fileو05.DW_AT_decl_line: خط 5 از account.c. .
  5. 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:

BuildAccesses through the first parameterFieldsStride evidenceMembers matchedSize
-O0115scale 32, call-site 32, call-site 645 of 532, match
-O195loop-step 325 of 532, match
-O2175 after folding, 12 beforeoffset-delta 32, unrolled 32, loop-step 256, scale 32, loop-step 325 of 532, 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:

output
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              _ZNK4Rect4nameEv

Slot 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يه تماس مجازی ميکنه:

output
       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/. صف هایی برای ابزار هایی که در اینجا نصب نشده اند، اسناد خود را توصیف و بیان می کنند.

ToolVersion, licenceWhere types liveOn our fixtures
Ghidra12.1.4, 2026-09-21, Apache-2.0Data Type Manager with built-in, user-defined, and derived types; .gdt archives; types used by a program are copied into itWith 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 Pro9.4, 2026-07-13, commercial; IDA Free is non-commercial with a cloud decompiler for x86 onlyLocal Types window, .til type libraries, DWARF and PDB import into a dwarf or pdb folderNot installed. Documentation only
Binary Ninja6.0, 2026-09-03, commercial with a Free editionType editing, header import and export, type archives, debug info from DWARF and PDB, type libraries, platform typesNot installed. Documentation only
radare26.2.4, 2026-10-05, LGPLv3ts, tsc, afvdts 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
rizin0.9.1, 2026-06-29, LGPL corets, tsc, afvdtsc 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
lldblldb-1703.0.236.21image lookup -tbyte-size = 32 and the full compiler_type text from the ELF object
gdbnot installed hereptype /o prints "the offsets and sizes of fields in a struct" with holes markedDocumentation only
pyelftools0.33, public domainPython API over .debug_infoA 43-line script prints the same five members and unit version 5
llvm-dwarfdumpApple LLVM 17.0.0--debug-info, --debug-abbrev, --debug-str-offsetsThe 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 هستند: همان ساختار، بدون پارامتر برای تخم ریزی.

آن را بسازید

نمایش نمایش را اجرا کن:

bash
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فهرست محصولات ابزار ضبط شده است.

شکل انتظار می رود:

json
{
  "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در این ترتیب:

  1. ElfObjectبخش ها، نمادها و نقل مکان ها را می خواند وrelocated()اعمال می شودS + Aبه بخش تخريب
  2. parse_abbreviations()وDwarfInfo._parse_unit()سر و قسمت، جدول اختصار و نوشته ها را بخوانید و سپس حل کنید strxوaddrxاز طریق دو میز
  3. DwarfInfo.struct_layouts()وfunctions()به عنوان StructLayoutوFunctionInfoسوابق باtype_name()،type_size()وtype_kind(). .
  4. parse_listing()،parse_memory()وaccess_width().llvm-objdumpمتن
  5. trace_function()ماشین حالت انتزاعی است وfold_unrolled()وinfer_layout()دسترسی ها رو به طرح تبدیل کن
  6. compare_layouts()خطوط وضعیت را تولید می کند وsysv_layout()وctypes_layout()دو تا دیگر از آن ها است.
  7. 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++ را پوشش دهید.

ازش استفاده کن

بر روي يه شيوه جديد که خودت ساختي:

  1. دستگیرش کنllvm-objdump -d -rاز این موضوع و نگهش در کنار آن موضوع نگه دارید.
  2. فرار کنpython3 code/main.py inferدر لیست. اول خط شاهدان را بخوانید: این نام عملکرد هایی را می نامد که پارامتر اول آن به عنوان پایگاه حافظه استفاده شده است. اگر یک تابع از دست رفته باشد، پارامتر آن هرگز به یک اپراند حافظه نرسد، یا از طریق مسیر که عبور دنبال نمی کند به یکی رسیده است.
  3. هر فيلد رو با تعداد دسترسی اش بخوان. يک دسترسی يک شاهد است. يک فيلد با يک شاهد را به عنوان نتيجه ثبت کن.
  4. شواهد قدم رو مطالعه کنcall-siteیاscaleورود قویه.loop-stepورود بدون نوع دیگری ضعیف است، زیرا یک حلقه بدون چرخاندن نشان دهنده مرحله چندین عنصر است. اگر unrolledظاهر ميشه، دست به دست بکشيد
  5. اگر-gساخت وجود دارد، مقایسه را اجرا کنید و هر ردیف که وجود ندارد را بخوانیدmatch.no-evidenceخط نام عضو را نام می دهد هیچ تابع لمس نشده است.unexpectedنام خطي که پاسي نمي تونست داخلش کنه
  6. طرح را به عنوان یک ساختار در مدیر نوع ابزار خود تایپ کنید و ثبت کنید که چه زمینه ها، نام ها و انواع حدس است.

سه تا روحاني رو در دسترس نگه دارctypes، و DWARF در یک طرح شما نوشتند اختلاف نظر، شما پیدا کرده اید یک ساختار بسته بندی شده، یک میدان بیت، یا یک هدف با قوانین مختلف.

آزمایشگاه تعاملی

لابراتوار A: رمزگذاری ورودی عضو

ازcodeدایرکتوری، پیتون باز:

bash
cd phases/13-tools-and-protocols/41-recovering-data-and-types/code
python3 -q

راه رفتن:

python
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()، و تفاوت رو توضیح بده

لابراتوار ب: ردیابی یک تابع

python
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: حلقه ویکتور را بکشید

python
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: جدول و نام ها

python
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 زمانی که وجود دارد و بیانیه ای از نام ها و واجد شرایط است که هیچ دستورالعمل نمی تواند ارائه دهد.

بررسی کنید

نمایش و مجموعه تعیین کننده را اجرا کنید:

bash
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طرحی مشابه خواننده را چاپ کنید
  • قوانین psABIctypes, 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 و هیچ اندازه
  • و بازتوليد کننده همه 19llvm-cxxfiltرشته ها
  • هر دو vtables، هر دو typeinfo ثبت و تماس مجازی در 0x16به سمت سمت 2 قرار گرفت و DWARF موافق بود
  • .-O1C ++ آبجیکت الگوی تماس را حفظ می کند و هیچ vtable ندارد
  • ضبط های Ghidra، rizin و radare2 نشان می دهد که ساختار با DWARF و تعویض خام بدون آن
  • این ارقام تعاملی همان داده هایی را که تجزیه و تحلیل تولید می کند، در خود جای می دهد.

روش های شکست تولید

FailureWhat the weak tool reportsWhat the analysis must prove
Unrolled loopEight fields 32 bytes apart and a 232-byte structLoop step 256 equals 8 times the offset delta 32, so one field at offset 4
Stride read from one incrementsizeof 256 from addq $0x100The greatest common divisor of every stride source, with the scalar tail's addq $0x20
8-byte field called a pointervoid * at offset 24A later access uses the loaded value as a base, or the kind stays int64
Pointer called an integerlong at offset 24The dereference at 0xfb, recorded as pointer proof
Narrow comparechar with a signcmpb carries no sign; only movsbl or movzbl does
Five functions assumed to share a typeOne struct with no evidence of aliasingCall sites in main that pass one frame object, or an explicit analyst assertion
Strings read from .debug_str by indexGarbage names from a relocatable objectstr_offsets_base plus the relocated .debug_str_offsets entry
Addresses read as numberslow_pc of 0 for every functionaddrx through the relocated .debug_addr table
DWARF 4 objectA reader that misparses the headerVersion 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 baseOff-by-two slot numbersThe address point is 16 bytes past the symbol, after offset-to-top and typeinfo
Mangled name strippedZ8describeRK5Shapei left as is--no-strip-underscore on macOS, or a demangler that checks for _Z
Struct typed from one tool's guesschar *param_1 taken as the field typeThe width and the dereference, written as evidence, and the name marked as a guess

قاعده عملیاتی

یک زمینه را از یک دسترسی و یک قدم را از یک حلقه بازیافت کنید و دستورالعمل را به هر یک از آنها متصل کنید. قبل از اينکه به يک تعويض پشت قدم اعتماد کني، حلقه هاي باز را بکش. DWARF را از طریق اختصار و جدول های نقل مکان شده آن بخوانید، هرگز به عنوان یک بخش رشته ای فهرست نکنید. با هر رشته ای مقایسه کنید و هر عضو را بدون شواهد، هر اشاره ای که اثبات نشده و هر نام را به عنوان ناشناخته بنویسید. در یک شی C ++، vtable را از محل های خود و اسلات را از تماس بخوانید، سپس از DWARF برای تأیید اسلات که خوانده اید استفاده کنید.

خواندن بیشتر

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.