Phase 13: Tools & Protocols

डेटा और प्रकारों को पुनर्प्राप्त करनाः स्ट्रक्चर, सरणी, सी ++ ऑब्जेक्ट और डीडब्ल्यूएआरएफ

एक डिसीम्पिलर प्रिंट्स a->balanceजब इसमें टाइप टेबल है और *(int *)(param_1 + 4)यह पाठ पहुँचों से तालिका बनाता है, DWARF 5 से वास्तविक तालिका पढ़ता है, और दिखाता है कि दोनों कहाँ सहमत हैं।

Type: Build

Languages: Python, C, C++, x86-64 assembly

Prerequisites: Phase 13 · 33 (disassembly and control flow), Phase 13 · 34 (lifting to pseudocode)

Time: ~140 minutes

सीखने के लक्ष्य

  • इंटेल एसडीएम एड्रेसिंग नियमों से आधार रजिस्टर प्लस विस्थापन के रूप में संरचना सदस्य पहुंच और आधार प्लस इंडेक्स समय चरण के रूप में सरणी पहुंच पढ़ें।
  • सिस्टम V संरेखण नियमों को हाथ से लागू करें struct accountऔर प्रत्येक पैडिंग बाइट समझाएं।
  • एक प्रकार-रिकवरी पास बनाएं जो किसी सूची में प्रवेशों को क्षेत्र ऑफसेट, चौड़ाई, प्रकार, एक कदम और एक आकार में बदल देता है, प्रत्येक के लिए सबूत के साथ।
  • DWARF 5 पाठक के लिए लिखें .debug_info,.debug_abbrev,.debug_str_offsetsऔर .debug_addrएक स्थानांतरित करने योग्य वस्तु के साथ इसकी जाँच करें।llvm-dwarfdump. .
  • पुनर्प्राप्त लेआउट की तुलना DWARF लेआउट के साथ करें और प्रत्येक अंतर को लापता सबूत, एक मिश्रित पहुंच या एक असत्यापित प्रकार के रूप में नामित करें।
  • एक इटैनियम सी ++ vtable, इसके टाइपइंफो, और स्थानांतरण से इसके खराब नाम पढ़ें, और एक स्लॉट के लिए एक आभासी कॉल को हल करें।

समस्या

आप संकलित किया 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इसके बिना decompilator केवल जानता है कि कुछ 8 बाइट्स चौड़ाई में आया था%rdiऔर यह 4 बाइट्स पर +4पढ़ी और लिखी गई थी।

उस तालिका में सब कुछ एक स्रोत है. ऑफसेट 4 के विस्थापन बाइट से आता हैaddl 0x4(%rax), %ecxचौड़ाई 4 से आता है lनाम एक से आता हैDW_TAG_memberप्रवेश .debug_info. इस पाठ में दोनों आधे होते हैंः एक पास जो निर्देशों के प्रमाण को पुनर्प्राप्त करता है, और एक पाठक जो संकलक द्वारा रिकॉर्ड किए गए को डिकोड करता है। उनके बीच अंतर है कि एक प्रकार नाम की लागत क्या है।

सदस्य प्रवेश विस्थापन है

इंटेल एसडीएम वॉल्यूम 1, धारा 3.7.5 में प्रभावी पता को परिभाषित किया गया है 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पाठ 33 के नियमों के साथ।03है ADD r32, r/m32. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .48है 01 001 000mod = 018-बिट विस्थापन के साथ एक मेमोरी ऑपरेंड को संदर्भित करता है, 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. .

सिस्टम वी लेआउट नियम

