رفتن به مطلب
مرجع رسمی سی‌پلاس‌پلاس ایران

برنامه نویسی

  • نوشته‌
    24
  • دیدگاه
    19
  • مشاهده
    9,763

مشارکت‌کنندگان این وبلاگ

قسمت سوم بررسی استراکچرها در زبان C - فهمایش در سطح دیزاسمبلی

cdefender

39 بازدید

بررسی دیزاسمبلی x86 در لینوکس – کامپایلر gcc

به منظور بررسی دیزاسمبلی کد منبع C در لینوکس که محتوای آن در تصویر 2 نمایش داده شد، ابتدا خروجی تولید شده توسط کامپایلر gcc برای معماری x86 را توسط دیباگر gdb مورد بررسی قرار خواهیم داد. همانطور که در تصویر 12 قابل مشاهده است، وقتی کامپایلر باینری را با فلگ -ggdb کامپایل می‌کند، به دلیل وجود اطلاعات دیباگ درون باینری، دیباگر gdb می‌تواند اطلاعات تکمیلی و کامل درباره ساختار درونی باینری ELF ما نمایش دهد. برنامه ای که در تصویر 12 برای دیباگ باینری مورد استفاده قرار گرفته است، GDB Dashboard است که علاوه بر کد منبع، کد دیزاسمبلی، اطلاعات مرتبط با پشته و رجیسترها و خروجی که توسط کدها در حال تولید است، نمایش می‌دهد.

Picture12.png.0157910b8c9ff094caf4b823bdd378ae.png

تصویر 12: خروجی دیزاسمبلی برنامه در محیط لینوکس

در ادامه به منظور درک بهتر کدی که توسط gcc برای معماری x86 تولید شده است، خط به خط مورد بررسی و تحلیل قرار خواهیم داد تا با رفتار کامپایلر gcc در تولید کدهای اسمبلی آشنا شویم. همانطور که در تصویر 13 قابل نمایش است، وقتی دستورالعمل call فراخوانی می‌شود، مجدد آدرس دستورالعمل بعدی را درون پشته قرار خواهد داد. آدرسی که توسط call بر روی پشته قرار گرفته است، در تصویر 13 با رنگ قرمز قابل مشاهده است.

Picture13.png.2289ce6cb57d2008be914ef062827ba1.png

تصویر 13: خروجی اجرای دستورالعمل call

وقتی وارد تابع StructureAnalysis می‌شویم، در گام اول با Prologue تابع روبه‌رو خواهیم شد. در Prologue تابع StructureAnalysis بعد از اینکه فریم جدید تابع ایجاد می‌شود،  مقدار 0x424 (1060 دسیمال) از مقدار جاری رجیستر ESP کم خواهد شد. این مقدار نشان می‌دهد که 1060 بایت برای ذخیره‌سازی متغییرهای درون این تابع رزرو خواهد شد.

Picture14.png.1c91e24341b9189a349327051da86484.png

تصویر 14: دیزاسمبی مقداردهی اعضای Structure توسط GCC

خروجی که توسط کامپایلر GCC تولید شده است، مشابه خروجی MSVC است. متغیرهای درون Struct با استفاده از آدرس پایه‌ای که درون رجیستر EBP قرار دارد، و همچنین آفست‌هایی که مشخص کننده اندازه متغیرها است، مقداردهی می‌شوند. برای کار با داده‌های اعشاری هم از دستورات fld و fstp استفاده شده است. در نهایت وقتی استراکچر مقداردهی اولیه شد، برای نمایش مقادیر آن‌ها به تابع printf عبور داده می‌شوند.

Picture15.png.314287d3abe1be2b782bc7c1766b8fb9.png

تصویر 15: خروجی دیزاسمبلی تابع StructureAnalysis

