Phase 13: Tools & Protocols

कॉलिंग कन्वेंशन और स्टैक फ्रेमः सिस्टम वी, विंडोज x64, और AArch64

एक डिमॉन्डेशन दिखाता है कि कौन सा रजिस्टर एक फ़ंक्शन पढ़ता है। केवल एबीआई बताता है कि उन रजिस्टरों का क्या अर्थ है, और एबीआई ऑब्जेक्ट हेडर द्वारा चुना जाता है, इसलिए यह सबक पहले हेडर पढ़ता है और फिर प्रत्येक रजिस्टर को उस नियम के खिलाफ जांचता है जो इसे वहां रखता है।

Type: Build

Languages: Python, C, x86-64 and AArch64 assembly

Prerequisites: Phase 13 · 32 (ELF from scratch), Phase 13 · 33 (disassembly and control flow)

Time: ~140 minutes

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

  • ऑब्जेक्ट हेडर से कॉल कन्वेंशन चुनेंः ELF x86-64 System V psABI है, COFF AMD64 Microsoft x64, ELF AArch64 AAPCS64, Mach-O arm64 Apple का संस्करण है।
  • प्रत्येक एबीआई के लिए एक फ़ंक्शन के बाइट द्वारा बाइट के प्रोग्रू को डिकोड करें और प्रत्येक सहेजे गए रजिस्टर, रिटर्न एड्रेस और फ्रेम पॉइंटर को एंट्री स्टैक पॉइंटर से इसके ऑफसेट पर रखें।
  • फ़ंक्शन के पैरामीटर रजिस्टर को उन रजिस्टरों से इनपुट करें जो यह उन्हें लिखने से पहले पढ़ता है, और स्टैक तर्क स्लॉट को एंट्री स्टैक पॉइंटर के ऊपर लोड से।
  • माइक्रोसॉफ्ट x64 के स्थिति स्लॉट से सिस्टम V के स्वतंत्र रजिस्टर अनुक्रमों को बताएं, और दोनों AAPCS64 के NGRN और NSRN काउंटर से।
  • स्रोत हस्ताक्षर, विनिर्देश में नियम और संकलक के अपने ही विघटन डेटा के साथ प्रत्येक निष्कर्ष की जाँच करें .pdata,.xdataऔर .eh_frame. .
  • तुलना करें कि गिद्रा, आईडीए, बाइनरी निंजा, रडारे2, रिजिन और एएनजीआर का नाम कैसे रखा जाता है और एक ही तथ्य को पुनर्प्राप्त करते हैं, और रिकॉर्ड करते हैं कि उनका आउटपुट स्रोत से कब असहमत है।

समस्या

आप संकलित किया convention.cके लिए लिनक्स x86-64 पर -O2और रडार2 6.2.4 से उनके तर्क के लिए पूछा।eight_argsउसने सात से उत्तर दिया:rdi,rsi,rdx,r8,r9,rsp+0x8और rsp+0x10. सी स्रोत में आठ मापदंड हैं. बाइट्स पढ़ते हैं%rcxठीक एक बार, में leaq (%rdx,%rcx), %rax, और उपकरण के चर वसूली एक रजिस्टर नहीं गिना जो केवल एक पते के अंदर दिखाई देता है.

फिर आपने रिज़िन 0.9.1 में उसी फ़ाइल का विंडोज बिल्ड खोला। यह सम्मेलन को बताया किamd64और पाया r8,rcx,rdxवस्तु COFF है, तो सम्मेलन माइक्रोसॉफ्ट x64 है, चौथा रजिस्टर तर्क है r9दोनों उपकरण एक ही स्रोत में अलग रजिस्टरों को याद करते हैं।

सूची निर्देशों का एक अनुक्रम है। एक कॉल कन्वेंशन एक अनुबंध है जो सूचीबद्ध करने के बाहर रहता हैः कौन से रजिस्टर तर्क ले जाते हैं, जिन्हें कॉल करने वाले को संरक्षित करना चाहिए, जहां वापसी पता और स्टैक तर्क बैठते हैं, और कॉल करने वाले को स्टैक पॉइंटर के ऊपर क्या छोड़ना चाहिए। चार दस्तावेजों में इस मामले में महत्वपूर्ण अनुबंधों की परिभाषा है। सिस्टम V AMD64 psABI (वर्जन 1.0, 12 मार्च 2025 की बिल्ड) धारा 3.2.1 से 3.2.3 तक। माइक्रोसॉफ्ट x64 कॉल कन्वेंशन, स्टैक उपयोग, और प्रलोग और एपिलॉग पृष्ठ (पृष्ठ दिनांक 2018-12-17 से 2025-03-19 तक) । आर्म एएपीसीएस 64 रिलीज 2025Q4 (23 जनवरी 2026) धारा 6.1 से 6.9 तक। एप्पल के arm64 प्लेटफॉर्म नोट्स। यह पाठ उन नियमों को पढ़ता है, उन्हें एक सी फ़ाइल पर लागू करता है जो तीन तरीकों से संकलित है, और विश्लेषक का निर्माण करता है जो सूची को पैरामीटर स्थानों, सहेजे गए रजिस्टरों और फ्रेम ऑफसेट की एक चेक की गई सूची में बदल देता है।

एक शीर्षक नियम पुस्तिका चुनता है

convention.cबारह कार्य करते हैंः leaf_add,eight_args,mixed,by_value,big_ret,sum_varargs,keeps, और पांच कॉलर्स externसंरचनाओं का उपयोग long long, क्योंकि longयह 4 बाइट्स MSVC लक्ष्य पर और 8 बाइट्स अन्य पर है, और एक संरचना है कि आकार परिवर्तन कन्वेंशन बदलता है। एप्पल क्लांग 17.0.0 इस मशीन से इसे सात बार संकलित कियाः-target x86_64-unknown-linux-gnu,x86_64-pc-windows-msvcऔर aarch64-unknown-linux-gnuपर-O0और -O2और arm64-apple-macosपर-O2. कोई लिनक्स या विंडोज हेडर स्थापित नहीं हैं, इसलिए फ़ाइल यह घोषणा करता है कि यह क्या जरूरत है और केवल क्लिंग के अपने शामिल है stdarg.hऔर stddef.h. .

describe_object()में code/main.py4 बाइट जादू संख्या और एक मशीन क्षेत्र पढ़ता है और अनुबंध का नाम देता हैः

HeaderFieldValueRulebook
7f 45 4c 46 ELFe_machine at offset 0x1262System V AMD64 psABI
64 86 COFFMachine at offset 00x8664Microsoft x64
7f 45 4c 46 ELFe_machine183AAPCS64
cf fa ed fe Mach-O 64cputype at offset 40x0100000cAAPCS64 with Apple differences

तीनों अनुबंध तालिका की लगभग हर पंक्ति पर असहमत हैं। प्रत्येक मूल्य को हेडर पंक्ति में नामित दस्तावेज़ से उद्धृत किया गया है, और प्रत्येक को फिक्स्चर के साथ परीक्षण द्वारा जांच किया जाता है।

RuleSystem V psABIMicrosoft x64AAPCS64
Integer arguments%rdi %rsi %rdx %rcx %r8 %r9, next free registerRCX RDX R8 R9, by positionx0 to x7, by NGRN
Floating-point arguments%xmm0 to %xmm7, next free registerXMM0 to XMM3, by positionv0 to v7, by NSRN
Integer result%rax, then %rdxRAXx0, then x1
Floating-point result%xmm0, then %xmm1XMM0v0
Result too large for registersaddress passed in %rdi "as if it were the first argument", returned in %raxpointer in RCX, arguments shifted right, returned in RAXaddress in x8, x0 to x7 untouched
Callee-saved%rbp %rbx %r12 to %r15RBX RBP RDI RSI RSP R12 to R15, XMM6 to XMM15r19 to r29, SP, low 64 bits of v8 to v15
First stack argument on entry8(%rsp)40(%rsp), above a 32-byte shadow store0(sp)
Below the stack pointer128-byte red zone"considered volatile"no red zone (Apple: 128 bytes)
Alignment at the call(%rsp + 8) multiple of 16 on entry16-byte aligned outside prolog and epilogSP mod 16 = 0
Variadic marker%al holds an upper bound on vector registers usedfloats duplicated in the integer registernone in registers; Apple uses 8-byte stack slots

प्रणाली V: दो स्वतंत्र रजिस्टर अनुक्रम और एक लाल क्षेत्र