x86-64 psABI, धारा 3.1.2, संकलित के लिए तीन नियम देता है। "संरचनाएं और संघ अपने सबसे सख्ती से संरेखित घटक के संरेखण को मानते हैं। प्रत्येक सदस्य को उपयुक्त संरेखण के साथ सबसे कम उपलब्ध ऑफसेट को सौंपा जाता है। किसी भी वस्तु का आकार हमेशा वस्तु के संरेखण का गुणक होता है।" उसी दस्तावेज़ के चित्रा 3.1 में स्केलर आकार और संरेखण दिए गए हैंः char1 और 1, short2 और 2, int4 और 4, double8 और 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 है बिना पूंछ पैडिंग के। 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 है। Arm AAPCS64 अपने खंड 5.10.1 में समान संचयी नियम बताता है, और Apple के arm64 दस्तावेज़ इन पांच स्केलर आकारों को रखता है, इसलिए संख्याएं दोनों लक्ष्यों पर सहमत हैं। पाठ के x86-64 ऑब्जेक्ट्स को संकलित किया गया है-target x86_64-unknown-linux-gnuताकि पाठ 33 के ELF पाठक और नीचे के 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 $0x532 से गुणा करता है।addq,%raxहै list + 32 * i, और लोड तत्व के ऑफसेट 4 पर 4 बाइट्स पढ़ता है i. शिफ्ट काउंट इस ऑब्जेक्ट में एकमात्र स्थान है जहां स्ट्रक्चर का आकार संख्या के रूप में दिखाई देता है. इन निर्देशों को शामिल करने वाला लूप से चलता है 0x49jmp 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), जो अभी भी ऑफसेट 0 पर 1 बाइट कहता है और संकेत के बारे में कुछ नहीं कहता है।

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)

movq8 बाइट्स को ऑफसेट 24 पर पढ़ता है। अपने आप में यह एक 8 बाइट्स पूर्णांक या एक पॉइंटर है।cmpbएक मान जो एक पते के रूप में उपयोग किया जाता है एक पॉइंटर है।cmpbसूचकांक पर 1 बाइट पढ़ता है %rcxऔर -O1सूचक के साथ आगे है leaq 0x1(%rcx), %rcxलूप के अंदर, तो इंगित तत्व 1 बाइट के बीच हैं.

पांच कार्यों को एक साथ रखें और संरचना है{ i8 at 0, i32 at 4, i16 at 8, f64 at 16, pointer at 24 }एक से तीन और 10 से 15 के अंतराल बिल्कुल पद्मांकन psABI नियमों की भविष्यवाणी की है। निर्देश क्या नहीं कहते हैंः सदस्य नाम, कि सूचक है const char *, हस्ताक्षरित बाइट का घोषित प्रकार 0 पर, जो स्रोत द्वारा वर्तनी char, और पांच कार्यों को एक ही प्रकार प्राप्त है। अंतिम दावा से आता हैmain, जो सभी पांच पर एक फ्रेम वस्तु पारित करता है।-O0कॉल साइटें इसे दृश्यमान बनाती हैंः leaq -0x70(%rbp), %rdiइसके बाद addq $0x20, %rdiपहलेcallq bumpऔर addq $0x40, %rdiपहलेcallq tag. 0 से कम हो जाता है .0x20और 0x40एक सरणी के तीन तत्व हैं, और memcpy0x60बाइट्स जो इसे भरते हैं 96 बाइट्स है, जो 32 के तीन तत्वों है।

पास

trace_function()में code/main.pyएक धारक एक 64-बिट रजिस्टर या एक फ्रेम स्लॉट जैसे -0x8(%rbp)प्रत्येक धारक के तीन मानों में से एक है।Pointerएक स्ट्रैप रिकॉर्ड करता है कि उस कई बाइट्स का एक अज्ञात गुणक जोड़ा गया है।Indexएक अज्ञात मूल्य का एक पूर्णांक है जो एक पैमाने का गुणक है. कुछ भी और कुछ भी नहीं है.