شایان ذکر است، در ابتدای هر تابع درون برنامه همانطور که در تصویر 15 و تصویر 16 قابل مشاهده است، یک تابع با نام __x86.get_pc_thunk.bx فراخوانی می‌شود. در تجزیه و تحلیل یک باینری لینوکس، تابع __x86.get_pc_thunk.bx معمولاً همراه با ثبت ebx برای محاسبه آدرس پایه یک بخش داده یا کد مستقل از موقعیت (PIC) استفاده می‌شود. هدف __x86.get_pc_thunk.bx این است که آدرس دستور بعدی را در رجیستر ebx بارگذاری کند. این امکان را فراهم می‌کند تا دستورات بعدی بتوانند از ebx به عنوان یک رجیستر پایه استفاده کنند تا به داده‌ها یا کدهای نسبت به موقعیت فعلی در حافظه دسترسی پیدا کنند. با استفاده از __x86.get_pc_thunk.bx و ebx، کدهای مستقل از موقعیت می‌توانند به گونه‌ای نوشته شوند که به آدرس مطلق کد یا داده وابسته نباشند. شایان ذکر است، پیاده‌سازی __x86.get_pc_thunk.bx ممکن است بسته به کامپایلر و توزیع لینوکس متفاوت باشد. این تابع معمولاً توسط کتابخانه‌های اجرایی کامپایلر یا کتابخانه C سیستم ارائه می‌شود. در تصویر 16، این موضوع قابل نمایش است.

Picture16.png.ce3607a80ebe075ada9ad525040feba5.png

تصویر 16: نمایش فراخوانی __x86.get_pc_thunk.ax در ابتدای تابع main

بررسی دیزاسمبلی x86 در لینوکس – کامپایلر clang

خروجی که کامپایلر clang تولید می‌کند به مراتب از خروجی که توسط GCC و MSVC در مراحل قبلی تولید شده است، متفاوتر است. در ادامه خروجی نسخه 14 کامپایلر clang را برای برنامه‌ای که به زبان C نوشته شده بود، مورد بررسی قرار خواهیم داد. در تصویر 17، ساختار دیزاسمبلی تابع main قابل مشاهده است که توسط کامپایلر clang تولید شده است. یکی از مهم ترین تفاوت هایی که در خروجی تولید شده توسط کامپایلر clang نسبت به gcc و msvc وجود دارد، در شروع توابع است. خروجی که clang تولید می‌کند، بعد از ایجاد فریم برای تابع، ما یک دستور call خواهیم داشت. هنگامیکه این دستور call اجرا می‌شود، تازه بدنه اصلی تابع شروع به اجرا خواهد شد.

Picture17.png.a327d8272c004b35dc0618f4131a9841.png

تصویر 17: دیزاسمبلی کد تولید شده توسط clang

همانطور که در تصویر 17 قابل مشاهده است، در ابتدای تابع یک call به یک لیبل با عنوان LAB_000112EA داریم که از ابتدای آن اجرای بدنه اصلی تابع شروع می‌شود. تفاوت بعدی که وجود دارد، Calling Convention مورد استفاده توسط کامپایلر clang است که نسبت به msvc و gcc تفاوت دارد. به عنوان مثال، در تصویر 18 اصول عبور پارامترها به تابع printf قابل مشاهده است. در خروجی دیزاسمبلی clang به جای PUSH شدن پارامترها از راست به سمت چپ درون پشته، در خروجی clang از رجیسترها برای عبور پارامترها به توابع C استفاده می‌شود.

Picture18.png.a4797140d8e836d4dc5dc5eaa1d6c9b5.png

تصویر 18: نحوه عبور پارامترها به تابع

با توجه بررسی خروجی کامپایلرهای GCC و MSVC و Clang در پردازش Structها در زبان سی تفاوت برجسته ای وجود نداشت. هر سه کامپایلر برای مقداردهی اعضای استراکچر از آدرس پایه ای استفاده کرده بودند که توسط رجیستر EBP مورد ارجاع قرار می‌گرفت. دو کامپایلر GCC و MSVC از یک اصول تقریبا مشابه‌ای دنباله روی می کردند، ولی clang به جای استفاده از Stack از رجیسترهای عمومی x86 برای عبور پارامترها به توابع استفاده می‌کرد.

پایان.

 



0 دیدگاه


نظرهای پیشنهاد شده

هیچ دیدگاهی برای نمایش وجود دارد.

مهمان
افزودن دیدگاه

×   شما در حال چسباندن محتوایی با قالب بندی هستید.   حذف قالب بندی

  تنها استفاده از ۷۵ اموجی مجاز می باشد.

×   لینک شما به صورت اتوماتیک جای گذاری شد.   نمایش به عنوان یک لینک به جای

×   محتوای قبلی شما بازگردانی شد.   پاک کردن محتوای ویرایشگر

×   شما مستقیما نمی توانید تصویر خود را قرار دهید. یا آن را اینجا بارگذاری کنید یا از یک URL قرار دهید.

  • کاربران آنلاین در این صفحه   0 کاربر

    هیچ کاربر عضوی،در حال مشاهده این صفحه نیست.

×
×
  • جدید...