psABI 3.2.3 प्रत्येक तर्क के प्रत्येक आठ बाइट को INTEGER, SSE, या MEMORY के रूप में वर्गीकृत करता है और फिर दो परस्पर नहीं होने वाली सूचियों से रजिस्टर को असाइन करता है। "INTEGER" "%rdi, %rsi, %rdx, %rcx, %r8 और %r9 अनुक्रम का अगला उपलब्ध रजिस्टर" लेता है। SSE लेता है %xmm0%xmm7. "यदि किसी भी तर्क के आठ बाइट के लिए कोई रजिस्टर उपलब्ध नहीं है, तो पूरे तर्क को स्टैक पर पारित किया जाता है", और स्मृति तर्क "स्टैक पर उल्टे (दाएं से बाएं) क्रम में धकेल दिए जाते हैं।

eight_args(a, b, c, d, e, f, g, h)पर-O227 बाइट्स है:

text      10: 48 01 fe                     	addq	%rdi, %rsi
      13: 48 8d 04 0a                  	leaq	(%rdx,%rcx), %rax
      17: 48 01 f0                     	addq	%rsi, %rax
      1a: 4c 01 c0                     	addq	%r8, %rax
      1d: 4c 01 c8                     	addq	%r9, %rax
      20: 48 03 44 24 08               	addq	0x8(%rsp), %rax
      25: 48 03 44 24 10               	addq	0x10(%rsp), %rax
      2a: c3                           	retq

छह रजिस्टर, फिर दो लोड पर 8(%rsp)और 16(%rsp). psABI के चित्र 3.3 में "मेमोरी तर्क आठबाइट 0" को 16(%rbp)कब%rbpफ्रेम आधार है, जो है 8(%rsp)किसी भी धक्का से पहले प्रवेश पर। g8 पर है।h16 साल की उम्र में।

mixed(int i, double d, long long l, float f)इन दोनों सूचियों को दिखाता है।%ediऔर %rsi, पहला और दूसरा INTEGER रजिस्टर।%xmm0और %xmm1, SSE रजिस्टर की पहली और दूसरी.cvtsi2sd %edi, %xmm2और %xmm2एक स्क्रैच रजिस्टर है कि फ़ंक्शन पढ़ने से पहले लिखता है। एक विश्लेषक जो cvtsi2sdएक रीड एक तीसरे फ्लोटिंग-पॉइंट तर्क का आविष्कार करता है। रडार2 ने ठीक यही किया और रिपोर्ट की।mixedजैसे (double arg1, double arg2, double arg3)में xmm0,xmm1,xmm2. .

by_value(struct pair p)एक 16-बाइट संरचना को दो INTEGER आठबाइट्स में पारित करता है %rdiऔर %rsi, और उसी तरह लौटाता हैः leaq 0x1(%rdi), %rax; leaq -0x1(%rsi), %rdx; retq. .big_ret(long long seed)32 बाइट्स लौटाता है, जो वर्ग स्मृति है, और psABI कहते हैं "कॉलर रिटर्न मूल्य के लिए स्थान प्रदान करता है और इस भंडारण का पता %rdi में पारित करता है जैसे कि यह पहला तर्क था। "seed%rsi, और फ़ंक्शन से शुरू होता हैmovq %rdi, %raxक्योंकि "%rax पर वापस करने पर उस पते को शामिल किया जाएगा जिसे %rdi में कॉल करने वाले द्वारा भेजा गया है। "

लाल क्षेत्र अन्य प्रणाली V हस्ताक्षर है। psABI 3.2.2: "%rsp द्वारा इंगित स्थान से परे 128-बाइट क्षेत्र को आरक्षित माना जाता है और संकेत या बाधित हैंडलर द्वारा संशोधित नहीं किया जा सकता है।"-O0,eight_argsहै pushq %rbp; movq %rsp, %rbpऔर फिर छह दुकानों के लिए -0x8(%rbp)पार-0x30(%rbp)बिना किसीsubqदुकानें स्टैक पॉइंटर से 48 बाइट नीचे हैं।-O2निर्माणsum_varargs के साथ 88 बाइट्स आवंटित करता हैsubq $0x58, %rspऔर फिर रजिस्टर सहेजें क्षेत्र लिखता है -0x58(%rsp)-0x80(%rsp): 128 बाइट्स स्टैक पॉइंटर से नीचे, पूरे लाल क्षेत्र. के साथ एक ही फ़ाइल का निर्माण -mno-red-zoneऔर leaf_addलाभ pushq %raxऔर एक addq $0x8, %rspअपने 8 बाइट्स स्थानीय लोगों को स्टैक पॉइंटर के ऊपर रखने के लिए।

वेरिएडिक कॉल एक रजिस्टर जोड़ते हैं। "%al का उपयोग 0 से 8 तक ऊपरी सीमा के साथ वैक्टर रजिस्टरों की संख्या निर्दिष्ट करने के लिए छिपे हुए तर्क के रूप में किया जाता है। call_varargsयह सेट करता है के साथ xorl %eax, %eaxपर-O2और movb $0x0, %alपर-O0. .sum_varargsपहले पढ़ता हैः testb %al, %al; jeआठ पर कूद movaps %xmmNजब कोई वेक्टर रजिस्टर इस्तेमाल नहीं किया गया था तब सहेजता है।

माइक्रोसॉफ्ट x64: पोजीशन, एक छाया स्टोर, और कोई रेड जोन नहीं

माइक्रोसॉफ्ट के नियम स्थितिगत हैं। "पूरे तर्क रजिस्टर आरसीएक्स, आरडीएक्स, आर 8 और आर 9 में पारित किए जाते हैं। फ्लोटिंग प्वाइंट तर्क XMM0L, XMM1L, XMM2L और XMM3L में पारित किए जाते हैं।" और "पहले चार मापदंडों में कोई भी फ्लोटिंग-पॉइंट और डबल-सटीक तर्क XMM0 - XMM3 में पारित किए जाते हैं, स्थिति के आधार पर।" वही mixed%ecx,%xmm1,%r8,%xmm3: स्लॉट 0 एक int है, तो RCX; स्लॉट 1 एक डबल है, तो XMM1; स्लॉट 3 एक तैरता है, तो XMM3. .XMM0और XMM2खाली रहो।

कॉल करने वाले को भी जगह छोड़नी चाहिए। "कॉलर को हमेशा चार रजिस्टर पैरामीटर स्टोर करने के लिए पर्याप्त स्थान आवंटित करना चाहिए, भले ही कॉलले इतने पैरामीटर नहीं लेता है।" पृष्ठ इसे छाया स्टोर या होम पते कहते हैं, और चार स्लॉट "प्रभावी रूप से बुलाया फ़ंक्शन के स्वामित्व में हैं।" प्रवेश पर 0(%rsp)वापसी का पता है, 8(%rsp)39(%rsp)चार घर हैं, और पहले ढेर तर्क पर है 40(%rsp). .eight_argsपर-O2पढ़ता है 0x28(%rsp),0x30(%rsp),0x38(%rsp),0x40(%rsp): 40, 48, 56, 64.

कॉल करने वाले पक्ष अंकगणित दिखाता है।call8पर-O2है subq $0x48, %rspऔर फिर दुकानों के 5, 6, 7, 8 पर 0x20(%rsp)0x38(%rsp)पहलेmovl $1, %ecxऔर दोस्तों. 72 बाइट्स छाया भंडार के लिए 32 है, 32 चार स्टैक तर्कों के लिए, और 8 है, ताकि रिटर्न पता एक 16-बाइट सीमा पर है। call_varargsकेवल तर्क दर्ज करता है और अभी भी आवंटित करता है subq $0x28, %rsp: 32 छाया भंडारण के लिए और 8 संरेखण के लिए। keepsतीन गैर-भगोड़ रजिस्टर (24 बाइट्स) धक्का देता है और फिर subq $0x20, %rsp, तो यह है किextरिटर्न पते के नीचे अपने 32-बाइट छाया भंडारण पाता है।