राज्य छह सिस्टम वी तर्क रजिस्टर रखने से शुरू होता है Pointer("param0", 0)पारPointer("param5", 0). . फिर प्रत्येक निर्देश एक नियम लागू करता हैः

  • ए movqएक 64-बिट रजिस्टर और एक फ्रेम स्लॉट के बीच मूल्य को कॉपी करता है। एक 32-बिट या संकीर्ण रजिस्टर में लिखना इसे एक बनाता है Index1 के साथ।
  • ए 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और q1, 2, 4 और 8 बाइट्स के प्रत्यय, movsblऔर movswlहस्ताक्षरित संकीर्ण भारों के लिए, movzblअघोषित करने के लिए, sdऔर ss8-बाइट और 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यह नियम है कि "8 बाइट्स" से "पूंटर" में ऑफसेट 24 बदलता है।

चार प्रकार के कदम सबूत

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कlang वेक्टरिस sum_balances. से लूप शरीर0x500xaaप्रति पास आठ तत्व पढ़ता हैः

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>

%raxlist + 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बिल्ड बाइट 32 से आगे सात फ़ील्ड दिखाता है और कोई आकार नहीं है। यह राज्य एक पाठक जो लूप के बिना विस्थापन पर भरोसा करता है देखता है।

DWARF 5 बाइट्स से

-gनिर्माणaccount.cइसमें आठ डिबग सेक्शन हैं। .debug_info493 बाइट्स है, .debug_abbrev179, .debug_str230, .debug_str_offsets124, .debug_addr112, .debug_line312, .debug_line_str12, तथा .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. के पहले बारह बाइट्स.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स्ट्रिंग ऑफसेट तालिका में एक 1-बाइट सूचकांक है, 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= 32, DW_AT_decl_file0 है और DW_AT_decl_lineहै 3, जो है की रेखा 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_offsetsरखती है 29 R_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 + A32-बिट और 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से चलता है 0x00x21, जो है DW_AT_low_pcऔर DW_AT_high_pcलंबाई. इसकी फ्रेम बेस हैDW_OP_reg6 RBP, और इसकी मापदंड aस्थान अभिव्यक्ति है 91 78DW_OP_fbregSLEB128 मान के साथ 0x78, जो -8 है, जो फ्रेम स्लॉट है -0x8(%rbp)जिसमें सूचीबद्ध किया गया है %rdiपते पर0x4डिबग जानकारी और निर्देश दो पक्षों से एक ही स्लॉट का वर्णन करते हैं।

दो लेआउट की तुलना करें

compare_layouts()DWARF सदस्यों के माध्यम से चलता है और उसी ऑफसेट पर एक अनुमानित क्षेत्र की तलाश करता है।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कदम जोड़ता है। tag0 और 8 के ऑफसेट जोड़ता है। projected16 पर फ्लोट जोड़ता है।label_length24 पर सूचक जोड़ता है और यह साबित करता है कि यह एक सूचक है। main पर कॉल साइटों को जोड़ता है-O0और कुछ भी नहीं जोड़ता -O1और -O2, क्योंकि संकलक प्रत्येक कॉल को इनलाइन किया गया है. आंकड़ा प्रत्येक बिल्ड के लिए इस क्रम के माध्यम से कदम.

तीन चीजें पहुंच से बाहर हैं. नाम निर्देशों में नहीं हैं.constनिर्देशों में नहीं है।labelकेवल " तत्वों से 1 बाइट अलग" है leaq 0x1(%rcx), %rcxस्ट्रिंग लूप में, जो संगत है charऔर साथ में unsigned charऔर _Bool.-O10 पर बाइट का संकेत अज्ञात है, क्योंकि cmpb $0x6c, (%rdi)बिना विस्तार किए तुलना करता है। पास रिपोर्ट int8वहाँ और i8पर-O0, जहां movsblये तथ्य हैं पाठ 35 अज्ञात के रूप में रिकॉर्ड।

सी ++ ऑब्जेक्ट

