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

تمامی فعالیت ها

این جریان به طور خودکار بروزرسانی می شود     

  1. هفته گذشته
  2. جدیدا
  3. mag77

    شما در محیط لینوکس، وقتی کیوت رو نصب می‌کنید برای استفاده از اون مخصوصاً ساخت (کامپایل، بیلد) و اجرای برنامه نیاز به یک سری ابزار‌ها و کتابخانه‌های پیش‌نیاز دارید که با دستورات زیر باید پکیج‌های مربوط به اون رو نصب کنید. sudo apt-get install build-essential sudo apt-get install mesa-common-dev حالا شما دستور منسوخ شده رو اجرا کردین که ظاهراً مشکلتون حل شده، دلیلش هم احتمالاً دو ابزار qt4-qmake (= 4:4.8.7+dfsg-17) و qt4-dev-tools در پکیج هستش که نسخه‌های به‌روز تر و بهترش در پکیج build-essential موجود هست.
  4. بررسی دیزاسمبلی x86 در لینوکس – کامپایلر gcc به منظور بررسی دیزاسمبلی کد منبع C در لینوکس که محتوای آن در تصویر 2 نمایش داده شد، ابتدا خروجی تولید شده توسط کامپایلر gcc برای معماری x86 را توسط دیباگر gdb مورد بررسی قرار خواهیم داد. همانطور که در تصویر 12 قابل مشاهده است، وقتی کامپایلر باینری را با فلگ -ggdb کامپایل می‌کند، به دلیل وجود اطلاعات دیباگ درون باینری، دیباگر gdb می‌تواند اطلاعات تکمیلی و کامل درباره ساختار درونی باینری ELF ما نمایش دهد. برنامه ای که در تصویر 12 برای دیباگ باینری مورد استفاده قرار گرفته است، GDB Dashboard است که علاوه بر کد منبع، کد دیزاسمبلی، اطلاعات مرتبط با پشته و رجیسترها و خروجی که توسط کدها در حال تولید است، نمایش می‌دهد. تصویر 12: خروجی دیزاسمبلی برنامه در محیط لینوکس در ادامه به منظور درک بهتر کدی که توسط gcc برای معماری x86 تولید شده است، خط به خط مورد بررسی و تحلیل قرار خواهیم داد تا با رفتار کامپایلر gcc در تولید کدهای اسمبلی آشنا شویم. همانطور که در تصویر 13 قابل نمایش است، وقتی دستورالعمل call فراخوانی می‌شود، مجدد آدرس دستورالعمل بعدی را درون پشته قرار خواهد داد. آدرسی که توسط call بر روی پشته قرار گرفته است، در تصویر 13 با رنگ قرمز قابل مشاهده است. تصویر 13: خروجی اجرای دستورالعمل call وقتی وارد تابع StructureAnalysis می‌شویم، در گام اول با Prologue تابع روبه‌رو خواهیم شد. در Prologue تابع StructureAnalysis بعد از اینکه فریم جدید تابع ایجاد می‌شود، مقدار 0x424 (1060 دسیمال) از مقدار جاری رجیستر ESP کم خواهد شد. این مقدار نشان می‌دهد که 1060 بایت برای ذخیره‌سازی متغییرهای درون این تابع رزرو خواهد شد. تصویر 14: دیزاسمبی مقداردهی اعضای Structure توسط GCC خروجی که توسط کامپایلر GCC تولید شده است، مشابه خروجی MSVC است. متغیرهای درون Struct با استفاده از آدرس پایه‌ای که درون رجیستر EBP قرار دارد، و همچنین آفست‌هایی که مشخص کننده اندازه متغیرها است، مقداردهی می‌شوند. برای کار با داده‌های اعشاری هم از دستورات fld و fstp استفاده شده است. در نهایت وقتی استراکچر مقداردهی اولیه شد، برای نمایش مقادیر آن‌ها به تابع printf عبور داده می‌شوند. تصویر 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، این موضوع قابل نمایش است. تصویر 16: نمایش فراخوانی __x86.get_pc_thunk.ax در ابتدای تابع main بررسی دیزاسمبلی x86 در لینوکس – کامپایلر clang خروجی که کامپایلر clang تولید می‌کند به مراتب از خروجی که توسط GCC و MSVC در مراحل قبلی تولید شده است، متفاوتر است. در ادامه خروجی نسخه 14 کامپایلر clang را برای برنامه‌ای که به زبان C نوشته شده بود، مورد بررسی قرار خواهیم داد. در تصویر 17، ساختار دیزاسمبلی تابع main قابل مشاهده است که توسط کامپایلر clang تولید شده است. یکی از مهم ترین تفاوت هایی که در خروجی تولید شده توسط کامپایلر clang نسبت به gcc و msvc وجود دارد، در شروع توابع است. خروجی که clang تولید می‌کند، بعد از ایجاد فریم برای تابع، ما یک دستور call خواهیم داشت. هنگامیکه این دستور call اجرا می‌شود، تازه بدنه اصلی تابع شروع به اجرا خواهد شد. تصویر 17: دیزاسمبلی کد تولید شده توسط clang همانطور که در تصویر 17 قابل مشاهده است، در ابتدای تابع یک call به یک لیبل با عنوان LAB_000112EA داریم که از ابتدای آن اجرای بدنه اصلی تابع شروع می‌شود. تفاوت بعدی که وجود دارد، Calling Convention مورد استفاده توسط کامپایلر clang است که نسبت به msvc و gcc تفاوت دارد. به عنوان مثال، در تصویر 18 اصول عبور پارامترها به تابع printf قابل مشاهده است. در خروجی دیزاسمبلی clang به جای PUSH شدن پارامترها از راست به سمت چپ درون پشته، در خروجی clang از رجیسترها برای عبور پارامترها به توابع C استفاده می‌شود. تصویر 18: نحوه عبور پارامترها به تابع با توجه بررسی خروجی کامپایلرهای GCC و MSVC و Clang در پردازش Structها در زبان سی تفاوت برجسته ای وجود نداشت. هر سه کامپایلر برای مقداردهی اعضای استراکچر از آدرس پایه ای استفاده کرده بودند که توسط رجیستر EBP مورد ارجاع قرار می‌گرفت. دو کامپایلر GCC و MSVC از یک اصول تقریبا مشابه‌ای دنباله روی می کردند، ولی clang به جای استفاده از Stack از رجیسترهای عمومی x86 برای عبور پارامترها به توابع استفاده می‌کرد. پایان.
  5. در تصویر 7، خروجی کپی مقادیر CC به درون آدرسی که توسط EDI مورد ارجاع قرار گرفته است، قابل نمایش است. تصویر 7: کپی مقدار CC به درون آدرس مورد ارجاع توسط EDI دستوراتی که در ادامه آورده شده‌اند، مربوط به فلگ GS کامپایلر هستند که مفهوم قناری در پشته را به درون طرح پشته اضافه می‌کند. ابتدا یک مقدار شبه رندوم که در ثابت __security_cookie قرار دارد توسط کامایلر بازیابی شده و در رجیستر EAX ذخیره می‌شود. سپس بر روی مقدار درون رجیستر EAX با مقدار درون EBP یک عملیات XOR انجام می‌شود و در نهایت، خروجی عملیات XOR در آدرس [ebp-4] ذخیره می‌شود. مقدار نهایی که در این آدرس قرار می‌گیرد، به منظور بررسی عدم تخریب حافظه Stack مورد بررسی قرار خواهد گرفت. در نهایت تابع CheckForDebuggerJustMyCode فراخوانی شده است که به دلیل فعال سازی فلگ JMC به کد اضافه می‌شود. در تصویر 8، خروجی دیزاسمبلی این تابع در حالت Release درون IDA Pro نمایش داده شده است. همانطور که قابل مشاهده است، بخش زیادی از کدهای دیزاسمبلی که در حالت Debug قابل مشاهده است، هنگامیکه باینری در مُد Release تولید می‌شود، آن اطلاعات اضافی وجود ندارد. تصویر 8: خروجی دیزاسمبلی باینری مُد Release درون IDA Pro با این حال، همانطور که در تصویر 9 نمایش داده شده است، بعد از اینکه ما یک Struct تعریف می‌کنیم، و اولین ممبر آن را مقداردهی می‌کنیم، دستور mov dword ptr آورده شده است. هنگامیکه در خروجی لیست دیزاسمبلی با dword رو به رو می‌شویم، به این معنا است که با یک متغیر 32 بیتی رو به رو هستیم. از آنجایی که اولین عضو Struct ما از نوع int بوده است، در اینجا عمل mov بر روی یک آدرس 32 بیتی صورت گرفته است. ولی در ادامه به جای o_structure در سطح اسمبلی کامپایلر از رجیستر ebp به منظور دسترسی به اعضای استراکچر Disassembly استفاده کرده است. مثلا در گام دوم، برای کپی مقدار اسکی 14h به درون عضو m_char از رجیستر ebp به همراه آفستی که آن عضو درون پشته قرار دارد، استفاده کرده است تا مقدار 14h را به درون آن منتقل کند. در ادامه همین مسئله مجدد رخ داده است و تنها اندازه آفست دستورالعمل mov تغییر کرده است. همچنین در نهایت به دلیل اینکه یک مقدار Floating-Point مورد استفاده قرار گرفته است، در سطح اسمبلی شاهد استفاده از رجیسترهای XMM به همراه دستور movss هستیم. هنگامیکه در معماری x86 با یک نوع داده float رو به رو هستیم، از دستورالعمل movss و dword ptr استفاده شده است، اما هنگامیکه با یک نوع داده double رو به رو هستیم، از دستورالعمل movsd و mmword ptr استفاده شده است. تصویر 9: مقداردهی اعضای Struct در زبان C در ادامه آرایه کاراکتری که درون Struct تعریف شده است، با استفاده از تابع strncpy مقداردهی شده است. همانطور که در تصویر 10 قابل مشاهده است، دستور mov esi, esp مقدار جاری پشته را در ثبات esi قرار می‌دهد. این دستور معمولاً برای ذخیره کردن مقدار پشته در یک ثبات استفاده می‌شود. در ادامه دستورالعمل push offset string "Milad" آدرس رشته "Milad" را در پشته قرار می‌دهد. این دستور برای پاس دادن آرگومان‌ها به توابع در Calling Convention زبان C استفاده می‌شود. در ادامه مجدد یک دستور push آورده شده است که این دستور مقدار 400 به صورت عددی را در پشته قرار می‌دهد. دستور lea eax, [ebp-40Ch] آدرس ebp-40C را در eax قرار می‌دهد. دستور lea برای محاسبه آدرس یک متغیر استفاده می‌شود. سپس مجدد مقدار درون این رجیستری با استفاده از دستور push eax در پشته قرار می‌دهد. در نهایت وقتی پارامترهای مورد نیاز strcpy_s به درون پشته کپی شد، این تابع فراخوانی می شود. تابع strcpy_s برای کپی کردن یک رشته به رشته دیگر استفاده می‌شود. دستورالعمل بعدی که فراخوانی شده است add esp, 0Ch مقدار 0Ch (12 در مبنای 16) را به esp اضافه می‌کند. این دستور برای تعیین موقعیت صحیح پشته پس از فراخوانی تابع استفاده می‌شود. در نهایت با فراخوانی دستورالعمل مقدار درون رجیستر esi و esp با دستورالعمل cmp مورد مقایسه قرار خواهند گرفت. این دستور برای بررسی صحت موقعیت پشته استفاده می‌شود. دستورالعمل نهایی که به دلیل فلگ RTC1 به باینری اضافه شده است، تابع __RTC_CheckEsp را فراخوانی می‌کند. این تابع برای بررسی صحت موقعیت پشته استفاده می‌شود. در کل، این کد اسمبلی یک رشته به نام "Milad" را با استفاده از تابع strcpy_s کپی می‌کند و سپس موقعیت پشته را بررسی می‌کند. در تصویر 10، خروجی همین ساختار را در IDA Pro قابل مشاهده است: تصویر 10: خروجی دیزاسمبلی باینری در مُد Release یکی از مهم‌ترین تفاوت‌هایی که در خروجی تولید شده توسط IDA Pro نسبت به خروجی تصویر 9 وجود دارد، مقداردهی تمامی اعضای استراکچر به صورت یکجا است. متغیرها با استفاده از آدرس پایه که توسط رجیستر esp مورد ارجاع قرار گرفته است، و آفست‌هایی که در تصویر 10 قابل مشاهده است، تمامی اعضای استراکچر مقداردهی اولیه شده‌اند. شایان ذکر است، قبل از اینکه تابع strcpy_s فراخوانی شود، پارامترهای آن درون پشته PUSH شده است. شایان ذکر است، دستور and esp, 0FFFFFFC0h اشاره‌گر به پشته یا ESP را تراز می‌کند و دستور sub esp, 440h فضایی برای متغیرهای محلی رزرو می‌کند. این مراحل تضمین می‌کنند که مدیریت صحیح پشته انجام شده و محیط یکنواختی برای اجرای تابع فراهم می‌کنند. سپس در ادامه تمامی این مقادیر با عبور به تابع printf نتیجه آن‌ها برای کاربر نمایش داده خواهند شد. تصویر 11: عبور مقادیر اعضای استراکچر به تابع printf
  6. مقدمه‌ای بر فهمایش دیزاسمبلی در این تحقیق تلاش بنده بر این بوده است که ساختار بلاک‌های اسمبلی که کامپایلر MSVC و GNU و Clang برای استراکچرها زبان C در پلتفرم x86 تولید می‌کنند، مورد بررسی قرار بدهم. شایان ذکر است، خروجی MSVC با استفاده IDA Pro در محیط ویندوز و خروجی GCC و Clang در محیط لینوکس با استفاده از Ghidra مورد بررسی قرار گرفته‌اند. استراکچری که دیزاسمبلی آن مورد بررسی قرار گرفته است، به شرح زیر است: تصویر 1: محتوای استراکچر مورد بررسی در تصویر 1، محتوای استراکچر نمایش داده شده است. این استراکچر شامل 6 عضو از انواع داده در زبان C می‌شود. در تصویر 2، نحوه استفاده از این استراکچر نمایش داده شده است. از قبیل اینکه چگونه اعضای این استراکچر که دارای نوع داده اشاره‌گر به کاراکتر هستند، مقداردهی و مورد فراخوانی قرار گرفته‌اند: تصویر 2: نحوه استفاده از استراکچر در زبان C بعد از اینکه برنامه بالا نوشته شد، برنامه بالا را با فلگ‌های زیر توسط MSVC کامپایل کردم. در جدول 1 فلگ‌های پیش‌فرض که در MSVC تنظیم شده بود، آورده شده است: Debug Configuration Compiler Flags: /JMC /permissive- /ifcOutput "x64\Debug\" /GS /W3 /Zc:wchar_t /ZI /Gm- /Od /sdl /Fd"x64\Debug\vc143.pdb" /Zc:inline /fp:precise /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /RTC1 /Gd /MDd /FC /Fa"x64\Debug\" /EHsc /nologo /Fo"x64\Debug\" /Fp"x64\Debug\Structs.pch" /diagnostics:column Release Configuration Compiler Flags: /permissive- /ifcOutput "x64\Release\" /GS /GL /W3 /Gy /Zc:wchar_t /Zi /Gm- /O2 /sdl /Fd"x64\Release\vc143.pdb" /Zc:inline /fp:precise /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /Gd /Oi /MD /FC /Fa"x64\Release\" /EHsc /nologo /Fo"x64\Release\" /Fp"x64\Release\Structs.pch" /diagnostics:column جدول 1: فلگ‌های MSVC برای کامپایل برنامه برخی از فلگ‌هایی که در مُد Debug و Release برای Visual Studio تعریف شده‌اند، در ادامه مورد تشریح قرار گرفته‌اند. این فلگ ها به صورت پیش فرض در مُدهای مختلف Visual Studio فعال هستند، با این حال فلگ‌های دیگر که به صورت سفارشی به منظور تاثیر آن‌ها بر روی خروجی دیزاسمبلی مورد بررسی قرار خواهند گرفت، به صورت مجزا تشریح خواهند شد: فلگ‌های فعال در مُد دیباگ: فلگ JMC: فلگ Just My Code برای ساده‌تر کردن دیباگ برنامه‌ها طراحی شده است و با تمرکز بر روی کد خود کاربر، کدهای مرتبط با سیستم و فریمورک را مخفی می‌کند. وقتی این ویژگی فعال است، پروسه دیباگ می‌تواند از کد غیرکاربری، مانند کتابخانه‌های سیستم یا فریمورک‌های شخص ثالث عبور کند و این امر باعث می‌شود که مراحل پیمایش و درک جریان کد آسان‌تر شود. به عنوان مثال، هنگامیکه در حال دیباگ برنامه با استفاده از رویکرد Step-In هستیم، وارد توابع سیستمی و کتابخانه‌های جانبی نخواهیم شد. این امر در نهایت موجب می‌شود، رویکرد دیباگ محدود به کدهایی شود که خود برنامه‌نویس نوشته است. فلگ Permissive: فلگ Permissive به منظور حفظ مطابقت با قواعد نحوی و استانداردهای برنامه‌نویسی CPP است. وقتی این فلگ را تنظیم می‌کنید، کامپایلر اصول Compatibility را نادیده خواهد گرفت، و تلاش بر این خواهد شد که طبق استانداردهای جاری مطابقت نحوی (Syntax Conformance) و مطابقت معنایی (Semantic Conformance) برنامه را مورد بررسی قرار بدهد. به عنوان مثال، این گزینه اگر فعال باشد امکان استفاده از توابعی مانند strcpy به ما داده نخواهد شد، و شخص برنامه‌نویس باید از strcpy_s اضافه کند که نسخه ایمن‌سازی شده تابع مذکور است. فلگ GS: فلگ GS به عنوان یک ویژگی امنیتی برای شناسایی برخی از آسیب‌پذیری‌های سرریز بافر استفاده می‌شود. وقتی فلگ GS فعال است، کامپایلر MSVC کد اضافی را به اجرایی تولید شده اضافه می‌کند تا در زمان اجرا بررسی‌هایی روی برخی از عملیات‌های بافر انجام دهد. فلگ GS به حفاظت در برابر سرریز بافرهای مبتنی بر Stack کمک می‌کند. این با افزودن یک کوکی امنیتی به نام Security Cookie به فریم Stack توابعی که از بافرهای لوکال استفاده می‌کنند، عمل می‌کند. این کوکی امنیتی به عنوان یک مقدار نگهبان عمل می‌کند و قبل از بازگشت تابع بررسی می‌شود تا اطمینان حاصل شود که فریم Stack دچار خرابی نشده است. این استفاده از فلگ GS در MSVC باعث ارائه یک لایه اضافی از امنیت برای شناسایی و کاهش آسیب‌پذیری‌های سرریز بافر می‌شود. فلگ ZI: فلگ Zi امکان تولید اطلاعات دیباگینگ را فراهم می‌کند. هنگام استفاده از این فلگ، کامپایلر اطلاعات اضافی را در فایل اجرایی قرار می‌دهد که پروسه دیباگ را بهبود می‌بخشد. فلگ Zi به کامپایلر دستور می‌دهد تا یک فایل PDB تولید کند که شامل سمبول‎ها و اطلاعات دیباگینگ درباره کد منبع است. این اطلاعات شامل نام‌های تابع، نام‌های متغیر، شماره خطوط و سایر جزئیاتی است که برای دیباگ و تجزیه و تحلیل برنامه در زمان اجرا مفید است. فایل PDB که با استفاده از فلگ Zi تولید می‌شود، می‌تواند توسط ابزارهای دیباگینگ مانند Visual Studio Debugger استفاده شود تا دیباگ ساده‌تری را فراهم کند. هنگامیکه یک باینری را در IDA Pro دیباگ می کنیم، اگر فایل PDB آن را به دیباگر بدهیم، خروجی با معناتری را به ما ارائه می‌دهد که صرفا آدرس‌های خالی نیستند. فلگ Od: این فلگ برای غیرفعال‌سازی بهینه‌سازی استفاده می‌شود و به کامپایلر دستور می‌دهد که در طول فرآیند کامپایل، تمام بهینه‌سازی‌ها را غیرفعال کند. زمانی که از فلگ Od استفاده می‌شود، کامپایلر کد اسمبلی بدون بهینه‌سازی تولید می‌کند که به کد منبع اصلی نزدیک است. این مورد در برخی حالت‌ها مفید است، مانند زمانی که برنامه خود را در حالت دیباگ قرار می‌دهید و می‌خواهید کد تولید شده به صورت دقیق با کد منبع اصلی همخوانی داشته باشد تا دیباگ و پیمایش کد آسان‌تر باشد. غیرفعال‌سازی بهینه‌سازی با استفاده از فلگ Od منجر به اجرای کد با سرعت کمتر نسبت به کدهای بهینه‌سازی شده با سایر فلگ‌های بهینه‌سازی مانند O1، O2 یا Ox شود. با این حال، برای اهداف دیباگ می‌تواند مفید باشد زیرا کد تولید شده آسان‌تر قابل فهم است. شایان ذکر است، به طور پیش‌فرض، زمانی که کد منبع را بدون مشخص کردن هیچ فلگ بهینه‌سازی کامپایل می‌کنید، کامپایلر فرض می‌کند Od استفاده شده و بهینه‌سازی را غیرفعال می‌کند. اگر می‌خواهید بهینه‌سازی را فعال کنید، باید یکی از فلگ‌های بهینه‌سازی مانند O1، O2 یا Ox را مشخص کنید تا سطوح مختلف بهینه‌سازی را فعال کنید. فلگ Zc:inline: فلگ Zc برای کامپایلر توابع inline را بر اساس استاندارد زبان CPP فعال یا غیرفعال می‌کند. در زبان CPP، کلیدواژه "inline" برای نشان دادن به کامپایلر استفاده می‌شود که یک تابع باید در محل فراخوانی به صورت inline گسترش یابد، به جای اینکه به صورت یک تابع جداگانه فراخوانی شود. هنگامیکه این فلگ را استفاده می کنیم، تمامی توابعی که به صورت inline تعریف شده اند، در محل فراخوانی خود expand خواهند شد و پرشی به محلی دیگر از حافظه صورت نخواهد گرفت. در نتیجه به دلیل عدم استفاده از حافظه Stack و دستورات مرتبط با کار با پشته سرعت اجرای برنامه بهینه خواهد شد. اگر این فلگ در نظر گرفته نشده باشد، حتی با وجود واژه inline در امضا توابع، کامپایلر توابع مذکور را به صورت inline مورد پردازش قرار نخواهد داد. فلگ fp:precise: فلگ fp:precise در کامپایلر MSVC مدل اعداد ممیز شناور را کنترل می‌کند. به طور پیش فرض، MSVC از گزینه /fp:precise استفاده می‌کند که به معنای تولید کد توسط کامپایلری است که به طور دقیق از استاندارد IEEE 754 پیروی می‌کند. این گزینه کنترل دقیقی را برای حالت گردکردن فراهم می‌کند و تضمین می‌کند که نتایج میانی با دقت کامل محاسبه می‌شوند. فلگ /fp:precise به ویژه در مواردی که نیاز به رعایت دقیق استاندارد IEEE 754 وجود دارد، مانند برنامه‌های علمی یا محاسبات عددی که دقت و سازگاری بسیار مهم است، مفید است. این فلگ می‌تواند سازگاری و قابلیت حمل‌ونقل کدی که بر روی رفتار خاص اعداد ممیز شناور را بستگی دارد، حفظ کند. بنابراین، در مواردی که عملکرد اولویت اصلی است و رعایت دقیق استاندارد لازم نیست، می‌توانید از مدل‌های دیگر اعداد ممیز شناور ارائه شده توسط MSVC مانند /fp:fast یا /fp:strict استفاده کنید. فلگ Zc:forScope: این فلگ قوانین استاندارد CPP را برای متغیرهای حلقه for فعال می‌کند. به طور پیش فرض، MSVC از قوانین قدیمی استفاده می‌کند که متغیر حلقه for در خارج از محدوده حلقه قابل مشاهده است. این موضوع می‌تواند منجر به مشکلات و رفتارهای ناخواسته شود. زمانی که فلگ Zc:forScope فعال شود، قوانین MSVC برای متغیرهای حلقه for مطابق با استاندارد CPP اعمال می‌شود و قابلیت دید متغیر حلقه فقط در بدنه حلقه محدود می‌شود. فلگ RTC1: برای فعال کردن بررسی‌های خطا در زمان اجرا استفاده می‌شود. این فلگ مخفف "Run-Time Error Checks Level 1" می‌باشد. وقتی این فلگ فعال شود، کامپایلر کد اضافی را در برنامه قرار می‌دهد تا بررسی‌هایی در زمان اجرا برای انواع مختلف خطاها انجام دهد، مانند بررسی‌های سرریز بافر، متغیرهای نا‌مقداردهی شده و استفاده نادرست از اشاره‌گرها از جمله این خطاها در برنامه‌نویسی هستند. فعال کردن فلگ RTC1 می‌تواند به شناسایی و تشخیص خطاهای برنامه‌نویسی که ممکن است منجر به رفتار تعریف نشده یا خرابی در زمان اجرا شوند، کمک کند. این فلگ هزینه اضافی را به اجرای برنامه اضافه می‌کند، زیرا بررسی‌های زمان اجرا نیازمند کد و محاسبات اضافی هستند. با این حال، در مراحل توسعه و دیباگ، می‌تواند یک ابزار مفید باشد تا مشکلات احتمالی را در مراحل اولیه شناسایی و رفع کنید. فلگ Gd: این فلگ برای تعیین نحوه فراخوانی توابع C استفاده می‌شود. نحوه فراخوانی تعیین می‌کند که چگونه پارامترهای تابع منتقل می‌شوند و چگونه تابع با کد فراخواننده تعامل می‌کند. وقتی از پرچم /Gd استفاده می‌شود، نحوه فراخوانی پیش‌فرض را مشخص می‌کند که به عنوان نحوه فراخوانی cdecl شناخته می‌شود. در نحوه فراخوانی cdecl، پارامترهای تابع از راست به چپ روی Stack قرار می‌گیرند و فراخواننده مسئول پاکسازی Stack پس از بازگشت تابع است. این نحوه فراخوانی به طور معمول در برنامه‌نویسی به زبان C استفاده می‌شود. شایان ذکر است، به طور پیش‌فرض، MSVC از نحوه فراخوانی cdecl استفاده می‌کند، بنابراین استفاده از فلگ Gd ضروری نیست. با این حال، مشخص کردن صریح /Gd مطمئن می‌شود که نحوه فراخوانی پیش‌فرض استفاده می‌شود. فلگ MDd: فلگ MDd برای تنظیمات دیباگ و استفاده از کتابخانه‌های پیش‌فرض (CRT) استفاده می‌شود. این فلگ برای توسعه و دیباگ برنامه‌ها استفاده می‌شود و به شما امکان می‌دهد تا از امکانات دیباگ موجود در برنامه استفاده کنید. وقتی از فلگ MDd استفاده می‌کنید، برنامه شما با استفاده از کتابخانه‌های پیش‌فرض (CRT) که به صورت دیباگ شده هستند، کامپایل می‌شود. فلگ FC: فلگ FC برای تعیین نام فایل منبع در خروجی کامپایلر استفاده می‌شود. این فلگ با اضافه کردن مسیر کامل فایل منبع به خروجی کامپایلر، به تولید پیام‌های خطا و اخطار با اطلاعات بیشتر کمک می‌کند. هنگام استفاده از فلگ /FC، کامپایلر مسیر کامل فایل منبع را به همراه پیام خطا یا اخطار نمایش می‌دهد. این ویژگی مفید است زمانی که شما چندین فایل منبع در دایرکتوری‌های مختلف دارید، زیرا به شما امکان می‌دهد به سرعت مکان خطا یا اخطار را تشخیص دهید. فلگ FA: فلگ FA برای کنترل تولید کد اسمبلی در فرآیند کامپایل استفاده می‌شود. این فلگ به شما امکان می‌دهد نوع و فرمت کد اسمبلی تولید شده را مشخص کنید. فلگ FA چندین گزینه دارد که رفتار تولید کد اسمبلی را تعیین می‌کنند. فلگ FA در هنگام فرآیند کامپایل، یک فایل اسمبلی (.asm) همراه با فایل آبجکت (.obj) تولید می‌کند. فایل لیستینگ کد اسمبلی شامل کد اسمبلی تولید شده متناظر با کد منبع است. فلگ Fa این گزینه را به ما اجازه می‌دهد نام و مکان سفارشی برای فایل لیستینگ اسمبلی مشخص کنید. شایان ذکر است، فلگ FAs همزمان فایل لیستینگ کد اسمبلی (.asm) و فایل لیستینگ کد ماشین (.cod) را در هنگام کامپایل تولید می‌کند. فایل لیستینگ کد ماشین شامل نمایش شماره‌ای از دستورات ماشین به صورت هگزادسیمال است. فلگ EHsc: این فلگ برای تعیین مدل برخورد با اکسپشن‌ها در کد CPP استفاده می‌شود. این فلگ برای "Exception Handling (Standard C++)" استفاده می‌شود و براساس استاندارد CPP برخورد با اکسپشن‌ها را فعال می‌کند. به طور پیش فرض، MSVC از فلگ EHs استفاده می‌کند که برخورد با اکسپشن‌های CPP را فعال می‌کند اما از مشخص‌کردن نوع اکسپشن پشتیبانی نمی‌کند. هنگام استفاده از فلگ EHsc، رفتار EHs را توسعه می‌دهد تا شامل پشتیبانی از مشخص‌کردن نوع اکسپشن هم باشد. برخورد با اکسپشن یک مکانیزم در CPP است که به شما اجازه می‌دهد اکسپشن‌ها را در طول اجرای برنامه کنترل و انتقال دهید. این مکانیزم روش ساختاری برای مقابله با شرایط استثنایی یا خطاهایی که در زمان اجرا ممکن است رخ دهند، فراهم می‌کند. این فلگ به هر صورت شرایطی را فراهم می‌کند که کامپایلر به صورت خودکار بتواند شرایط خاص را با استفاده از بلاک‌های دستوری try-catch مبتنی بر استاندارد CPP مدیریت و کنترل کند. با این حال، فعال‌سازی این فلگ موجب می‌شود که مقداری بر روی کد Overhead آورده شود و Performance برنامه به خاطر کنترل‌های اضافی که کامپایلر به باینری اضافه می‌کند، کاهش پیدا کند. فلگ‌های فعال در مُد Release: فلگ GL: این فلگ برای فعال کردن Global Optimization استفاده می‌شود. این بهینه‌سازی به کامپایلر امکان می‌دهد تا عملکرد و سرعت اجرای برنامه را بهبود بخشیده و حجم کد تولید شده را کاهش دهد. با استفاده از پرچم GL، کامپایلر MSVC بهینه‌سازی‌هایی را اعمال می‌کند که بهبود عملکرد برنامه را در مقیاس سراسر برنامه فراهم می‌کند. این بهینه‌سازی‌ها می‌توانند شامل تغییرات در ساختار داده‌ها، بهینه‌سازی‌های ریاضی و منطقی، حذف Dead Code و ترتیب اجرای کدها باشند. با فعال کردن فلگ GL، می‌توانید عملکرد برنامه خود را بهبود بخشیده و زمان اجرا را کاهش دهید. همچنین، حجم کد تولید شده نیز کاهش می‌یابد که می‌تواند منجر به افزایش سرعت بارگذاری و اجرای برنامه شود. به طور معمول، برای استفاده کامل از این بهینه‌سازی، باید فلگ LTCG را در مرحله لینک فعال کنید. این بهینه‌سازی‌ها ممکن است باعث افزایش زمان کامپایل شود، اما عملکرد و سرعت اجرای برنامه را بهبود می‌بخشند. فلگ Gy: فلگ Gy برای فعال کردن Function Level Linking استفاده می‌شود. زمانی که در هنگام کامپایل از فلگ Gy استفاده می‌شود، کامپایلر را به تولید فایل‌های آبجکت جداگانه برای هر تابع در کد مجبور می‌کند. این امر به پیوند کارآمدتر و کاهش حجم فایل اجرایی منجر می‌شود. با فعال کردن Function Level Linking با فلگ Gy، لینکر قادر است توابع بی‌استفاده را از فایل اجرایی نهایی حذف کند و حجم کل باینری را کاهش دهد. این قابلیت به خصوص در پروژه‌های بزرگ که نه همه توابع استفاده می‌شوند و نه همه توابع فراخوانی می‌شوند، مفید است. فلگ Gm-: این فلگ به کامپایلر می گوید که تکنیم Incremental Compilation و همچنین استفاده از فایل های PCH را غیرفعال کند. این دو مورد موجب کامپایل برنامه با سرعت بالاتری می شوند اما در برخی شرایط غیرفعال کردن Incremental Compilation و ایجاد فایل .pch می‌تواند مفید باشد. این مورد مفید است زمانی که در پروژه‌های بزرگ کار می‌کنید و هزینه نگهداری و به‌روزرسانی فایل .pch بیشتر از فواید Incremental Compilation سریع است. در حالت کلی، فلگ‌هایی که در بالا آورده شده‌اند به صورت پیش‌فرض در مُدهای Debug و Release در Visual Studio تعریف شده‌اند. برنامه C که با رویکرد Structها نوشته شده است، در هر دو مُد مورد بررسی قرار گرفته است تا درک بهتری از ساختار باینری همچنین با فلگ‌های گوناگون کامپایلر به دست آورده شود. بررسی دیزاسمبلی x86 در ویندوز به منظور درک دیزاسمبلی ساختمان داده Struct در زبان برنامه نویسی C ابتدا خروجی تولید شده برای معماری x86 را در دو مُد Debug و Release مورد بررسی قرار خواهیم داد. بعد از بررسی خروجی دیزاسمبلی برای معماری x86، خروجی کامپایلر MSVC برای معماری x64 را مورد بررسی و ارزیابی قرار خواهیم داد. تصویر 3: کد منبع برنامه مورد بررسی همانطور که در جدول 1 آورده شده است، خروجی دیزاسمبلی کد منبع در تصویر 3 با فلگ‌های پیش‌فرض کامپایلر برای مُد Debug مورد بررسی قرار گرفته است. اولین نکته‌ای در تحلیل کد منبع نظر من را جلب کرد، نادیده گرفتن inline بودن تابع MyOperation بود. با اینکه این تابع را به صورت inline تعریف کرده بودم، و همچنین فلگ Zc:inline هم فعال بود، با این حال کامپایلر تصمیم گرفته است که این تابع را به صورت inline در محل فراخوانی خود گسترش ندهد. از همین روی در تابع main، یک دستور call داریم. وقتی این دستور فراخوانی می‌شود، آدرس 00C11A66 را در پشته قرار می‌دهد تا وقتی دستور ret فراخوانی شد، جریان اجرای باینری به مسیر صحیح خود بازگردد و ادامه اجرای برنامه را بعد از فراخوانی تابع ادامه بدهد. در تصویر 4، خروجی دیزاسمبلی اجرای این دستور در محیط Visual Studio نمایش داده شده است. وقتی دستور Call اجرا شده است، آدرس دستوربعد بر روی بالا پشته قرار گرفته است که با ارجاع به مقدار ESP-4 در پانل Memory می‌توان مقداری که در بالای پشته قرار گرفته است، مشاهده کرد که این مقدار با رنگ آبی نمایش داده شده است. تصویر 4: خروجی اجرای دستور Call در دیباگر Visual Studio در ادامه همین مسئله در دیزاسمبلر IDA Pro مورد بررسی قرار گرفته است. وقتی باینری مذکور را در دیزاسمبلر IDA Pro باز کنیم، و وارد دیباگر این دیزاسمبلر شویم، موقعی که دستور Call فراخوانی شود، آدرس دستور بعدی را بر روی پشته قرار می‌دهد که دستور Ret بتواند فرایند اجرای باینری را به مسیر صحیح خود بازگرداند. در پانل Stack View این باینری مقداری که درون پشته قرار گرفته است، قابل نمایش است. تصویر 5: خروجی اجرای دستور Call در دیباگر IDA Pro در تصویر 5، دیزاسمبلی باینری را در مُد Release درون دیباگر IDA Pro مشاهده می‌کنیم. همانطور که در تصویر 5 قابل مشاهده است، وقتی دستور Call اجرا شده است، آدرس بعدی خود را درون پشته قرار داده است که بعد از اجرای دستورالعمل ret اجرای باینری بتواند به مسیر اجرایی خود بازگردد. بعد از اینکه وارد تابع MyOpertion شویم، خروجی دیزاسمبلی تعریف و مقداردهی Struct در زبان C قابل مشاهده است. در تصویر 6، دیزاسمبلی تابع MyOperation نمایش داده شده است: تصویر 6: خروجی دیزاسمبلی تابع MyOperation سه دستور اول اصطلاحا Prologue تابع MyOperation هستند. این سه دستور موجب ایجاد یک فریم جدید و ایجاد فضا برای ذخیره‌سازی متغیرهای محلی تابع می‌شوند. دستور push ebp موجب ذخیره سازی فریم تابع قبلی بر روی پشته خواهد شد. دستور mov ebp, esp موجب شکل گیری یک فریم جدید برای تابع MyOperation خواهد شد. دستور sub esp, 66Ch موجب کم کردن مقدار 66C از پشته به منظور ذخیره سازی متغیرهای محلی درون تابع خواهد شد. به هر صورت، این دستورالعمل فضایی برای متغیرهای محلی و داده‌های دیگری که توسط تابع نیاز است، در پشته اختصاص می‌دهد. در ادامه سه دستور PUSH داریم که به نظر می آید به این دلیل انجام می‌شوند که قبل از استفاده از رجیسترهای ebx، esi و edi مقادیر قبلی آن‌ها بر روی پشته ذخیره شود تا در ادامه مقادیر آن‌ها قابل بازیابی بانشد. دستور بعدی آدرس مؤثر [ebp-42Ch] را محاسبه کرده و در رجیستر EDI ذخیره می‌کند. دستور بعدی مقدار 10Bh (هگزادسیمال) را به رجیستر ECX منتقل می‌کند. مقدار درون رجیستر ECX به عنوان میزان تکرار برای دستور rep stos است که در گام بعد آورده شده است. دستور rep stos مقداری که درون EAX قرار دارد، به آدرسی که توسط EDI اشاره می کند، کپی خواهد کرد.
  1. نمایش فعالیت های بیشتر
×
×
  • جدید...