स्ट्रक्ट्स अपने नियम का पालन करते हैं। "किसी भी तर्क जो 8 बाइट्स में फिट नहीं होता है, या 1, 2, 4, या 8 बाइट्स नहीं है, को संदर्भ द्वारा पारित किया जाना चाहिए।" और "16 बाइट के तर्क संदर्भ द्वारा पारित किए जाते हैं।" by_valueइसलिए एक प्रतियों के लिए एक संकेत प्राप्त करता है %rdx, में एक छिपा परिणाम सूचक के बाद %rcx: "कॉलर को रिटर्न वैल्यू के लिए मेमोरी आवंटित करनी होगी और उसे पहले तर्क के रूप में एक पॉइंटर पास करना होगा। शेष तर्क को फिर एक तर्क को दाईं ओर स्थानांतरित किया जाएगा।" call_pair पर कॉपी बनाता है0x20(%rsp)के साथmovaps, पास leaq 0x20(%rsp), %rdx, परिणाम बफर से गुजरता है leaq 0x38(%rsp), %rcxऔर पढ़ता है .bवापस से 0x40(%rsp). .

कोई लाल क्षेत्र नहीं है। "आरएसपी के वर्तमान पते के परे सभी स्मृति अस्थिर माना जाता हैः ओएस, या एक डिबगर, उपयोगकर्ता डिबगर सत्र के दौरान इस स्मृति को ओवरराइट कर सकता है, या एक विराम हैंडलर।" -O0 leaf_addस्थानीय लोगों के 8 बाइट्स की जरूरत है और उन्हें साथ मिलता है pushq %raxप्रवेश पर और popq %rcxपहलेretq, एक के बजाय दो एकल बाइट निर्देश subqऔर एक addq. स्टैक उपयोग पृष्ठ इस प्रकार कर सकते हैं जो पाना परिभाषित करता हैः "एक पाना समारोह एक है कि एक समारोह तालिका प्रविष्टि की आवश्यकता नहीं है. यह RSP सहित किसी भी गैर-विस्थापन रजिस्टर में परिवर्तन नहीं कर सकते हैं। "

वेरिएडिक अपने रजिस्टर को घर कहता है। "वार्ग या प्रोटोटाइप रहित कार्यों के लिए, किसी भी फ्लोटिंग प्वाइंट मान को संबंधित सामान्य प्रयोजन रजिस्टर में दोहराया जाना चाहिए। "sum_varargsपर-O2दुकानें %rdx,%r8,%r90x18(%rsp),0x20(%rsp),0x28(%rsp)एक के बाद pushq %rax, जो कि प्रवेश ऑफसेट 16, 24, 32: दूसरे, तीसरे और चौथे रजिस्टर पैरामीटर के घरों है।va_listहै leaq 0x18(%rsp), %rax, घर के %rdx, पहला अज्ञात तर्क।

AAPCS64, और एप्पल क्या बदलता है

AAPCS64 6.8.2 दो काउंटर के साथ नियमों की एक सूची चलाता है। C.9: एक अभिन्न तर्क "x[NGRN]" और NGRN वृद्धि में कम से कम महत्वपूर्ण बिट्स पर कॉपी किया जाता है। C.1 NSRN के साथ फ्लोटिंग-पॉइंट तर्कों के लिए समान करता है। C.12: अधिकतम 8 - NGRNदोहरे शब्दों "सलग सामान्य प्रयोजन के रजिस्टर में कॉपी किया जाता है।" C.13 सेट करता है "एनजीआरएन 8" एक बार एक तर्क स्टैक में जाता है, तो बाद में कुछ भी रजिस्टर में वापस नहीं आता है। eight_argsउपयोगx0x7और कभी भी ढेर को छूता नहीं है।mixedहै w0,d0,x1,s1. .by_valueहै x0और x1अंदर और बाहर.