shapes.cppपरिभाषित करता है Shapeएक आभासी विनाशकारी के साथ, एक शुद्ध आभासी area(), और एक आभासी name()और Rect, जो Shapeऔर दो जोड़ता है intItanium C ++ एबीआई नियंत्रित करता है कि कैसे क्लेंग x86-64 लिनक्स पर इन सेट करता है.

भ्रष्ट नाम

एबीआई की धारा 5.1.2 में कहा गया है कि "एक नाम को पूर्ववर्ती के द्वारा खराब किया जाता है _Zवस्तु में 19 ऐसे प्रतीक हैं।_ZNK4Rect4areaEvयह पढ़ता है _Z, तो Nएक घोंसले हुए नाम के लिए, Kconst सदस्य फ़ंक्शन के लिए, 4Rectऔर 4areaलम्बाई पूर्वनिर्धारित स्रोत नाम के रूप में, Eघोंसले हुए नाम को बंद करने के लिए, और vएक खाली पैरामीटर सूची के लिए। llvm-cxxfiltछापें Rect::area() const. ._Z8describeRK5Shapeiहै describe(Shape const&, int)Rसंदर्भ है, Kconst है, 5Shapeवर्ग है, iहै int. धारा 5.1.4 में विशेष नाम हैं _ZTVएक वेंटिलेटर के लिए, _ZTIटाइपइंफॉ के लिए, और _ZTSटाइपइंफो नाम स्ट्रिंग के लिए। बिल्डर और विध्वंसक हैं 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-underscoreआउटपुट. macOS पर ध्वज महत्वपूर्ण है, जहां उपकरण डिफ़ॉल्ट रूप से एक प्रमुख रेखांकित को हटा देता है और फिर पहचान नहीं करता है Z8describe.... .

उसके स्थानांतरण से उत्पन्न वसूली

एबीआई के खंड 2.5.2 में आभासी तालिका के घटकों को क्रमशः आभासी कॉल ऑफसेट, आभासी बेस ऑफसेट, ऑफसेट टू टॉप, टाइपइंफो पॉइंटर और फिर आभासी फ़ंक्शन पॉइंटर की सूची दी गई है। पता बिंदु, किसी वस्तु के vptr का मान, पहले फ़ंक्शन पॉइंटर का पता है (धारा 2.6.2) । Rectकोई आभासी आधार नहीं है, इसलिए इसकी तालिका शीर्ष, टाइपइंफो और कार्यों के लिए ऑफसेट है।

प्रतीक _ZTV4Rect0x30 बाइट्स, 6 8-बाइट स्लॉट है। ऑब्जेक्ट में प्रत्येक बाइट शून्य है। सामग्री में है .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

स्लॉट 0 ओवरऑफ को शीर्ष पर रखता है, जो 0 है और स्थानांतरण की आवश्यकता नहीं है। स्लॉट 1 टाइपइंफो है। पते का बिंदु +16. वहाँ से, स्लॉट 0 पूर्ण-ऑब्जेक्ट विध्वंसक है, स्लॉट 1 हटाने विध्वंसक है, स्लॉट 2 area, स्लॉट 3 name. .read_vtable()प्रतीक आकार, इसकी सीमा में स्थानान्तरण, और 0 पर शून्य पढ़ता है। Shapeटेबल के साथ एक ही आकार है __cxa_pure_virtualस्लॉट 2 में, जो रनटाइम फ़ंक्शन है कि एक शुद्ध आभासी कॉल तक पहुँचता है।

आरटीटीआई

धारा 2.9.4 में वर्णन किया गया है std::type_infoएक vptr के रूप में, और एक पॉइंटर के साथ, और संकलित प्रकार के नाम के लिए, और व्युत्पन्न वर्गों की सूची। _ZTI4Rectतीन स्थानान्तरण है: _ZTVN10__cxxabiv120__si_class_type_infoE+0x10,_ZTS4Rectऔर _ZTI5Shapeपहला है Vtable का__si_class_type_info, वर्ग एबीआई एक प्रकार के लिए उपयोग करता है, जिसमें शून्य के साथ एक सार्वजनिक गैर-आभासी आधार है, और +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ऑब्जेक्ट पॉइंटर लोड करता है। दूसरा ऑब्जेक्ट के ऑफसेट 0 पर 8 बाइट्स पढ़ता है, जो vptr है, क्योंकि एबीआई इसे वर्चुअल फ़ंक्शंस वाले वर्ग में पहले रखता है।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स्लॉट 3 पर है, और विनाशकों स्लॉट 0 पर है।Rectहै DW_TAG_structure_typeएक के साथ 16 बाइट्स के DW_TAG_inheritance के लिए प्रविष्टिShape0 और सदस्यों पर ऑफसेट w8 बजे और h12 बजे।Shapeएक कृत्रिम सदस्य के साथ 8 बाइट्स है _vptr$Shapeदो स्वतंत्र व्युत्पन्नों के लिए स्लॉट 2 देता है area: स्थानांतरण तालिका और डिबग प्रविष्टियाँ।

-O1वस्तु में कोई विटाबल नहीं है।mainनिर्माण Rectस्थानीय के रूप में, तो संकलक हल r.name()और describe(r, 2)तालिका का पता नहीं लेता, इसलिए तालिका नहीं जारी की जाती है। describeअभी भी शामिल है movq (%rdi), %raxऔर callq *0x10(%rax), क्योंकि इसके पैरामीटर किसी भी हो सकता हैShape. एक गायब vtable एक वस्तु फ़ाइल के बारे में एक तथ्य है, और परीक्षण test_the_optimised_object_keeps_the_call_pattern_but_drops_the_vtablesदोनों आधे की जांच करें।

माइक्रोसॉफ्ट एबीआई इन सभी विवरणों में भिन्न होता है। सजावटी नामों से शुरू होता है ?और कॉल सम्मेलन को एन्कोड, तो void test(void)C ++ के रूप में संकलित __cdeclहै ?test@@ZAXXZ. आरटीटीआई लोकेटर पर स्थित है vfptr[-1]एक के रूप में _RTTICompleteObjectLocator, और प्रतीकों में से ??_7एक vftable और ??_R4माइक्रोसॉफ्ट के अपने दस्तावेजों में कहा गया है कि "कंपायलर विक्रेताओं के बीच सी ++ नामकरण के लिए कोई मानक नहीं हैं।" इस पाठ के फिक्स्चर केवल इटैनियम हैं।

कैसे उपकरण बरामद प्रकारों को प्रस्तुत करते हैं

प्रत्येक पंक्ति नीचे है कि नाम एक आउटपुट इस मशीन पर बनाया गया था और कैप्चर किया गया था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

DWARF के बिना Ghidra के आउटपुट में काम पर दो ही नियम दिखाते हैं।char * के पैरामीटर तकtagक्योंकि फ़ंक्शन एक बाइट के माध्यम से तुलना करता है, और char **0x18क्योंकि label_lengthलोड किए गए मान को हटा देता है और बाइट्स की तुलना करता है। ये वही दो नियम हैं जो इस पाठ में पास लागू होता हैः निर्देश से चौड़ाई, संदर्भ से संकेत। गिद्रा के डिकोम्पाइलर दस्तावेज में कहा गया है कि पी-कोड वार्नोड "उनसे जुड़े डेटा-प्रकार की आवश्यकता नहीं है" और डिकोम्पाइलर "अंततः प्रजनन द्वारा एक औपचारिक डेटा-प्रकार को सौंपता है। "स्थिर संकेतकों को सम्मिलित करें" विकल्प समान संवर्धन को स्थिरांक तक बढ़ाता है। गिड्रा 12.1 ने डिसीमुलेटर में बिटफील्ड रिकवरी जोड़ा, जिसे इस पाठ के फिक्स्चर का प्रयोग नहीं करता है।