6.9 में कहा गया है कि रजिस्टर में पारित किया जाएगा कि एक परिणाम "एक ही रजिस्टर में लौटाया जाता है", अन्यथा "मेमोरी ब्लॉक का पता x8 में फ़ंक्शन के लिए एक अतिरिक्त तर्क के रूप में पारित किया जाएगा। "big_retलिखता है str x0, [x8],stur q0, [x8, #0x8],str x9, [x8, #0x18]और कभी नहीं लिखता x0. .call_bigसेट mov x8, spऔर seedअंदर रहता है x0, क्योंकि 6.9 जोड़ता है "कोई आवश्यकता नहीं है कि कॉलली x8 में संग्रहीत मूल्य को बनाए रखने के लिए" और x8तर्क रजिस्टरों में से एक नहीं है।

6.1.1: "एक उप-रूटीन कॉल को रजिस्टर r19-r29 और SP की सामग्री को संरक्षित करना चाहिए" 6.4.5.2: एक सार्वजनिक इंटरफ़ेस पर "SP mod 16 = 0". स्टैक पूरी तरह से नीचे उतर रहा है और "कोई धागा (पढ़ने या लिखने के लिए) निष्क्रिय क्षेत्र तक पहुंचने की अनुमति नहीं है" इसके नीचे, इसलिए लिनक्स -O0 leaf_addउत्सर्जनsub sp, sp, #0x108 बाइट्स के लिए स्थानीय जहां प्रणाली V लाल क्षेत्र का उपयोग किया।

6.4.6 फ्रेम रिकॉर्ड को परिभाषित करता हैः "स्टैक पर दो 64-बिट मानों का एक फ्रेम रिकॉर्ड ... सबसे कम संबोधित डबल-वर्ड पिछले फ्रेम रिकॉर्ड को इंगित करेगा और सबसे अधिक संबोधित डबल-वर्ड में वर्तमान फ़ंक्शन में प्रवेश करने पर LR में पारित मूल्य होगा।" और "स्टैक फ्रेम के भीतर फ्रेम रिकॉर्ड का स्थान निर्दिष्ट नहीं है।" क्लिंग इस एक फ़ाइल में दोनों स्थानों को दिखाता है। keepsपर-O2लिनक्स पर नीचे रिकॉर्ड डालता है के साथ stp x29, x30, [sp, #-0x30]!; mov x29, sp. उसी कार्य पर -O0यह शीर्ष पर डालता है के साथ sub sp, sp, #0x20; stp x29, x30, [sp, #0x10]; add x29, sp, #0x10. .

Apple के arm64 नोट रजिस्टर और संरेखण को बनाए रखते हैं और फ्रेम, लाल क्षेत्र और विविध कॉल को बदलते हैं। "फ्रेम पॉइंटर रजिस्टर (x29) को हमेशा एक वैध फ्रेम रिकॉर्ड को संबोधित करना चाहिए। कुछ फ़ंक्शन, जैसे पत्ते के फ़ंक्शन या पूंछ कॉल, इस सूची में एक प्रविष्टि नहीं बनाने का विकल्प चुन सकते हैं।"keepsबचत करता हैx22, x21, x20, x19पहले और रिकॉर्ड को शीर्ष पर रखता हैः stp x29, x30, [sp, #0x20]; add x29, sp, #0x20. "प्लेटफ़ॉर्म रिजर्व रजिस्टर x18. इस रजिस्टर का उपयोग न करें. " "एआरएम 64 लाल क्षेत्र में 128 बाइट्स हैं जो स्टैक पॉइंटर के ठीक नीचे हैं", और अभी भी क्लिंग जारी है sub sp, sp, #0x10के लिए -O0पत्ती, इसलिए संकलक इसे डिफ़ॉल्ट रूप से उपयोग नहीं करता है। Variadic तर्क "8 बाइट स्टैक स्लॉट की उचित संख्या" और "प्रकार va_list char के लिए एक उपनाम है*" के लिए असाइन किया जाता है। call_varargsApple स्टोर पर 10, 20, 30 से [sp],[sp, #8],[sp, #0x10]और केवल गुजरता है w0; लिनक्स बिल्ड पास w1,w2,w3और के साथ पूंछ कॉलb extva. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .sum_varargsपढ़ता है add x8, sp, #0x10, कॉल करने वाले के स्टैक पॉइंटर, के रूप में यह va_list; लिनक्स sum_varargsबचत करता हैx1x7और q0q7एक 224 बाइट फ्रेम में और संरचना क्षेत्रों को शुरू करता है __gr_offs-56 और __vr_offsएक के साथ -128 तक mov x8, #-0x38; movk x8, #0xff80, lsl #32. .

Mach-O प्रत्येक C प्रतीक के साथ prefixes_, तो सूची दिखाता है _keepsऔर पहला कार्य निजी लेबल के अंतर्गत दिखाई देता है ltmp0. विश्लेषक प्रतीक तालिका के माध्यम लेबल नाम बदलता है.

एक अग्रलेख को हाथ से तीन बार डिकोड करें

keeps(int n)कॉलext(i)एक लूप में और रखता है n,iऔर accकॉल के माध्यम से, तो हर एबीआई रजिस्टर सहेजने के लिए चाहिए.-O2प्रत्येक लक्ष्य के लिए निर्माण।

प्रणाली V, 4 बाइट्सः

  1. 55है push r64निम्न तीन बिट्स में रजिस्टर के साथः 50 + 5,%rbp. गहराई 8, %rbpप्रवेश ऑफसेट -8 पर सहेजा गया।
  2. 41 5641क्या REX के साथ Bसेट, 56है 50 + 6. REX.B 6 से 14 तक बढ़ाया गया है:pushq %r14- 16 पर।
  3. 53है 50 + 3pushq %rbx-24 पर।

तीन धक्का 24 बाइट्स हैं. रिटर्न पता 8 जोड़ता है, और 32 16 का गुणक है, तो callq extलूप के अंदर एक के बिना संरेखित है subq. . कोई नहीं है .movq %rsp, %rbp, तो इस निर्माण में कोई फ्रेम पॉइंटर नहीं है. शरीर का उपयोग करता है%rbpजैसे कि कॉल-सेव रजिस्टर जो रखता है acc(xorl %ebp, %ebp,addl %eax, %ebp) । एपिलॉग दर्पण हैः5b 41 5e 5d c3. .

माइक्रोसॉफ्ट x64, 7 बाइट्स:

  1. 56है pushq %rsi-8 पर।57है pushq %rdi-16.53है pushq %rbx-24. सभी तीनों अस्थिर हैं यहाँ, और RDIऔर RSIवे सिस्टम V के अंतर्गत तर्क रजिस्टर थे।
  2. 48 83 ec 20: REX.W, opcode 83 /58-बिट तत्काल के साथ, ModRM ecहै 11 101 100subq $0x20, %rspगहराई 56।

56 प्लस रिटर्न पता है 64, संरेखित. 32 बाइट्स छाया भंडार है किextउपसर्ग पृष्ठ केवल "एक जोड़ें RSP, निरंतर या lea RSP, निरंतर [FPReg], शून्य या अधिक 8-बाइट रजिस्टर पॉपअप की एक श्रृंखला के बाद और एक वापसी या एक jmp" और बाइट्स हैं।48 83 c4 20 5b 5f 5e c3. .

AArch64, 16 बाइट्स चार शब्दों में, छोटे-एंडियनः

  1. a9bd7bfdहै stpपूर्व सूचकांक के साथ। 31:30 बिट 1064 बिट रजिस्टर का चयन करें, बिट 25:23 011पूर्व सूचकांक चुनें, बिट 22 भंडारण के लिए 0 है।1111010, जो -6, द्वारा स्केल 8: -48. 14:10 पर Rt2 30 है, Rn 9:5 पर 31 है (sp), Rt 4:0 पर 29 है। stp x29, x30, [sp, #-0x30]!: स्टैक पॉइंटर 48 गिर जाता है।x29-48 और x30-40 तक।
  2. f9000bf5है strएक असीमित 12-बिट ऑफसेट के साथ 8: 21:10 पर imm12 2 है, Rn है sp, आरटी 21 है।str x21, [sp, #0x10], प्रवेश ऑफसेट -32.
  3. 910003fdहै addतत्कालः imm12 0 है, Rn है sp, Rd 29 है ।mov x29, sp: फ्रेम पॉइंटर रिकॉर्ड पर -48 पर अंकित है।
  4. a9024ff4है stpएक हस्ताक्षरित ऑफसेट के साथः imm7 4 है, 32 तक स्केल किया गया। Rt2 है 19, Rt है 20. stp x20, x19, [sp, #0x20]x20-16, पर।x19-8 पर।

फ्रेम 48 बाइट्स हैः x290 परx30आठ बजेx2116 पर 8 बाइट्स पैडिंग 24 पर।x2032 पर, x19रिकॉर्ड का सबसे निम्न शब्द पिछले रिकॉर्ड को इंगित करता है और अगले शब्द में 6.4.6 की आवश्यकता के अनुसार LR होता है। cmp w0, #0x1पहले और दूसरे स्टोर के बीच बैठता है; शेड्यूलर इसे प्रोग्यू में ले जाता है, और विश्लेषक स्टैक संचालन के बीच तुलना या अंकगणितीय निर्देश को सहन करता है।

संकलक ने दो बार एक ही तथ्य लिखे

एक प्रोग्यूज पार्स एक व्युत्पन्न है। संकलक विंडर के लिए दूसरा निष्पादित करता है, और दोनों सहमत होना चाहिए।

विंडोज पर दूसरी व्युत्पन्न है .pdataऔर .xdata. . निर्माण -O2बिना वस्तु-fno-asynchronous-unwind-tablesऔर llvm-objdump -hदिखाता है.xdata0x34 बाइट्स और .pdata0x48 बाइट्स के. 0x48 है 72, छह RUNTIME_FUNCTIONतीन के साथ 12 बाइट्स के प्रविष्टियाँ IMAGE_REL_AMD64_ADDR32NBप्रत्येक स्थानान्तरण।sum_varargs,keeps,call8,call_varargs,call_pairऔर call_big. अन्य छह फ़ंक्शन पत्ते हैं, और अपवाद संभालने वाले पृष्ठ पर कहा गया है "यदि खोज में फ़ंक्शन तालिका प्रविष्टि नहीं मिलती है, तो कोड को पत्ते फ़ंक्शन का हिस्सा माना जाता है, और आरएसपी सीधे रिटर्न पॉइंटर को संबोधित करता है। "

.xdata के लिए बाइट्सkeepsहैं01 07 04 00 07 32 03 30 02 70 01 60. .UNWIND_INFO3 बिट्स में संस्करण और 5 बिट्स में ध्वज के साथ शुरू होता हैः संस्करण 1, कोई ध्वज नहीं। आकारOfProlog 7, CountOfCodes 4, फ्रेम रजिस्टर और फ्रेमऑफसेट 0 है। प्रत्येक UNWIND_CODEprolog में एक ऑफसेट है और एक बाइट ऑप्ट कोड और जानकारी है। 07 32: ऑफसेट 7 पर, ओप 2 है UWOP_ALLOC_SMALL, सूचना 3 का अर्थ है 3 * 8 + 8 = 32बाइट्स।03 30: op 0 है UWOP_PUSH_NONVOL, रजिस्टर 3 है rbx. .02 70rdi. .01 60rsi. कोड निष्पादन के उल्टे क्रम में संग्रहीत कर रहे हैं, और वे नाम के समान तीन धक्का और 32 बाइट आवंटन के रूप में बाइट्स 56 57 53 48 83 ec 20. . .sum_varargsप्रविष्टि है 01 01 01 00 01 02 00 00: एक कोड के साथ एक-बाइट प्रलोग, UWOP_ALLOC_SMALL8 बाइट्स के. यह है कि विंटर कैसे देखता है.pushq %rax: एक आवंटन, क्योंकि %raxअस्थिर है और कुछ भी बचाया नहीं जा रहा है।

लिनक्स पर दूसरा व्युत्पन्न है .eh_frame, जो कि psABI 4.2.4 द्वारा परिभाषित किया गया है और DWARF 5 अनुभाग 6.4 को कोडित करता है। llvm-dwarfdump --eh-frameस्थानांतरित करने योग्य वस्तु पर प्रति फ़ंक्शन एक FDE प्रिंट करता है और हल करता है R_X86_64_PC32स्थानांतरण, ताकि रेंज इस्तेमाल करने योग्य हैं।keeps:

text  DW_CFA_def_cfa_offset: +16
  DW_CFA_def_cfa_offset: +24
  DW_CFA_def_cfa_offset: +32
  DW_CFA_offset: RBX -32
  DW_CFA_offset: R14 -24
  DW_CFA_offset: RBP -16

CFA कॉल से पहले स्टैक पॉइंटर है, प्रवेश स्टैक पॉइंटर से ऊपर 8 है, इसलिए RBP -16प्रवेश काटाक्ष -8 है, R14 -24-16, है।RBX -32-24, और 32 के सबसे गहरे CFA ऑफसेट 24 बाइट फ्रेम प्लस रिटर्न पता है।keepsकहते हैंDW_CFA_def_cfa: W29 +48के साथW29 -48,W30 -40,W21 -32,W20 -16,W19 -8. वहाँ सीएफए प्रवेश स्टैक पॉइंटर स्वयं है, इसलिए ऑफसेट रूपांतरण के बिना फ्रेम ऑफसेट हैं, और सीएफए को परिभाषित किया गया है x29जबकि फ्रेम रिकॉर्ड लाइव है। eh_frame_cross_check()दोनों वस्तुओं के सभी 12 FDE के लिए इस तुलना में किया जाता है। एक प्रविष्टि देखभाल की आवश्यकता हैः call8सिस्टम वी के तहत दो आउटगोइंग तर्कों को शरीर के मध्य में धक्का देता है, इसलिए इसका सबसे गहरा सीएफए ऑफसेट 32 है जबकि इसका प्रोग्यू फ्रेम 8 है, और चेक सबसे गहरा स्टैक पॉइंटर की तुलना करता है शरीर तक पहुंचता है, 24, प्लस 8.

उपकरण इसे क्या कहते हैं

इस क्षेत्र में प्रत्येक उपकरण में कॉल कन्वेंशन के लिए एक डेटा संरचना और एक पास है जो एक फ़ंक्शन को सौंपता है। नाम अलग-अलग हैं और अंतराल अलग-अलग हैं। बंद उपकरणों का वर्णन उनके दस्तावेज से किया जाता है; खुले उपकरण फिक्स्चर पर चलाए गए थे और उनके आउटपुट को वस्तुओं के बगल में कैप्चर किया जाता है।

Ghidra 12.1.4 (Apache-2.0).नियम संकलक विनिर्देश फ़ाइलों में रहते हैं, .cspec, सस्ते भाषा के बगल में। x86-64-gcc.cspecएक <default_proto>नामित__stdcall, "सिस्टम V एप्लिकेशन बाइनरी इंटरफेस AMD64 आर्किटेक्चर प्रोसेसर सप्लीमेंट अप्रैल 2016 से व्युत्पन्न", के साथ <pentry> के लिए सूचियाँXMM0_QaXMM7_Qaऔर RDI, RSI, RDX, RCX, R8, R9, ऑफसेट 8 पर एक स्टैक प्रविष्टि, एक join_dual_class16 बाइट तक के प्रकारों के लिए नियम, आउटपुट XMM0_Qa, XMM1_Qa, RAX, RDXऔर hidden_returnबाकी के लिए।x86-64-win.cspecघोषणा करता है__fastcallचार के साथ <group>जोड़े वाले तत्व XMM0_Qaके साथRCX,XMM1_Qaके साथRDX, और इतने पर, जो स्थिति नियम है, एक स्टैक प्रविष्टि ऑफसेट 40 पर।convert_to_ptr3, 5, 6 या 7 बाइट्स के स्ट्रैक्ट के लिए टिप्पणी "केवल आकार 8,16,32,64 बिट्स के स्ट्रैक्ट को पंजीकरण के लिए पैक किया जा सकता है", unaffectedसूचीबद्ध करनाRBX RBP RDI RSI RSP R12R15और XMM6XMM15, और एक localrangeस्टैक 8 से 39 तक, जो छाया भंडार है।AARCH64.cspecघोषणा करता है__cdeclजिसका पहला प्रविष्टि है x8के साथstorage="hiddenret", तो q0q7और x0x7,convert_to_ptr17 बाइट या उससे अधिक के स्ट्रक्चर के लिए, और killedbycallसहित x8टिप्पणी के साथ "अप्रत्यक्ष परिणाम स्थान रजिस्टर, जो पेंट्री सूची में प्रतिबिंबित नहीं है। डिकोम्पाइलर का अंतिम चरण "स्थापित करें फ़ंक्शन का प्रोटोटाइप" कहा जाता है; विश्लेषकों "कॉल कन्वेंशन आईडी" और "डिकोम्पाइलर पैरामीटर आईडी" हैं; प्रोग्राम विकल्प "प्रोटोटाइप मूल्यांकन" डिफ़ॉल्ट मॉडल चुनता है।

IDA Pro and IDA Free (Hex-Rays, commercial; current release 9.4, build 9.4.260714).फ़ंक्शन प्रकार सेटटाइप के माध्यम से सेट की गई एक C घोषणा है, और "IDA फ़ंक्शन तर्कों के बारे में जानकारी के साथ विघटन पर टिप्पणी करने के लिए प्रकार की जानकारी का उपयोग करेगा।" ऐसी जगहें जिन्हें कोई मानक सम्मेलन नहीं बनाता है, IDA के स्वयं के सम्मेलन में लिखी जाती हैंः int __usercall func@<ebx>(int x, int y@<esi>)परिणाम को EBXऔर yमें ESIऔर @<^12.4>ऑफसेट और आकार के अनुसार स्टैक स्थान का नाम। __userpurgeएक ही पृष्ठ सूची के साथ एक ही है__spoilsक्लोबर्ड सेट के लिए, __hidden,__return_ptrऔर __struct_ptrइस पाठ को हाथ से प्राप्त किया जाता है।__usercallएक निर्देश के लिए प्रोटोटाइप. IDA मुक्त केवल x86 और x86-64 समर्थन करता है.

Binary Ninja 6.0 "Krypton" (Vector 35, commercial, with a Free edition that decompiles x86, x86_64, ARMv7, and AArch64).ए Platform"बाइनरी के निष्पादन वातावरण से संबंधित सभी जानकारी में शामिल है, मुख्य रूप से उपयोग किए जाने वाले कॉल कन्वेंशन, ऑपरेटिंग सिस्टम और वास्तुकला", और सूची में शामिल हैं linux-x86_64,windows-x86_64,linux-aarch64और mac-aarch64, प्रत्येक के साथ एक default_calling_convention.CallingConventionहैint_arg_regs,float_arg_regs,callee_saved_regs,caller_saved_regs,int_return_reg,high_int_return_reg,float_return_reg, और दो ध्वज जो इस पाठ में अंतर को सीधे एन्कोड करते हैंः arg_regs_share_index, "जब True, Nth तर्क पूरे और फ्लोट रजिस्टर सूची दोनों के Nth स्लॉट को खपत करता है, इसके प्रकार के बावजूद", और stack_reserved_for_arg_regs, "विंडोज x64 कॉल सम्मेलन द्वारा इस्तेमाल छाया / घर स्थान। "eligible_for_heuristics,required_arg_regsऔर required_clobbered_regsप्लेटफार्म प्रकार के अंतर्गत सी हेडर हैं।types/platform/, प्रत्येक प्लेटफार्म नाम के लिए एक।

radare2 6.2.4 (LGPL-3.0, run here). afclवास्तुकला प्लगइन जानता है कि सम्मेलनों की सूची देता हैः amd64 amd64syscall dlang golang golang_abi0 ms p9 reg swiftx86-64 के लिए और arm64 dlang golang p9 reg swiftAArch64 के लिए।afcवर्तमान कार्य की संधि रिपोर्ट करता है, afcrछापें cc.amd64.arg0: rdiपारarg5: r9और ret0: rax, और प्रोफाइल नामित एसडीबी फ़ाइलें हैं cc-ARCH-BITS.sdb. रडार 2 चुना amd64ELF वस्तु के लिए और msCOFF वस्तु के लिए, जो सही विभाजन है। afvतर्क और चरों की सूची देता है, तथा afsलिनक्स पर एक हस्ताक्षर प्रिंट करता है।-O2वस्तु afveight_argsऔर गिर गयाrcx; विंडोज वस्तु पर यह पाया rcx, rdx, r8और चार ढेर स्लॉट और गिर गया r9दोनों गायब रजिस्टर केवल एक के अंदर दिखाई देते हैंlea. . पर .mixedयह रिपोर्ट double arg1 @ xmm0, arg2 @ xmm1, arg3 @ xmm2लिनक्स पर और विंडोज पर कोई तर्क नहीं।x0x7के लिएeight_args, और के लिए big_retयह सूचीबद्ध है x0और double arg2 @ d1जहां फ़ंक्शन पढ़ता है x8और एक निरंतर में लोड करता है q1. हर .afsहस्ताक्षर से शुरू होता है void, क्योंकि रिटर्न प्रकार का अनुमान नहीं लगाया गया है।-O0, जहां हर तर्क फ्रेम में संग्रहीत किया जाता है,afvसभी आठ तर्क पाया eight_argsके साथrbp+0x10और rbp+0x18स्टैक जोड़ी के लिए.

rizin 0.9.1 (LGPL-3.0, run here) and Cutter 2.5.0. afclसूची amd64 amd64syscall ms reg swift. .afcलौटा दिया amd64COFF ऑब्जेक्ट के लिए, तो Microsoft प्रोफ़ाइल मौजूद है और प्रारूप से नहीं चुना गया था। afieight_argsलिनक्स पर, फिर से बिना rcx, और विंडोज पर r8, rcx, rdxतीन स्टैक स्लॉट के साथ, बिना r9और बिना स्लॉट पर 0x40. .afvमौजूद नहीं है; सूची आदेश है afvlकटर रिज़िन पर ग्राफिक फ्रंट एंड है।

angr 10.0.3 (BSD-2, run here). CFGFastइसके बाद CompleteCallingConventions(recover_variables=True)सेट function.calling_conventionSimCCSystemVAMD64,SimCCMicrosoftAMD64या SimCCAArch64, द्विआधारी प्रारूप में, और function.prototypeइसने सभी आठ तर्कों को वापस लिया।eight_argsसभी तीनों लक्ष्यों पर, छुपा संकेत big_retx86-64 पर (BOT*, long long) -> BOT*, विंडोज के उप-संकेत संरचना by_valueजैसे (BOT*, BOT*) -> BOT*, और प्रणाली V वैरिएडिक sum_varargsजैसे (long long, ...). . . इसकी खाई रडार2 से अलग है:mixedवापस आया था (long long, long long, double, float)प्रणाली V और AArch64 पर, सही रजिस्टर गलत क्रम में होते हैं, क्योंकि पूर्णांक सूची फ्लोट सूची से पहले छपी होती है; AArch64 big_retवापस आया था (long long) -> double, तो x8सम्मेलन चूक गया और परिणामस्वरूप वेक्टर स्टोर को पढ़ा गया; और एआरएच 64 sum_varargs16 मापदंडों के साथ वापस आया, पूरे रजिस्टर बचत क्षेत्र.

Others.Hopper 6.6.0 macOS और लिनक्स के लिए एक वाणिज्यिक विघटन और विघटनकर्ता है; इसके सम्मेलन संभाल इस पाठ के लिए चलाया या प्रलेखित नहीं किया गया था। lldb 1703 और gdb लाइव फ्रेम के साथ दिखाएँ frame infoऔर register readकंपाइलर एक्सप्लोरर स्रोत से तीन सूचियों को एक साथ दिखाता है, जो बाइट्स में खोज करने से पहले एक नियम की पुष्टि करने का सबसे तेज़ तरीका है।

ConceptGhidraIDABinary Ninjaradare2 / rizinangrthis lesson
The convention objectprototype in a .cspeccalling convention keyword in the function typeCallingConvention on a Platformafc name, cc-ARCH-BITS.sdbSimCC subclassexpected_locations(abi, name)
Default per target<default_proto> per cspeccompiler settingPlatform.default_calling_conventionafc=, anal.ccby formatdescribe_object()
Argument registers<pentry> list, <group> for positional@<reg> in __usercallint_arg_regs, float_arg_regs, arg_regs_share_indexcc.NAME.argNARG_REGS, FP_ARG_REGSINT_ARG_REGS, FLOAT_ARG_REGS
Shadow storelocalrange stack 8 to 39implicitstack_reserved_for_arg_regscc.ms.argn=stackSTACKARG_SP_DIFFSTACK_ARG_BASE[WIN64] = 40
Hidden result pointerhidden_return, storage="hiddenret"__return_ptr, __hiddenreturn type too large for int_return_regnot modelledBOT* first argumentHIDDEN_RETURN_REG
Callee-saved<unaffected>__spoils lists the oppositecallee_saved_regsprofileCALLEE_CLEANUP and archCALLEE_SAVED
Assigning one to a function"Call Convention ID" analyzer, "Establish Function's Prototype"user or type libraryheuristic detection with required_arg_regsafc, afva, aaftCallingConventionAnalysisread-before-write plus the rulebook

इसे बनाओ

डेमो चलाएँः

bashcd phases/13-tools-and-protocols/38-calling-conventions-and-stack-frames
python3 code/main.py

आउटपुट एक JSON रिपोर्ट है। सात वस्तुओं में से प्रत्येक के लिए यह प्रारूप, मशीन और एबीआई का नाम देता है, और बारह कार्यों में से प्रत्येक के लिए यह प्रोग्यू बाइट्स, प्रविष्टि ऑफसेट के साथ सहेजे गए रजिस्टर, फ्रेम आकार, फ्रेम पॉइंटर, पैरामीटर रजिस्टर, स्टैक तर्क ऑफसेट, spill, रिटर्न रजिस्टर या छिपा पॉइंटर, कॉल गहराई, और कॉलर्स के आउटगॉउन्डिग रजिस्टर और स्टोर देता है। प्रत्येक फ़ंक्शन की जाँच उसके चेक के साथ समाप्त होती हैः नियम, हस्ताक्षर से अपेक्षित मूल्य, बाइट्स से देखे गए मूल्य, और क्या वे सहमत हैं। रिपोर्ट दो रिवाइंड क्रॉस-चेक और उपकरण कैप्चर के साथ समाप्त होती है। 84 कार्य विश्लेषणों में 489 जांचें चलती हैं और सभी 489 सहमत हैं।

अपेक्षित आकारः

json{
  "objects": {
    "convention-x86_64-windows-O2": {
      "format": "COFF", "machine": "AMD64", "abi": "Microsoft x64", "checks": "73/73 agree",
      "functions": {
        "keeps": {
          "prologue": ["56  pushq %rsi", "57  pushq %rdi", "53  pushq %rbx", "48 83 ec 20  subq $0x20, %rsp"],
          "frameBytes": 56,
          "savedRegisters": [{"register": "rsi", "entryOffset": -8}, {"register": "rdi", "entryOffset": -16}, {"register": "rbx", "entryOffset": -24}],
          "parameterRegisters": ["rcx"],
          "returnRegisters": ["rax"],
          "calls": [{"callee": "ext", "depth": 56}],
          "checks": [{"rule": "Microsoft x64: the caller must always allocate ...", "expected": "at least 32 bytes ...", "observed": [56], "agree": true}, "..."]
        }
      }
    }
  },
  "sehCrossCheck": ["..."],
  "ehFrameCrossCheck": {"...": "..."}
}

पढ़िए code/main.pyइस क्रम मेंः

  1. describe_object()ELF, COFF या Mach-O हेडर और प्रतीक तालिका को पढ़ता है और ABI और फ़ंक्शन सूची लौटाता है।
  2. parse_listing()विभाजित करता है llvm-objdump -d -rपाठ फ़ंक्शन में, प्रत्येक कॉल और कूद के स्थानांतरण लक्ष्य को बनाए रखता है, और सही वाक्यविन्यास के मार्कर के साथ टिप्पणियों को हटा देता हैः #AT&T के लिए, //और ;AArch64 के लिए, जहां #तत्काल पेश करता है।
  3. effect_x86()और effect_arm()एक निर्देश को रीड्स, एड्रेस रीड्स, राइट्स, लोड, स्टोर और स्टैक पॉइंटर डेल्टा में बदल दें। अंतिम एटी एंड टी ऑपरेंड गंतव्य है; पहला एआरएच 64 ऑपरेंड स्टोर, तुलना और शाखाओं को छोड़कर गंतव्य है। leaपता पढ़ता है और कुछ भी लोड नहीं करता है।mov,cvtsi2sd,setcc, और शून्य शब्दxor r, rएक कॉल पूरे अस्थिर सेट लिखता है। एक पूर्व-अनुक्रमित स्टोर चलता है।spपहुँच और पोस्ट-अनुक्रमित लोड के बाद इसे स्थानांतरित करने से पहले।
  4. analyze_function()शरीर को एक बार ऊपर ले जाता है।retयह स्टैक पॉइंटर की गहराई को उसके प्रवेश मूल्य से नीचे ट्रैक करता है, इसलिए एक मेमोरी ऑपरेंडdisp(%rsp)प्रवेश के समय में है disp - depthऔर disp(%rbp)है frame_pointer + disp. यह तर्क-वर्ग रजिस्टरों को रिकॉर्ड करता है जो उन्हें लिखने से पहले पढ़े जाते हैं, एबीआई के पहले स्टैक तर्क ऑफसेट पर या उससे ऊपर लोड करता है, फ़्रेम में तर्क रजिस्टरों को स्टोर करता है, वर्तमान स्टैक पॉइंटर से नीचे पहुंचता है, और प्रत्येक कॉल पर गहराई।
  5. _return_registers()एक परिणाम रजिस्टर जब उसके अंतिम पहुँच से पहले बाहर निकलने एक लेखन है रखता है. एक डेटा ऑपरेंड के रूप में पढ़ा उम्मीदवार को मारता है; एक पते के आधार के रूप में पढ़ा नहीं करता है, जो है जो रखता है raxजीवित में big_retमाइक्रोसॉफ्ट x64 के अंतर्गत, जहां movq %rcx, %raxइसके बाद दुकानों से (%rax). .
  6. expected_locations()एक सी हस्ताक्षर पर तीन नियम पुस्तकों को लागू करता हैः दो सिस्टम वी सूची आठ बाइट वर्गों के साथ, माइक्रोसॉफ्ट स्थिति स्लॉट उप-संदर्भ स्ट्रक्चर के साथ, और AAPCS64 counters के साथ x816 बाइट से ऊपर के परिणामों के लिए।
  7. run_checks()दोनों की तुलना करता है और फ्रेम नियम जोड़ता हैः कॉल से सहेजे गए रजिस्टरों को सहेजा जाना चाहिए, (rsp + 8) % 16 == 0या sp % 16 == 0प्रत्येक कॉल पर, 32 बाइट्स छाया प्रत्येक माइक्रोसॉफ्ट कॉल पर, माइक्रोसॉफ्ट और AAPCS64 के तहत स्टैक पॉइंटर के नीचे कोई पहुंच नहीं और अधिकतम 128 बाइट्स सिस्टम V के तहत, और एक एपिलोग जो स्टैक पॉइंटर के साथ प्रोग्यू को उसके प्रवेश मूल्य पर दर्शाता है।
  8. seh_cross_check()और eh_frame_cross_check()बिना निर्मित वस्तुओं के विघटन डेटा पढ़ें -fno-asynchronous-unwind-tablesऔर इसे प्रोग्रैग विश्लेषण के साथ तुलना करें।

तीन और आदेश मौजूद हैं।python3 code/main.py analyze convention-x86_64-windows-O2 keepsएक फ़ंक्शन प्रिंट करता है। python3 code/main.py object FILEशीर्षक के फैसले को प्रिंट करता है।python3 code/main.py sehडिकोड को प्रिंट करता है UNWIND_INFOप्रत्येक कार्य का जो एक है।

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

इस क्रम में विश्लेषक का उपयोग करें एक नए वस्तु पर आप बनाया है।

  1. दौड़ेंpython3 code/main.py object FILE.oऔर लिखें कि ABI यह नाम है. अगर हेडर और अपने निर्माण आदेश असहमत हैं, बंद करो; उनमें से एक गलत है.
  2. पकड़ llvm-objdump -d -r FILE.oऑब्जेक्ट के बगल में और विश्लेषण चलाएं. कुछ और से पहले प्रोग्रू बाइट्स और सहेजे गए रजिस्टर पढ़ें, और फ्रेम आकार के साथ तुलना करें धक्का और sub. .
  3. पैरामीटर रजिस्टर और स्टैक ऑफसेट को अपने स्रोत हस्ताक्षर के खिलाफ पढ़ें। एक रजिस्टर जिसे हस्ताक्षर स्पष्ट नहीं करता है वह एक छिपा सूचक, एक वैरिएडिक मार्कर या एक खरोंच रजिस्टर है जिसे विश्लेषक ने तर्क के लिए गलत समझा। अंतिम मामला प्रभाव तालिका में एक बग है। उस निर्देश के लिए सामान्य नियम के साथ इसे ठीक करें।
  4. उन चेक को पढ़ें जो असहमत हैं, यदि कोई हो तो प्रत्येक नियम का नाम है जिस पर यह निर्भर करता है, इसलिए सबसे पहले नियम, फिर बाइट्स, फिर विश्लेषक की जांच करना है।
  5. जब वस्तु के पास रिवाइंड डेटा हो, तो क्रॉस-चेक चलाएं। दो सहमत व्युत्पन्न इस पाठ का सबसे मजबूत सबूत है।
  6. जब कोई अन्य उपकरण स्थापित हो, तो उस कमांड के साथ उसका आउटपुट कैप्चर करें और तुलना करें। जहां यह सहमत है, आपके पास तीन व्युत्पन्न हैं। जहां यह भिन्न है, आपके पास एक विशिष्ट रजिस्टर और एक विशिष्ट निर्देश है जिसे देखने के लिए, और यह सबक 35 के लिए एक निष्कर्ष है।

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

लैब ए: एक निर्देश, तीन प्रभाव

codeनिर्देशिका, खुला पायथनः

bashcd phases/13-tools-and-protocols/38-calling-conventions-and-stack-frames/code
python3 -q

दौड़ें:

pythonfrom main import *
effect(Instruction(0, "48 8d 04 0a", "leaq", "(%rdx,%rcx), %rax"), SYSV)
effect(Instruction(0, "f2 0f 2a d7", "cvtsi2sd", "%edi, %xmm2"), SYSV)
effect(Instruction(0, "a9bd7bfd", "stp", "x29, x30, [sp, #-0x30]!"), AAPCS64)

प्रत्येक कॉल से पहले आप जो पठन, पता, लिखता है और स्टैक डेल्टा की उम्मीद करते हैं, उसे लिखें। फिर बदलें।[sp, #-0x30]![sp], #0x30के साथldpऔर डेल्टा से आगे बढ़ने की देख sp_deltapost_sp_delta. .

लैब बीः कैप के फ्रेम

pythoninfo, listing = load_fixture("convention-aarch64-linux-O2")
keeps = analyze_function("keeps", listing["keeps"], info.abi)
keeps.saved
keeps.frame_pointer
[c.to_dict() for c in frame_cells(keeps)]

ऊपर दिए गए हाथ डिकोड के साथ ऑफसेट की तुलना करें। फिर लोड करें convention-arm64-macos-O2और एक ही तीन पंक्तियों को चलाने; रिकॉर्ड -16 पर चला जाता है और चार रजिस्टर इसके नीचे बैठते हैं. फिर लोडconvention-x86_64-linux-O2-fpऔर पढ़ेंsavedके लिएkeeps: फ्रेम पॉइंटर वापस आ गया है, और एक भी है pushq %raxजिसका एकमात्र काम करना है(rsp + 8)कॉल पर 16 का गुणक।

लैब सी: मिश्रित पर पढ़ने से पहले लिखने

pythoninfo, listing = load_fixture("convention-x86_64-linux-O2")
mixed = analyze_function("mixed", listing["mixed"], info.abi)
mixed.param_registers
mixed.return_registers
expected_locations(SYSV, "mixed")["parameters"]
expected_locations(WIN64, "mixed")["parameters"]

सिस्टम V सूची है rdi, xmm0, rsi, xmm1और माइक्रोसॉफ्ट सूची है rcx, xmm1, r8, xmm3अब विंडोज ऑब्जेक्ट लोड करें और पहले तीन पंक्तियों को फिर से चलाएं. फिर लिनक्स ऑब्जेक्ट के लिए रडार 2 कैप्चर खोलें और खोजें[afv mixed]तीसरे को समझाता है कि निर्देश खोजें।

लैब डीः विन्डरिंग की दूसरी राय

pythonfor row in seh_cross_check():
    print(row["function"], row["sizeOfProlog"], row["unwindPushes"], row["unwindAllocBytes"], row["agree"])
for row in eh_frame_cross_check("convention-x86_64-linux-O2-eh"):
    print(row["function"], row["cfiSaved"], row["cfiMaxOffset"], row["deepestStack"], row["agree"])

pushq %raxsum_varargsSEH तालिका में और समझाएं कि क्यों विन्ड कोड इसे आवंटन के रूप में रिकॉर्ड करता है।call8.eh_frameटेबल और 32 की व्याख्या।

अभ्यास प्रयोगशाला

एक चौथा लक्ष्य और एक चौथा नियम जोड़ें।

आवश्यकताएँः

  • संकलित करेंconvention.cके साथ-target x86_64-apple-macos -O2और इसकी सूची को कैप्चर करें। विस्तार describe_object()मच-ओ को पढ़ने के लिए cputype0x01000007 और इसे सिस्टम V psABI के लिए मैप, जो एप्पल x86-64 पर पालन करता है, के साथ _प्रतीक पूर्वावलोकन।
  • जांचें कि प्रत्येक फ़ंक्शन सिस्टम V चेक के साथ मेल खाती है। प्रोग्रैम में आप जो अंतर पाते हैं उसे रिकॉर्ड करेंः डार्विन हर अनुकूलन स्तर पर फ्रेम पॉइंटर रखता है।
  • एक जोड़ें -fstack-protector-strongलिनक्स x86-64 ऑब्जेक्ट का निर्माण. प्रोग्यू स्कैन विस्तारित ताकि कैनरी लोड से %fs:0x28और स्थानीय लोगों के नीचे की दुकान एक के रूप में दर्ज की जाती है canaryसेल, और तुलना पहले retजैसे कि एक चेक जो एपिलॉग की रक्षा करता है।
  • एक परीक्षण जोड़े जो असफल हो जब एक कैनरी स्टोर को एक बहरा तर्क के रूप में गिना जाता है।
  • का फ्रेम लिखेंwith_canary-शैली कोड हाथ से विश्लेषणकर्ता चलाने से पहले, और परीक्षण के साथ ड्राइंग रखें.

सफलता का अर्थ हैpython3 code/main.pyप्रत्येक जांच के साथ नए वस्तु की रिपोर्ट करता है, और संरक्षित फ़ंक्शन के फ्रेम कोशिकाओं स्थानीय लोगों और सहेजे गए रजिस्टर के बीच कैनरी दिखाते हैं।

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

इस सबक में शामिल है outputs/skill-calling-conventions-and-stack-frames.md. इसका उपयोग आप बनाया गया एक वस्तु, इसकी सूची और सी हस्ताक्षर पैरामीटर स्थानों, एक फ्रेम लेआउट, एक वापसी रजिस्टर में बदलने के लिए, और यह नियम के खिलाफ हर निष्कर्ष की जांच करने के लिए किया जाता है पर आधारित है और संकलक के विन्ड डेटा के खिलाफ. यह शीर्षकों के ABI निर्णय की आवश्यकता है पहले, पढ़ने के लिए मना करता है ।cvtsi2sdया xor r, rगंतव्य के रूप में पढ़ता है, और पहले असहमति के साथ समाप्त होता है।

जाँचें

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

bashcd phases/13-tools-and-protocols/38-calling-conventions-and-stack-frames
python3 code/main.py
python3 -m unittest discover -s code/tests -v

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

  • सात वस्तुओं के नाम प्रणाली वी, माइक्रोसॉफ्ट x64, AAPCS64 और एप्पल arm64 के ELF, COFF और Mach-O हेडर
  • प्रत्येक वस्तु के प्रतीक तालिका Mach-O के साथ एक ही बारह कार्यों को सूचीबद्ध करता है_उपसर्ग और ltmp0लेबल हल
  • सूची पार्सर रखता है extकॉल लक्ष्य के रूप में और AArch64 तत्काल रखता है जैसे #-0x30अछूता
  • leaq (%rdx,%rcx), %raxदो पते रजिस्टर पढ़ता है और कुछ भी लोड नहीं करता है; xorl %eax, %eaxऔर cvtsi2sdकेवल लेखन-केवल हैं; पूर्व-अनुक्रमित हैं stpचाल spदुकान से पहले और पोस्ट-इंडेक्सldpलोड के बाद
  • keepsपर-O2है 55 41 56 53सिस्टम V पर 24 बाइट फ्रेम और कोई फ्रेम पॉइंटर नहीं, 56 57 53 48 83 ec 20माइक्रोसॉफ्ट x64 पर 56 बाइट फ्रेम के साथ, और a9bd7bfd-48 पर रिकॉर्ड के साथ AArch64 पर पहला
  • एप्पल बिल्ड रिकॉर्ड को -16 पर रखती है चार सहेजे गए रजिस्टर से ऊपर
  • प्रत्येक फ़ंक्शन में एक फ्रेम पॉइंटर है -O0लिनक्स पर और कोई नहीं -O2और -fno-omit-frame-pointerएक संरेखण धक्का के साथ इसे वापस लाता है
  • -O0पत्ती प्रणाली V के तहत लाल क्षेत्र के 8 बाइट्स का उपयोग करता है, pushq %raxMicrosoft x64 के अंतर्गत, तथा sub sp, sp, #0x10AAPCS64 के अंतर्गत; -mno-red-zoneलाल क्षेत्र का उपयोग हटा देता है
  • प्रत्येक एपिलॉग अपने प्रोग्रू को दर्शाता है और स्टैक पॉइंटर प्रवेश पर वापस है
  • eight_args6 रजिस्टर और स्लॉट 8 और 16, 4 रजिस्टर और स्लॉट 40 से 64 तक पढ़ता है, या x0x7, दोनों अनुकूलन स्तरों पर
  • mixedहै rdi, xmm0, rsi, xmm1प्रणाली V और rcx, xmm1, r8, xmm3Microsoft x64 के तहत, के साथ xmm0दोनों पर परिणाम
  • 16-बाइट संरचना प्रणाली V और AAPCS64 के तहत दो रजिस्टरों में और संदर्भ के तहत माइक्रोसॉफ्ट x64 के तहत एक छिपे हुए पॉइंटर के पीछे यात्रा करता है
  • 32 बाइट संरचना के माध्यम से वापस आता है rdi,rcxया x8
  • विविधतापूर्ण कैली पढ़ता है %al, घरों rdx, r8, r916, 24, 32, बचत करता है x1x7और q0q7, या Apple पर कॉल करने वाले के स्टैक पॉइंटर पढ़ता है
  • कॉल करने वाले अपेक्षित रजिस्टर सेट करते हैं, 0 और 8 पर 7 और 8 या 5 से 8 पर 32 से 56, शून्य पर स्टोर करते हैं %alवेरिएडिक कॉल के लिए, और परिणाम बफर पते में पारित rdi,rcxया x8
  • सभी 489 जांच अपने नियमों के साथ सहमत हैं
  • छठी UNWIND_INFOप्रविष्टियाँ प्रलाप विश्लेषण से मेल खाती हैं और पत्तियों में कोई नहीं है
  • 24 एफडीई सहेजे गए रजिस्टर ऑफसेट और सबसे गहरे स्टैक पॉइंटर से मेल खाते हैं
  • उपकरण रिकॉर्ड रडार2 की कैप्चर करता है amd64और ms, रिजिन की amd64COFF वस्तु पर, गायब rcx, तीन डबल्स, और Angr के प्रोटोटाइप
  • साइट मॉड्यूल में एम्बेडेड आंकड़ा डेटा विश्लेषक के आउटपुट के बराबर है

उत्पादन विफलता मोड

FailureWhat the weak tool reportsWhat the analysis must prove
COFF object read as System Vrdi and rsi as arguments that the function never readsThe header says 0x8664; the convention is Microsoft x64
Register read only inside a leaSeven arguments for an eight-argument functionBase and index registers are reads
cvtsi2sd destination counted as a readA third double argument in xmm2The first write to xmm2 is a write; mixed has two SSE arguments
Positional slots read as sequencesxmm0 named as the double argument on WindowsSlot 1 is XMM1, slot 3 is XMM3
push rbp taken as a frame pointerA frame pointer in an -O2 function that uses rbp as an accumulatorOnly mov %rsp, %rbp after the push creates one
Red zone measured from the entry stack pointer216 bytes "below the stack pointer"128 bytes below the current stack pointer after an 88-byte sub
Shadow store treated as localsStack arguments at 8 to 32Microsoft stack arguments start at 40
lea treated as a loadA stack argument at 0x60(%rsp) in a variadic calleelea computes overflow_arg_area; it loads nothing
Hidden pointer read as a normal argumentbig_ret(void *, long long)The pointer is stored through and copied to rax, rax, or stays in x8
Deepest CFA compared with the prologue frameA mismatch on a caller that pushes outgoing argumentsCompare with the deepest stack pointer the body reaches
Convention asserted from the ISAamd64 for a COFF objectThe format picks the rulebook, then the ISA picks the registers
Prototype trusted from one toolThree doubles, or sixteen variadic parametersThe source signature or a second derivation must agree

परिचालन नियम

पहले निर्देश से पहले हेडर पढ़ें, और नियम पुस्तिका का नाम दें। प्रत्येक सहेजे गए रजिस्टर और प्रत्येक तर्क को प्रविष्टि स्टैक पॉइंटर से एक ऑफसेट पर रखें, और इसे उस नियम के खिलाफ जांचें जो इसे वहां रखा था और उसी फ्रेम के लिए संकलक द्वारा लिखे गए अनविंड डेटा के खिलाफ। बाइट्स या स्रोत की पुष्टि होने तक किसी उपकरण के प्रोटोटाइप को दावा के रूप में देखें।

आगे पढ़ना

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.