हाथ से बने तालिका कहानी का दूसरा आधा है. गिदरा के डेटा प्रकार प्रबंधक, आईडीए के स्थानीय प्रकार, द्विआधारी निंजा के प्रकार संग्रह, और रिजिन के tdसभी आप पुनर्प्राप्त लेआउट से संरचना टाइप करने के लिए और decompiler फिर से प्रस्तुत देखने के लिए अनुमति देते हैं. पास आप क्या टाइप करने के लिए बताता है और आप क्या क्षेत्रों का अनुमान लगा रहे हैं.

rea 6.5.0 अपने Ghidra प्रदाता के साथ एक रिकॉर्ड के रूप में decompiler पाठ लौटाता है. Ghidra पंक्ति में सब कुछ इस पर लागू होता है, और रिकॉर्ड टाइप नामों के बिना सबूत है कि उन्हें उत्पन्न किया है. चल रहा है python3 code/main.py inferउसी सूची पर आपको वह सबूत देता है।

प्रकार इन्फरेंस अनुसंधान

यहाँ पास एक प्रकार के अनुमान प्रणाली के प्रतिबंध-उत्पादन आधा है, एक पैरामीटर से प्राप्त एक संरचना तक सीमित है। क्षेत्र आगे चला गया है।

  • टीआईई (ली, एव्गेरिनोस, ब्रमले, एनडीएसएस 2011) ने प्रकार के पुनर्निर्माण को बाइनरी आईआर पर प्रतिबंध जनरेशन और समाधान के रूप में ढांचा दिया, रूढ़िवादीता और सटीकता को मापा, और 87 कोरियूल प्रोग्राम पर एक वाणिज्यिक डिकोम्पाइलर के साथ तुलना की।
  • Retypd (नोनन, लॉगिनोव, कोक, PLDI 2016) ने उपप्रकार, बहुरूपवाद और पुनरावर्ती प्रकार जोड़े, और पॉइंटर के पुनर्निर्माण की सूचना दी constGrammaTech के नाम के तहत एक ओपन सोर्स कार्यान्वयन मौजूद है।
  • ओस्प्रे (जंग और अन्य, आईईईई एस एंड पी 2021) प्रत्येक मेमोरी स्थान को एक यादृच्छिक चर के रूप में व्यवहार करता है और सबसे अधिक संभावना वाले प्रकारों को निष्पादित करता है।
  • DIRTY (Chen और अन्य, USENIX सुरक्षा 2022) और ReSym (Xie और अन्य, CCS 2024) सीखते मॉडल के साथ पोस्ट-प्रोसेस डिसीपिलर आउटपुट, और ReSym पूर्ण संरचना लेआउट पर 72.9% सटीकता रिपोर्ट करता है।
  • RecStruct (चेन और अन्य, USENIX सुरक्षा 2026) एक डिसीमुलेटर के बिना घोंसले हुए स्ट्रैक्ट को पुनर्प्राप्त करता है, प्रवाह-असंवेदनशील प्रतीकात्मक निष्पादन और स्टैक स्लॉट पर संघ-खोज के साथ।

स्टैक संरचना इस सबक के पास छोड़ पहली बात है।-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

आउटपुट एक JSON रिपोर्ट है जिसमें पांच भाग हैं। dwarfइकाई हेडर, संरचनाओं और कार्यों को रखता है -O0वस्तु।oraclespsABI नियम लेआउट, ctypesलेआउट, DWARF लेआउट, मूल रन, और एक agreeध्वज।buildsप्रवेश, अनुमानित लेआउट, और तीन वस्तुओं में से प्रत्येक के लिए तुलना रखता है। cxxविघटित नामों, दोनों vtables, दोनों टाइपइंफो रिकॉर्ड, आभासी कॉल, और 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सी ++ आधा कवर।

इसका प्रयोग करें

एक नई वस्तु पर आप खुद बनायाः

  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 एक लेआउट पर असहमत आप लिखा है, आप एक पैक संरचना, एक बिट-क्षेत्र, या अलग नियमों के साथ एक लक्ष्य पाया है.

इंटरैक्टिव लैब

लैब ए: सदस्य प्रविष्टि को डिकोड करें

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. .

लैब सीः वेक्टरलाइज्ड लूप को फोल्ड करें

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 के साथ लूप, अकेला छोड़ दिया जाता है।

लैब डीः वक्ता और नाम

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 निकालें0x1200x12c, जहां %r144 पर शुरू होता है और आगे बढ़ता है 0x20जब तक 0x64, और दिखाएँ कि लूप बंधा हुआ है0x64तीन तत्वों के साथ क्षेत्र ऑफसेट है.
  • परिणाम की तुलना DWARF लेआउट के साथ करें और पांच मैचों तक पहुंचें, और रिकॉर्ड करें कि किस प्रकार का परीक्षण किया गया है।
  • एक पैरामीटर को फ्रेम रूट से संबंधित होने पर असफल होने वाला एक परीक्षण जोड़ें।

सफलता का अर्थ हैpython3 code/main.py inferपर -O1सूचीबद्ध सूची mainएक फ्रेम रूट के साथ एक गवाह के रूप में, और तुलना से पांच मैच दिखाता हैmainअकेले.

पाठ कलाकृतियाँ

इस सबक में शामिल है outputs/skill-recovering-data-and-types.md. इसका उपयोग एक सूची और एक वैकल्पिक डिबग बिल्ड को प्रति क्षेत्र के प्रमाण के साथ एक स्ट्रक्चर लेआउट में बदलने के लिए करें, इसके स्रोतों के साथ एक कदम, DWARF के साथ तुलना करें, और अज्ञात रहने वाली चीजों की सूची। इसके लिए एक्सेस तालिका, कदम सबूत, तह जांच, DWARF ओरेकल जब कोई मौजूद हो, और नामों और योग्यताओं का एक बयान की आवश्यकता होती है जो कोई निर्देश प्रदान नहीं कर सकता है।

जाँचें

डेमो और निर्धारक सूट चलाएंः

bash
cd phases/13-tools-and-protocols/41-recovering-data-and-types
python3 code/main.py
python3 -m unittest discover -s code/tests -v

सत्यापन से साबित होना चाहिएः

  • इकाई हेडर, 14 संक्षिप्तियाँ और पांच सदस्य ऑफसेट सहमत हैं llvm-dwarfdump
  • नाम स्थानांतरित के माध्यम से आते हैं .debug_str_offsetsतालिका और इकाई की str_offsets_baseऔर addr_base8 हैं
  • छह कार्यों के दायरे हैं, DW_OP_reg6 RBPऔर DW_OP_fbregस्थानों डंप दिखाता है, और उनके कम पते सूची के कार्य शुरू के बराबर
  • बिना किसी वस्तु के.debug_infoउठता है, और LEB128 डिकोडर हैंडल 0x78-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
  • प्रत्येक निर्माण आकार 32 और DWARF पैडिंग के बराबर अंतराल के साथ पांच में से पांच सदस्यों से मेल खाता है
  • बिना तह -O2बाइट 32 से परे सात फ़ील्ड और कोई आकार नहीं रिपोर्ट बनाएं
  • डिमेंगलर सभी 19 को पुनः उत्पन्न करता है llvm-cxxfiltतार
  • दोनों vtables, दोनों typeinfo रिकॉर्ड, और आभासी कॉल पर 0x16स्लॉट 2 पर निर्णय लें, और DWARF सहमत है
  • -O1C ++ ऑब्जेक्ट कॉल पैटर्न रखता है और कोई vtable नहीं है
  • गिद्रा, रिज़िन और रडार2 कैप्चर में 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.