<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های ابوالفضل کاظمی</title>
        <link>https://virgool.io/feed/@akazemi</link>
        <description></description>
        <language>fa</language>
        <pubDate>2026-07-01 08:03:36</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/159150/avatar/xpfnIT.png?height=120&amp;width=120</url>
            <title>ابوالفضل کاظمی</title>
            <link>https://virgool.io/@akazemi</link>
        </image>

                    <item>
                <title>مسیر من و Windows Internals و شروع حرکتی که مدت‌ها تو فکرم بود!</title>
                <link>https://virgool.io/@akazemi/%D9%85%D8%B3%DB%8C%D8%B1-%D9%85%D9%86-%D9%88-windows-internals-%D9%88-%D8%B4%D8%B1%D9%88%D8%B9-%D8%AD%D8%B1%DA%A9%D8%AA%DB%8C-%DA%A9%D9%87-%D9%85%D8%AF%D8%AA-%D9%87%D8%A7-%D8%AA%D9%88-%D9%81%DA%A9%D8%B1%D9%85-%D8%A8%D9%88%D8%AF-wxiyyuuszo3b</link>
                <description>اولین آشنایی من با کتاب فوق‌العاده خوب Windows Internals بر می‌گرده به درس سیستم‌عامل و استاد بی‌نظیری که خیلی چیزها ازش یاد گرفتم و تاثیری ماندگار روی من داشت. من سیستم‌عامل رو با دکتر علی داودیان (Ali Davoudian) داشتم. هر جلسه یک پروژه‌ی اختیاری بهمون می‌داد که به صورت عملی بتونیم با مطالبی که درس داده هم تجربه کسب کنیم و دست به آچار بشیم. منم که عاشق برنامه‌نویسی و تست عملی مفاهیم بودم همه‌ی پروژه‌هاش رو بجز آخریش که دستکاری بخشی از کد Minix بود و فرصت نشد برم سراغش، انجام دادم. یکی از چیزهایی که هر از گاهی توی درس بهش اشاره می‌کرد کتاب Windows Internals بود که البته اون موقع ویرایش ۴ام بود و بحث بیشتر حول ویژگی‌های ویندوز XP می‌چرخید. از همون موقع خیلی این کتاب رو دوست داشتم ولی برام سنگین بود و فقط جسته گریخته بخش‌هاییش رو خوندم و مابقی که میشه گفت ۹۰ درصد حجم کتاب رو شامل میشد رها شد. توی این سال‌ها و گذر کتاب از ویرایش ۴ و رسیدن تا ۷امین ویرایش و بجای یک کتاب بودن دو کتاب شدن، همیشه همراه کتاب بودم ولی باز هم جسته گریخته و نه متمرکز. هر از گاهی توی اوقات فراغت یک چرخی میزدم، با ابزارها ور می‌رفتم، یک چیزی یاد می‌گرفتم و لذت می‌بردم.چندبار تلاش کردم بشینم from cover to cover بخونمش ولی نمیشد. حتی دو، سه سال پیش با دوست و همکارم محمدرضا رمضانی (Mohammad Reza Ramezani) تلاش کردیم جلسات مرور کتاب رو داشته باشیم ولی اونم بعد از چند جلسه به خاطر مشغله کاری و کرونا رها شد. سال گذشته بالاخره تونستم تقریبا جلد اول رو بخونم یک بخش مسیر روی برم. (البته هنوز خیلی مونده تا بتونم ازش امتحان بدم!!!)یک چیزی که علی‌رغم عالی بودن کتاب برام کم بود این بود که از هر مبحثی علاوه بر بررسی مفاهیم با ابزارها، بشه نمونه کد‌های ساده‌ی مفهومی برای پیاده‌سازی اون بخش و درگیر برنامه نویسی شدن و مفهوم رو hands-on دیدن هم بزنم. یکم اینکار رو پیش بردم و تصمیم گرفتم شروع کنم به پر کردن ویدئو و انتشارشون به صورت آزاد، تا حداقل مسیر بقیه‌ی علاقه‌مندان هموارتر از من باشه. کار پر زحمت و زمان‌بری بود ولی تونستم از فصل نخ‌ها شروع کنم به انجام اینکار. شروع از فصل نخ‌ها هم به دو علت بود. اول اینکه بخش اجرایی کد هست و برنامه‌ای بدون نخ قابل اجرا نیست و این برام یک دلیل منطقی بود! (حداقل توی ویندوز اینطوریه!!) و دوم اینکه محمدرضا هم حرکت خوبی رو سال گذشته شروع کرد و از فصل اول کتاب، شروع کرد به شرح مفاهیم و استفاده از ابزارها. (سایت 7erom) اینطوری کار من و اون، با این امید که بتونیم هردومون این حرکت رو ادامه‌ داده و به سرانجام نیکی برسونیم، تکمیل کننده‌ی هم میشند.مواردی که من تا الان آماده کرده‌ام و این پست برای اطلاع رسانی شروع انتشارشون هست به شرح زیر هستند:1.   شروع کار با نخ‌ها توی Windows API به کمک CreateThread و دوستان2.   استفاده از Thread Pool توی WinAPI و کار با Callbackهای اون3.   آشنایی با Kernel Development توی ویندوز، نوشتن درایور ساده و تعریف Kernel Thread لطفا با لایک و انتشار در رسیدن این پست به دست علاقه‌مندان تازه‌کار و هموار کردن مسیرشون کمک کنید. در این حین امید دارم که دوستان با تجربه هم با نقطه نظراتشون و گوش‌زد کردن مشکلات و اشتباهات بتونند در بهتر شدن کیفیت ویدئوها بهم کمک کنند. خوشحال میشم از طریق لینکدین هم باهم در ارتباط باشیم و نقطه نظراتتون رو برام ارسال کنید.در نهایت برای مشاهده‌ی ویدئوها به لیست پخش زیر مراجعه کنید:لیست پخش دوره‌ی Windows Internals</description>
                <category>ابوالفضل کاظمی</category>
                <author>ابوالفضل کاظمی</author>
                <pubDate>Fri, 15 Apr 2022 10:19:21 +0430</pubDate>
            </item>
                    <item>
                <title>معرفی کتاب «عادت‌های اتمی»</title>
                <link>https://virgool.io/@akazemi/%D9%85%D8%B9%D8%B1%D9%81%DB%8C-%DA%A9%D8%AA%D8%A7%D8%A8-%D8%B9%D8%A7%D8%AF%D8%AA-%D9%87%D8%A7%DB%8C-%D8%A7%D8%AA%D9%85%DB%8C-tvdexbuqbcki</link>
                <description>فکر کنم برای همه‌ی ما تا حالا پیش اومده باشه که تصمیم جدی به انجام کاری گرفته و حتی نه از شنبه بلکه از فردای روز تصمیم کبری، شروع به کار هم کرده باشیم و مدتی پیش رفته ولی پس از اندکی طی طریق، کار مذکور را رها کرده و دوباره به چرخه‌ی عادی زندگی برگشته باشیم! علت رویدادهای این مدلی بیشتر از هر چیزی اینه که کاری که می‌خواهیم انجام بدیم برامون به صورت عادت در نیومده و باید از «تفکر کند به جای تفکر سریع» برای انجام کار بهره ببریم و انرژی زیادی صرف کنیم تا خودمون را برای انجام کار آماده کنیم. در زندگی افراد موفق هم که بررسی کنیم می‌بینیم که «این مردمان موثر دارای عادت‌هایی هستند» که به موفقیت و رشد آن‌ها کمک زیادی کرده است.کتابی که قصد معرفی آنرا دارم کمک می‌کنه که با ایجاد کردن «عادت‌های اتمی» یعنی کارهای کوچکی که دیگه قابل تقسیم به زیرکار نمی‌باشند، به مرور تغییرات کوچکی در زندگی خودمون ایجاد کنیم که با جمع شدن و ایجاد «اثر مرکب» در دراز مدت، تغییرات شگرفی را در زندگی خودمون مشاهده کنیم. یک داستان خیلی معروف بین چینی‌ها هست که از زندگی فردی میگه که با تمرین روزانه چند جمله انگلیسی، تونست بعد از چند سال از یک کارگر ساده بودن به وزیر امور خارجه تبدیل بشه، اینکه داستان صحت داره یا نه رو تحقیق نکرده‌ام ولی نکته‌ای که در دلش هست و به اثر مرکب عادات کوچک در زندگی می‌پردازه صحت داره. در کتاب «عادت‌های اتمی» هم داستان‌هایی از این دست که به تاثیر عادات در زندگی می‌پردازند زیاده که خواندن کتاب را لذت بخش‌تر می‌کنه. کتاب با ماجراهایی از زندگی نویسنده و مسیر رسیدن به نوشتن کتاب، شروع شده و پس از آن، در بخش اول به دلایل تاثیرات شگرفی می‌پردازه که تغییرات کوچک به همراه دارند. توی این بخش به این موضوع پرداخته میشه که بجای تمرکز بر روی اهداف، باید به سیستم بپردازیم و کمی کلی‌تر به قضیه نگاه کنیم. مثلا هدف یک مربی ورزش اینه که تیم رو به قهرمانی برسونه ولی سیستمی که داره، تشکیل میشه از جذب افراد مناسب و روش تمرین و مواردی از این دست. یک جمله‌ی کلیدی برای علت این تغییر دیدگاه اینه که «قهرمان شدن، هم هدف برنده‌هاست و هم هدف بازنده‌ها، ولی سیستم تمرین و روش آماده‌سازی این دو دسته متفاوته.» بعد از بحث و توضیح در مورد تفاوت‌های هدف و سیستم، کتاب «سیستم عادت‌های اتمی» رو معرفی می‌کنه و میگه که اگر شما در تغییر عادات خود مشکل دارید، مشکل از شما نیست، مشکل از سیستم شماست و «شما به اندازه‌ی اهداف خود بالا نمی‌روید، شما به اندازه‌ی سطح سیستمی که دارید، پایین می‌آیید!»کتاب برای شرح نحوه‌ی شکل‌دهی عادت‌های اتمی و ماندگاری عادات، به بررسی پروسه‌ی چهار مرحله‌ای عادت می‌پردازه که توضیح و تفسیر هر کدام از این مراحل با ذکر داستان‌های متنوع، و استفاده از این مراحل برای «سیستم عادت‌های اتمی» بخش‌های بعدی کتاب رو تشکیل می‌دهند. این چهار مرحله به ترتیب به این صورت هستند: cue یا نشانه‌ای برای شروع عادت، craving یا محرک انگیزشی که پشت عادت قرار داره، response‌ یا عادتی که انجام می‌دیم و در نهایت reward یا هدف نهایی عادت و چیزی که از عادت دنبال می‌کنیم. پس از اینکه دونستیم تمامی عادات چه مثبت و چه منفی از این چهار مرحله تشکیل شده‌اند، شکل دهی یک عادت مثبت و از بین بردن یک عادت منفی راحت‌تر میشه.برای شکل‌دهی یک عادت مثبت کافیه ابتدا به شیوه‌ای عمل کنیم که کار مورد نظر به صورت واضح جلوی چشممون قرار گرفته و نشانه‌هایی از اون را داشته باشیم تا انجام کار فراموش نشه. مثلا کار را بچسبانیم به مکان و زمان خاص یا یک کاری که عادت جاری‌مون هست را پیش نیازش قرار بدیم یا اینکه محیط را به گونه‌ای تنظیم کنیم که نشانه‌هایی برای ایجاد عادت مناسب در بر داشته و از نشانه‌های عادت‌های منفی خالی باشه. یک نمونه که کتاب به آن اشاره داره در مورد تحقیقی است که برای شکل دهی عادت ورزش کردن بر روی ۲۴۸ نفر در طی دو هفته انجام شده. این افراد به سه گروه تقسیم شده بودند، از گروه اول فقط خواسته شده بود که زمان‌هایی که ورزش می‌کردند را ثبت کنند. از گروه دوم خواسته شده بود که علاوه بر ثبت زمان‌ها، منابعی که در مورد فواید ورزش است را مطالعه کنند و از گروه سوم خواسته شده بود که علاوه بر کارهای گروه دوم، یک زمان و مکان ورزش نیز برای خود تعیین کنند. نتیجه‌ی نهایی این بود که از بین دو گروه اول حدود ۳۵ تا ۳۸ درصد واقعا ورزش کرده بودند ولی از بین گروه سوم بیش از ۹۰درصد افراد به ورزش پرداخته بودند!پس از واضح شدن کار، مرحله‌ی بعدی و بخش بعدی کتاب می‌پردازه به اینکه چرا و چگونه کار مورد نظر را جذاب کنیم که تمایل به انجام آن داشته باشیم. در این بخش کتاب، اشاره‌ای به داستان خواهران Polgar هم میشه و اینکه چطور پدر/مادرشان با جذاب کردن بازی شطرنج تونستند سه دختر که در سنین پایین به استاد بزرگی شطرنج رسیدند را تربیت کنند.هر کاری اگرچه واضح و لذت بخش، اگر قابلیت انجام راحت و ساده و با تلاش کم را نداشته باشه استقبال کمی را در پی خواهد داشت. به همین دلیل بخش بعدی کتاب به موضوع سهل الوصول بودن کارها می‌پردازه و به حرکت در مسیر، به صورت «آهسته و پیوسته» اشاره داره. این بخش با داستانی از یک تجربه‌ی دانشگاهی شروع میشه. یک استاد دانشگاه در درسی برای تمرین عکاسی، دانشجوها را به دو گروه تقسیم می‌کنه. گروه اول باید بر روی «کمیت» تمرکز کرده و تعداد عکس زیادی در طول ترم بگیرند، در نهایت از بین عکس‌های گرفته شده ۱۰۰عکس نمره‌ی A و ۹۰ عکس نمره‌ی B و ۸۰ عکس نمره‌ی C گرفته و به همین صورت عکس‌ها و کار دانشجویان نمره‌دهی می‌شوند. گروه دوم کلاس باید بر روی «کیفیت» تمرکز کرده و هر دانشجو در طول ترم تنها یک عکس با کیفیت بالا گرفته و نمره‌ی دانشجو از روی همان یک عکس تعیین می‌شود. نتیجه‌ی نهایی در پایان ترم جالب بوده و بهترین عکس‌ها از بین گروهی از دانشجویان که بر روی «کمیت» تمرکز داشتند بدست آمده. علت این رویداد این بود که دانشجویانی که «به راحتی» و بدون درگیر شدن با این موضوع که ایده‌آل چیست به عکاسی در تعداد بالا پرداخته بودند، مهارت بیشتری نیز در عکاسی کسب کرده بودند.بخش بعدی که به آخرین مرحله و نیازمندی در ایجاد عادت‌های پایدار می‌پردازه، موضوع رضایت بخش بودن عمل را شرح می‌ده. اگر تمامی مراحل قبلی با موفقیت انجام بشه ولی از کاری که انجام داده‌ایم احساس خوشحالی و رضایت نداشته و از کرده‌ی خود پشیمان باشیم، تکرار کردن عمل برامون راحت نخواهد بود. مثال‌هایی که این بخش باهاش شروع میشه خیلی ساده ولی جالب هستند، مثل این موضوع که استفاده از مایع دستشویی فومی یا خمیردندان طعم دار که احساس رضایت ایجاد می‌کنند باعث رشد چشم‌گیر استفاده از این محصولات و شکل‌گیری عادت شستن دست و مسواک زدن شده است.بخش آخر کتاب هم به قول نویسنده به موضوعات پیشرفته‌تر مثل تاثیر و اهمیت استعداد، حفظ انگیزه در شغل و زندگی، نیمه‌ی تاریک ایجاد عادات خوب و در نهایت جمع بندی و رمز و راز «نتایجی که باقی می‌مانند» می‌پردازه.امیدوارم این خلاصه آشنایی خوبی با این کتاب خوب براتون ایجاد کرده باشه و با خواندن کتاب و ایجاد عادت‌های اتمی بتونید کیفیت زندگی و شغل خودتون را افزایش بدید.</description>
                <category>ابوالفضل کاظمی</category>
                <author>ابوالفضل کاظمی</author>
                <pubDate>Sat, 26 Jun 2021 07:36:21 +0430</pubDate>
            </item>
                    <item>
                <title>آشنایی با MBR و ساختار پارتیشن‌ها</title>
                <link>https://virgool.io/@akazemi/%D8%A2%D8%B4%D9%86%D8%A7%DB%8C%DB%8C-%D8%A8%D8%A7-mbr-%D9%88-%D8%B3%D8%A7%D8%AE%D8%AA%D8%A7%D8%B1-%D9%BE%D8%A7%D8%B1%D8%AA%DB%8C%D8%B4%D9%86%D9%87%D8%A7-e3yw4ogv7rjo</link>
                <description>آیا تا به حال این سوال برایتان پیش آمده است که حداکثر تعداد پارتیشن بر روی یک هارد چه تعداد است؟ یا اینکه بعد از روشن کردن کامپیوتر چه اتفاقی افتاده و از کجا مشخص می‌شود که از روی کدام پارتیشن سیستم‌عامل بارگذاری شده و شروع به اجرا شدن می‌کند؟ آیا به این نکته توجه نموده‌اید که کوچکترین واحد ذخیره‌سازی اطلاعات بر روی دیسک چقدر است؟ آیا این مقدار برابر یک بایت بوده و یا مقدار بیشتری دارد؟ (شاید مقدار کوچک‌ترین واحد ذخیره‌سازی اطلاعات به نظر بی اهمیت برسد ولی در سرعت خواندن/نوشتن و میزان فضای هرز ایجاد شده بر روی دیسک تاثیر زیادی دارد) در این مقاله قصد داریم به این سوالات و سوالات دیگری پیرامون این موضوع پاسخ گوییم.مشاهده‌ی ویدئو مرتبط با مقاله: چند جلسه‌ای فرصت ارائه‌ی مطالبی در کلاس آزمایشگاه سیستم‌عامل دانشگاه علم‌وصنعت که توسط دکتر فردباستانی ارائه میشد را داشتم و یکی از این جلسات به شرح روال بوت شدن لینوکش و ساختار MBR گذشت که مرتبط با مطالب این مقاله است. اگر علاقه داشتید می‌تونید فیلم این جلسه را در آپارات مشاهده کنید.برگردیم به ادامه‌ی مقاله! داده‌های ذخیره شده بر روی هارددیسک در بلوک‌هایی به اندازه‌ی ۵۱۲بایت ذخیره می‌شوند که به این بلوک‌ها sector گفته می‌شود. این مقدار، کوچکترین میزان فضایی است که داده‌ای بر روی هارددیسک اشغال می‌کند. یعنی اگر شما حتی یک حرف یک بایتی مثل A‌ را ذخیره کنید، مقدار فضایی که اشغال کرده‌اید برابر با ۵۱۲بایت می‌باشد. البته قضیه به اینجا ختم نمی‌شود و مقدار دقیق فضای اشغال شده ۵۱۲بایت نیست. علت این امر این است که شما به صورت مستقیم و با استفاده از interrupt داده‌ای بر روی دیسک ذخیره نکرده و از سیستم‌عامل (در واقع با استفاده از SystemCall‌های مربوط به کار با سیستم‌فایل) کمک گرفته و از لایه‌ی File System عبور کرده و سپس داده را بر روی دیسک و در یک کلاستر ذخیره می‌کنید. هر کلاستر مجموعه‌ای از چند سکتور بوده و کوچکترین فضای ذخیره‌سازی است که توسط سیستم‌فایل ارائه می‌شود. این موضوع باعث می‌شود که مقدار فضای اشغال شده از ۵۱۲بایت بیشتر شده و مضربی از ۵۱۲بایت باشد. در بسیاری از سیستم‌های فایل امروزی مثل NTFS, Ext4 مقدار پیش‌فرض اندازه‌ی کلاستر برابر 4KB می‌باشد. البته به این نکته هم دقت کنید که این مقدار برای پارتیشن‌ها با اندازه‌های مختلف می‌تواند متفاوت بوده و حتی شما می‌توانید در زمان Format کردن یک پارتیشن مقدار آنرا خودتان مشخص کنید. اولین sector هارددیسک (اولین 512bytes اشغال شده) که مهمترین سکتور نیز است، با عنوان (Master Boot Record - MBR) شناخته می‌شود که پس از روشن شدن کامپیوتر و بارگذاری BIOS خوانده شده و اطلاعاتی دارد که برای پیدا کردن سیستم‌عامل، بارگذاری آن و دسترسی به اطلاعات پارتیشن‌ها مورد نیاز است و در صورتیکه این سکتور خراب شده و یا قابل دسترسی نباشد، با خطایی مواجه شده و امکان شروع به کار سیستم‌عامل وجود نخواهد داشت. در این ۵۱۲بایت، مقدار ۶۴بایت برای ذخیره‌ی اطلاعات پارتیشن‌ها در نظر گرفته شده است که با استفاده از آن امکان تعریف ۴ پارتیشن وجود داشته و برای هر پارتیشن ۱۶ بایت اطلاعات ذخیره می‌شود. پس با توضیحات ارائه شده مشخص می‌شود که بر روی هر دیسک ۴ پارتیشن بیشتر نمی‌توان ایجاد نمود. (مگر در حالت استفاده از درایو منطقی که در ادامه در مورد آن صحبت خواهد شد) از این ۴ پارتیشن یکی می‌تواند به عنوان پارتیشن Extended معرفی شده (و یا اصلا وجود نداشته باشد) و مابقی، پارتیشن‌های Primary می‌باشند. در MBR کد مربوط به پیدا کردن سیستم‌عامل و بارگذاری BootLoader آن در ۴۴۶ بایت ابتدایی سکتور نوشته شده و از Offset شماره ۴۴۶ مقدار ۶۴ بایت اطلاعات پارتیشن‌ها را در بر می‌گیرد. دوبایت آخر MBR نیز (510==64+446) همیشه مقدار 0xAA55 را در بر می‌گیرند.نکته: این ساختار در کامپیوترهایی وجود دارد که از BIOS و MBR استفاده می‌کنند و در کامپیوترهایی که UEFI و GPT دارند وجود ندارد.در شکل ۱ نحوه‌ی استخراج داده‌ی MBR‌ و نمایش آن به صورت Hex در لینوکس، قابل مشاهده است. دستور dd امکان کپی‌برداری از اطلاعات دیسک را فراهم می‌کند و همانطور که در شکل ۱ نمایش داده شده است مقدار ۵۱۲بایت از ابتدای دیسک اول (sda) کپی شده و در فایلی به نام mbr.img ذخیره می‌شود. (نام دیسک‌های sata, scsi در لینوکس با sd شروع شده و پس از آن، اولین دیسک sda، دومی sdb و... نام‌گذاری می‌شوند)دستور xxd نیز برای hex dump استفاده می‌شود که بایت‌های یک فایل را در مبنای ۱۶ نمایش می‌دهد. در سمت چپ Offset هرخط، در وسط مقدار در مبنای ۱۶ و در سمت راست مقدار ASCII اطلاعات نمایش داده شده است. در اطلاعات نمایش داده شده عبارت GRUB‌ مشاهده می‌شود که از آن می‌توان برداشت کرد که این MBR مربوط به سیستمی لینوکسی می‌باشد! در خط آخر (از Offset شماره 1f4==500 تا 1ff==511) و در مکان دو بایت آخر (یعنی در مکان‌های 510 و 511) مقدار 0xAA55 مشاهده می‌شود. (این مقدار که Signature مربوط به MBR است را من به صورت Little-Endian نوشتم که بایت با ارزش کمتر در Offset کمتر قرار می‌گیرد) در صورت عدم وجود این مقدار در انتهای MBR خطای Missing Operating System داده خواهد شد.شکل ۱) استخراج MBR و نمایش آن در لینوکسهمانطور که در بخش قبلی به آن اشاره شد، از Offset شماره ۴۴۶ اطلاعات پارتیشن‌ها ذخیره می‌شود که به ۴ بخش ۱۶ بایتی تقسیم می‌شوند. از جمله اطلاعاتی که در ۱۶بایت مربوط به هر پارتیشن قرار می‌گیرد و برای ما اهمیت دارد موارد زیر می‌باشند:بایت اول (شماره‌ی صفر) Active بودن پارتیشن را مشخص کرده و می‌تواند دو مقدار 0x80 و 0 را داشته باشد. مقدار 0x80 که تنها به پارتیشن‌های Primary داده می‌شود، بیانگر پارتیشنی است که سیستم‌عامل بر روی آن نصب شده و باید Boot Loader سیستم‌عامل از روی آن بارگذاری شود. این مقدار تنها برای یک پارتیشن باید ست شده و در غیر این صورت در زمان بارگذاری سیستم‌عامل با خطای Invalid Boot Partition مواجه می‌شویم.بایت شماره‌ی ۴ نوع پارتیشن را مشخص می‌کند. به عنوان مثال مقدار 0 برای خالی بودن، 0x05 برای Extended و 0x07 برای NTFS و 0x83 برای Linux‌ مورد استفاده قرار می‌گیرند.بایت‌های شماره‌ی ۸ تا ۱۱ برای (Logical Block Addressing - LBA) مورد استفاده قرار می‌گیرند. (چهاربایت که اولین بایت، کمترین ارزش و آخرین بایت، بیشترین ارزش را دارد) از این مقدار برای Offset شروع پارتیشن استفاده می‌شود و تعداد سکتوری (دقت شود که سکتور و نه بایت) را مشخص می‌کند که از ابتدای دیسک باید رد کنیم تا به ابتدای پارتیشن برسیم.بایت‌های شماره‌ی ۱۲ تا ۱۵ نیز اندازه‌ی پارتیشن به تعداد سکتور را مشخص می‌کنند.در شکل ۲ بخش‌های توضیح داده شده و مابقی بخش‌های تشکیل دهنده‌ی MBR نمایش داده شده است.شکل ۲) ساختار پارتیشن‌ها در MBRبرای درک دقیق‌تر موضوع بیایید از شکل ۱ کمک گرفته و سکتور شروع و اندازه‌ی پارتیشن اول را محاسبه کرده و سپس با خروجی دستور fdisk مقایسه کنیم. طبق توضیحات ارائه شده، سکتور شروع از روی مقدار LBA‌ محاسبه می‌شود که ۸ بایت از ابتدای اطلاعات پارتیشن جلوتر بوده و طول آن ۴ بایت است. همچنین شروع اولین پارتیشن از Offset با مقدار ۴۴۶ می‌باشد که این نتیجه را می‌رساند که باید 8+446==1C6 بایت از ابتدای MBR به جلو رفته و از آنجا 4 بایت را برای محاسبه‌ی مقدار LBA‌ برداریم. با دقت در مقدار Offset نمایش داده شده در شکل ۱ متوجه می‌شویم که اطلاعات مورد نیاز ما در خط سوم از پایین قرار دارد. این خط دارای Offset با مقدار 1C2 می‌باشد که ۴ بایت عقب‌تر از مقدار مورد نیاز ما برای شروع LBA یعنی همان 1C6 است. پس بایت پنجم از این خط شروع LBA را دارد. نکته‌ی دیگری که حائز اهمیت است این موضوع است که اولین بایت کم ‌ارزش‌ترین است و ارزش بایت بعدی ۲۵۶ برابر بایت قبلی می‌باشد. (هر بایت ۸ بیت بوده و در نتیجه pow(2,8)==256 برابر بایت قبلی ارزش دارد) در این بخش ۴ بایت مربوط به LBA مقدار 0x00080000 داشته و با محاسبه‌ی زیر به این نتیجه میرسیم که ۲۰۴۸ سکتور باید از ابتدای دیسک به جلو حرکت کنیم تا به ابتدای پارتیشن اول برسیم.به طریق مشابه برای بدست آوردن اندازه‌ی پارتیشن باید به ابتدای بایت 446+12==1CA رفته و ۴بایت را برداشته و اندازه‌ی پارتیشن را بدست آورد. (در خط سوم از پایین، چهاربایت ستون‌های ۵و۶ در شکل ۱ که مقدار 0x00008018 را تشکیل می‌دهند)اجرای دستور fdisk بر روی فایل mbr.img و بررسی صحت این اطلاعات در شکل ۳ نمایش داده شده است. دقت شود که به دلیل کپی نگرفتن از تمامی اطلاعات دیسک، اطلاعات پارتیشن‌های Extended کامل نبوده و fdisk خطایی می‌دهد. (در ادامه با ساختار کامل این پارتیشن‌ها و دلیل این خطا آشنا می‌شویم)شکل ۳) چک کردن اطلاعات پارتیشن‌ها با دستور fdiskتا اینجای بحث توضیح دادیم که ۴ بخش برای اطلاعات ۴ پارتیشن وجود دارد که یکی از این ۴تا می‌تواند از نوع Extended باشد. این نوع پارتیشن امکان ایجاد Logical Drive را فراهم می‌کند. از دید سیستم‌عامل و کاربر نهایی Logical Driveها نیز مشابه پارتیشن‌های دیگر بوده و فقط افزودن تعداد درایوها و رسیدن به تعداد درایور بیشتر از ۴ را فراهم می‌کنند.(البته منهای اینکه نمی‌توانند Active بوده و برای بوت سیستم‌عامل مورد استفاده قرار گیرند!!) روال کار به این شکل است که یک لیست پیوندی ایجاد شده که این پارتیشن Extended به عنوان Headعمل کرده و اشاره کننده به اولین Logical Drive است. (مقدار LBA این پارتیشن به ابتدای اولین سکتور از اولین درایو اشاره کرده و Size آن اندازه‌ی کلیه‌ی درایوها در مجموع و فضایی که تشکیل می‌دهند را مشخص می‌کند)در هر Logical Drive اولین سکتور با نام (Extended Boot Record - EBR) شناخته شده و ساختاری مشابه MBR دارد که از ۴ بخش مربوط به پارتیشن‌ها، تنها ۲ بخش استفاده شده که بخش مربوط به پارتیشن اول، اطلاعات درایو فعلی را در بر گرفته و بخش دوم، اشاره‌گر به Logical Drive بعدی می‌باشد. در انتهای کار و پس از رسیدن به آخرین درایو، مقدار فیلد type (نوع پارتیشن در بایت شماره‌ی ۴) پارتیشن دوم EBR (که اشاره‌گر به درایو بعدی بود) برابر صفر خواهد بود. (همان NULL در انتهای لیست پیوندی ساده) این ساختار در شکل ۴ نمایش داده شده است.شکل ۴) ساختار پارتیشن‌های Extendedنکته مهم: در Logical Driveها مقدار LBA مشخص شده در پارتیشن اول EBR، نسبت به درایو قبلی بوده و از ابتدای محل Extended و یا ابتدای دیسک نمی‌باشد. این نکته بر خلاف پارتیشن‌های Primary است که LBA مقدار Offset را از ابتدای دیسک مشخص می‌کند.برای بررسی عملی توضیحات ارائه شده، با استفاده از دستور dd سکتور مربوط به EBR را خوانده و اطلاعات نمایش داده شده توسط آنرا بررسی می‌نماییم. اولین مرحله پیدا کردن آدرس شروع EBR است. برای اینکار از شکل ۳ و خروجی دستور fdisk -l mbr.img استفاده می‌کنیم. در خط دوم این خروجی اطلاعات یک پارتیشن Extended‌ نمایش داده شده است. همانطور که در این شکل مشاهده می‌شود اگر از ابتدای دیسک 411045886 سکتور را رد کنیم به ابتدای EBR می‌رسیم. با استخراج EBR‌ می‌توانیم اطلاعات آنرا مشابه کاری که با MBR انجام دادیم بررسی نموده و اندازه‌ی پارتیشن آنرا بدست آوریم. استخراج EBR و بررسی آن به کمک fdisk در شکل ۵ نمایش داده شده است.شکل ۵) استخراج و بررسی EBRدر شکل ۵ مشخص است که درایو منطقی که درون Extended ایجاد شده است از نوع Swap بوده و شروع آن ۲سکتور بعد از شروع Extended می‌باشد (یعنی از سکتور 411045888 که برابر است با start_of_extended + start_of_partition ) جمع بندی نهایی و نمایش کلیه‌ی اطلاعات دیسک با استفاده از اجرای fdisk بر روی sda در شکل ۶ نمایش داده شده است.شکل ۶) نمایش اطلاعات دیسک به صورت کاملدر این مقاله سعی کردم ساختار MBR را معرفی کرده و نحوه‌ی ذخیره‌ی اطلاعات پارتیشن‌ها در آنرا شرح دهم. برای بررسی این موضوع و تست عملی یک کد پایتون ساده هم نوشته بودم که به‌همراه فایل‌های دیسک، MBR, EBR در github گذاشته‌ام و می‌تونید برای تست ازش استفاده کنید. کد نوشته شده تمامی شرایط رو در نظر نمی‌گیره و تمامی حالت‌های خطا در اون بررسی نشده ولی برای اینکه یک تستی انجام بدید و بررسی دقیق مطالب گفته شده رو ببینید مفیده. در آخر مقاله هم اسم یک کتاب خوب که برای نوشتن مقاله ازش کمک زیادی گرفتم رو می‌گذارم که علاقه داشتید بخونید. همچنین لازمه بگم که عکس مربوط به پارتیشن‌های Extended از سایت مایکروسافت گرفته شده.امیدوارم مفید بوده باشه، موفق باشید.Reference:Linux Forensics (2015) - Philip Polstra</description>
                <category>ابوالفضل کاظمی</category>
                <author>ابوالفضل کاظمی</author>
                <pubDate>Fri, 29 May 2020 00:59:47 +0430</pubDate>
            </item>
                    <item>
                <title>مقدمه‌ای بر حملات Stack Overflow - قسمت اول - معرفی در لینوکس ۳۲بیتی</title>
                <link>https://virgool.io/@akazemi/%D9%85%D9%82%D8%AF%D9%85%D9%87%D8%A7%DB%8C-%D8%A8%D8%B1-%D8%AD%D9%85%D9%84%D8%A7%D8%AA-stackbuffer-overflow-duqt4wrcarug</link>
                <description>۰) مقدمهاگر به صورت جدی با زبان‌های ++C/C برنامه نوشته باشید، قطعا با خطای Segmentation Fault‌ در زمان دسترسی خارج از محدوده‌ی آرایه‌ها و یا کار با اشاره‌گر نامعتبر و تلاش برای دسترسی به فضای حافظه‌ی غیر مجاز، مواجه شده‌اید. همچنین در زمان بکارگیری توابعی مثل gets نیز warningهای کامپایلر مبنی بر آسیب‌پذیر بودن استفاده از این توابع را مشاهده کرده‌اید، ولی آیا به دلیل آسیب‌پذیر بودن این توابع فکر کرده‌اید؟ آیا ارتباط بین Buffer Overflow و این توابع را می‌دانید؟نکته: دقت کنید که محدوده‌ی آرایه‌ها در ++C/C چک نشده و استثنایی مثل چیزی که در جاوا/پایتون و خیلی از زبان‌های دیگر وجود دارد پرتاب نمی‌شود ولی اگر خارج از محدوده‌ی آرایه تغییری ایجاد کنید ممکن است باعث خطای Segmentation Fault‌ شود که در پایان این مقاله علت آنرا فرا خواهید گرفت.در حالت کلی Overflow‌ به معنی نوشتن بیش از حد مجاز در محلی از حافظه است (البته در موارد مرتبط با موضوع این مقاله وگرنه این موضوع کلیت ندارد) و در صورت استفاده‌ی هوشمندانه می‌تواند باعث تغییر مسیر اجرای برنامه و در نهایت اجرای کد دیگری مثل bin/bash/ و دسترسی به Shell شود. در این مقاله ابتدا با ساختار پروسه‌ها و نگاشت حافظه در آن‌ها آشنا شده و پس از بررسی ساختار پشته در زمان اجرای توابع، به بررسی Stack Overflow خواهیم پرداخت.نکته: در این مقاله از نسخه‌ی ۳۲ بیتی Ubuntu 14.04 استفاده شده است، ولی مطالب آن بر روی نسخه‌ی ۳۲ بیتی توزیع‌های دیگر لینوکس و یا در صورت کامپایل برنامه‌ها در توزیع‌های ۶۴بیتی با gcc -m32 قابل پیاده‌سازی است. اگر براتون سوال شده، خودم هم نمی‌دونم چرا این توزیع را انتخاب کردم!!! :-Dبرنامه‌های نوشته شده، پس از کامپایل، کد زبان ماشین تولید می‌کنند که قابل فهم برای CPU بوده و قادر به اجرای آن‌ها می‌باشد. برای اجرای یک برنامه، باید کد و داده‌ی آن در حافظه بارگذاری شده و سپس CPU با خواندن کد برنامه از حافظه، شروع به اجرای آن کند. پس برای تمامی عملیاتی که در یک برنامه داریم، مثل انتساب‌ها، دستورات شرطی، حلقه‌ها و فراخوانی توابع باید چیزی در حافظه وجود داشته باشد که عملیات و داده‌ی مورد نیاز آنرا مشخص می‌کند. به عنوان مثال برای a=5 باید مشخص شود که a به چه بخشی از حافظه اشاره کرده و عملیات مورد نظر ما این است که مقدار ثابتی را در آن قرار دهیم و قصد جمع کردن و یا انتساب مقدار متغیر دیگر در آن را نداریم. به مشخص کننده‌ی عملیات CPU و کاری که باید انجام شود، Opcode می‌گویند که توسط سازنده‌ی CPU و برای تمامی عملیات پشتیبانی شده توسط آن تعریف شده و برای نوشتن برنامه از آن استفاده می‌شود. در زبان‌های برنامه‌نویسی، ما با عبارت‌ها و دستورات سطح بالا که درک آن برای انسان ساده‌تر می‌باشد کار می‌کنیم. حتی در زبان اسمبلی که سطح پایین‌ترین حالت برنامه نویسی است، ما برای راحتی با عبارت‌هایی مثل mov, add, push, pop کار می‌کنیم ولی پردازنده درکی از این عبارات نداشته و در نهایت باید وظیه‌اش به صورت مجموعه‌ای از Opcodeها مشخص شود.نکته:‌ مثال‌های ما بر روی پردازنده‌های Intel‌ می‌باشد. برای اطلاع از Opcode دستورات مختلف، دستورات پشتیبانی شده توسط پردازنده، ساختار پردازنده و موارد مختلف مربوط به آن به Intel Developer’s Manual مراجعه کنید.۱) معرفی Segmentهای برنامهزبان‌های برنامه نویسی مختلف، طرز کار متفاوتی برای تولید کد زبان ماشین (کد قابل فهم توسط CPU که شامل همان Opcode‌ها می‌باشد) دارند ولی در نهایت نتیجه‌ی کار و خروجی مورد نظر برای CPU یکسان خواهد بود. زبان‌های سطح پایینی مثل ++Assembly, C, C پس از کامپایل، مستقیم کد زبان ماشین تولید می‌کنند ولی زبان‌هایی مثل Java, .Net به این صورت عمل نکرده و کدی تولید می‌کنند که توسط ماشین مجازی آن زبان پردازش شده و در نهایت تبدیل به کد زبان ماشین شده و اجرا می‌گردد. داده و کد یک برنامه در بخش‌های مختلفی در حافظه به نام Segment قرار می‌گیرند که امکان تعیین مجوز خواندن، نوشتن، اجرا کردن برای دسترسی به داده و کد به صورت مجزا را فراهم می‌کنند.نکته: مجوز Segmentهای مختلف (RWX که قابلیت خواندن، نوشتن و اجرا شدن را مشخص می‌کند) قابل تغییر است که در ادامه به توضیح بخشی از آن خواهیم پرداخت.کد برنامه در text/code segment قرار داده می‌شود که مجوز خوانده و اجرا شدن دارد. متغیرهای global برنامه در صورتیکه مقدار اولیه داشته باشند، در data segment‌ و در صورتیکه مقداردهی برای آن‌ها انجام نشده باشد در bss segment قرار داده می‌شوند. پارامترهای ورودی توابع و متغیرهای تعریف شده در تابع، بر روی stack قرار داده می‌شوند که برای جلوگیری از تزریق کد در برنامه و اجرا کردن آن، بخش stack برنامه‌ها مجوز اجرا نداشته و تنها می‌توان از آن اطلاعاتی خوانده و بر روی آن نوشت. در صورتیکه حافظه به صورت پویا و در زمان اجرای برنامه اختصاص داده شود، از بخشی از حافظه به نام heap استفاده خواهد شد. این ساختارها در شکل ۱ مشاهده می‌شوند. به نحوه‌ی رشد stack و heap در این شکل دقت کنید. همانطور که مشخص است stack‌ از آدرس‌های بالاتر به سمت آدرس پایین‌تر رشد کرده و heap برعکس آن، از آدرس کمتر به سمت آدرس بیشتر رشد می‌کند.شکل ۱) ساختار حافظه برنامه‌هابرای بررسی بخش‌بندی حافظه‌ی برنامه‌ها و آشنایی دقیق با کاربرد stack‌ در ارسال پارامترها و تعریف متغیرهای محلی، برنامه‌ی شکل ۲ را در نظر بگیرید. همانطور که مشاهده می‌شود دو متغیر سراسری که یکی دارای مقدار اولیه بوده و دیگری مقداری ندارد به همراه دو متغیر محلی عددی تعریف شده و سپس تابع printf برای نمایش مقدار متغیرهای محلی فراخوانی شده است.شکل ۲) برنامه تستی یکبرای مشاهده‌ی بخش‌های مختلف برنامه‌ها و تحلیل آن‌ها در لینوکس می‌توان از ابزارهایی مثل objdump, readelf استفاده نمود. در شکل ۳ نحوه‌ی کامپایل برنامه‌ی شکل ۲ و بررسی segment‌ مربوط به متغیرهای سراسری نمایش داده شده است. در ستون چهارم (آبی رنگ) عنوان segment دو متغیر سراسری مشاهده می‌شود که متغیر سراسری some_value که مقداری نداشت در bss و channel_id که مقداردهی شده بود در data قرار داده شده است.شکل ۳) استفاده از دستور objdump برای بدست آوردن segment و offset متغیرهای سراسریبرای اطلاع از ساختار کلی، هدرها و segmentهای یک برنامه می‌توانیم مشابه شکل ۴ از دستور readelf استفاده نماییم. همانطور که در ستون چهارم از راست (آبی رنگ) مشاهده می‌شود، بخش text که حاوی کد برنامه است دارای مجوز X برای اجرا شدن می‌باشد ولی دارای مجوز W نبوده و امکان تغییر کد در زمان اجرا وجود ندارد. همچنین بخش data, rodata که داده‌های برنامه را در بر دارند مجوز اجرای کد ندارند.شکل ۴) استفاده از readelf برای مشاهده‌ی اطلاعات segmentهابا مشاهده‌ی شکل ۵ و نحوه‌ی استفاده از دستور execstack نیز مشخص است که stack دارای مجوز X نبوده و امکان اجرای کد از روی آن وجود ندارد. هر چند می‌توانیم در زمان کامپایل برنامه و یا همانطور که در شکل ۵ مشخص است، با دستور execstack آنرا تغییر دهیم.شکل ۵) تغییر اجرایی بودن stack با دستور execstack  ۲) معرفی ساختار و کاربرد Stackاز stack در فراخوانی توابع، ارسال پارامترهای مورد نیاز آن‌ها، تعیین حافظه برای متغیرهای محلی، ذخیره‌ی آدرس بازگشت به تابع فراخوان و همچنین ذخیره‌ی مقدار رجیسترهایی که در تابع تغییر کرده و مقدار آن‌ها در آینده مورد نیاز است، استفاده می‌شود. در برنامه‌نویسی اسمبلی دو رجیستر BP, SP برای کار با پشته در نظرگرفته شده‌اند. SP‌ همیشه به بالای پشته و BP به ابتدای فضای پشته‌ی مربوط به تابع فعلی اشاره می‌کند. در زمان فراخوانی یک تابع، برای از بین نرفتن فضای پشته تابع قبلی (تابع فراخوان یا caller) مقدار BP توسط تابع فعلی (تابع فرخوانی شده یا callee) بر روی پشته ذخیره شده و در انتهای کار تابع callee و قبل از بازگشت به تابع فراخوان، از روی پشته برداشته شده و در BP قرار می‌گیرد (تغییر مقدار رجیسترهای پشته در انتهای کار توابع توسط دستور LEAVE انجام می‌شود). توجه کنید که به دلیل رشد پشته از آدرس بیشتر به آدرس کمتر، بالای پشته در پایین حافظه قرار دارد.نکته: رجیسترهای BP, SP در حالت ۱۶ بیتی که زمان شروع به کار کامپیوتر بوده و Real Mode نامیده می‌شود استفاده می‌شوند. در این حالت حافظه‌ی قابل دسترس 1MB است. پس از شروع به کار هسته‌ی سیستم‌عامل، تغییر وضعیتی به Protected Mode انجام می‌شود که در آن امکان استفاده از حافظه‌ی 4GB (بدون در نظر گرفتن PAE) فراهم بوده و رجیسترها نیز ۳۲ بیتی می‌باشند و به عنوان مثال رجیسترهای پشته EBP, ESP هستند. در حالت ۶۴ بیتی نیز رجیسترهای پشته RBP, RSP بوده و البته مدل فراخوانی توابع نیز با مدل ۳۲ بیتی متفاوت است که در مقاله‌ی دیگری به آن خواهیم پرداخت.برای فراخوانی توابع، پارامترهای تابع از راست به چپ بر روی پشته قرار داده می‌شوند. اینکار باعث می‌شود که با قرار دادن EBP به عنوان مبنا، با اضافه کردن به EBP به پارامترهای تابع و به ترتیب از اولین پارامتر تا آخرین آن‌ها و با کم کردن از EBP به متغیرهای محلی دسترسی داشته باشیم. در نهایت نکته‌ی آخر در مورد بازگشت از تابع است. پس از اتمام کار یک تابع، دستوری که بلافاصله پس از محل فراخوانی قرار دارد، اجرا شده و اجرای برنامه ادامه می‌یابد. رجیستر EIP همواره حاوی آدرس دستور بعدی است که باید توسط CPU اجرا شود. در زمان شروع فراخوانی یک تابع، ابتدا آدرس دستور بعد از فراخوانی تابع بر روی پشته قرار داده شده و سپس پرش به ابتدای تابع انجام شده و EIP برابر آدرس ابتدای تابع شده و اجرای تابع شروع می‌شود (اینکار توسط دستور call انجام می‌شود) در انتهای کار تابع نیز، مقدار آدرس برگشت ذخیره شده، از روی پشته برداشته شده و در EIP قرار داده شود و به این صورت ادامه‌ی کار از جایی که فراخوانی تابع انجام شده بود پیگیری خواهد شد (اینکار توسط دستور ret انجام می‌شود). توضیحات ارائه شده در مورد فراخوانی توابع در شکل ۶ به تصویر کشیده شده‌اند. در این شکل دقت کنید که EBP همواره به محل ذخیره‌ی مقدار قبلی خود اشاره کرده، آدرس بازگشت از تابع در محل EBP+4 قرار داشته و پارامترهای ورودی تابع از EBP+8 شروع می‌شوند که اولین پارامتر تابع در این آدرس قرار دارد.شکل ۶) ساختار پشته در زمان فراخوانی تابعبا دقت در شکل ۶ مشاهده می‌شود که اندازه‌ی هر خانه از پشته برابر ۴ بایت یا ۳۲ بیت است که مدل پیش‌فرض چینش پشته در برنامه‌های ۳۲ بیتی است. تاثیر این موضوع به این شکل است که اگر به عنوان مثال یک متغیر محلی به صورت [char s[10 نیز داشته باشید، مقدار فضای تخصیص داده شده برای آن بر روی پشته ۱۲ بایت می‌باشد و نه ۱۰ بایتی که شما تعریف کرده‌اید. با جمع بندی مطالب بیان شده در مورد پشته در زمانیکه در یک تابع قرار داشته باشید، ساختار پشته از دید تابع به صورت شکل ۷ خلاصه می‌شود. شکل ۷) خلاصه ساختار stack در زمان فراخوانی تابع و از دید تابع ۳) بررسی ساختار Stack به کمک gdbبرای بررسی ساختار پشته به صورت عملی برنامه‌ی شکل ۲ را در (gdb (GNU Debugger باز کرده و کد اسمبلی و ساختار اجرایی آنرا بررسی می‌کنیم. برای باز کردن یک برنامه در محیط gdb کافی است مسیر برنامه را به عنوان پارامتر برای آن ارسال نمود:  gdb prog-pathاین محیط به صورت پیش فرض از مدل اسمبلی AT&amp;T استفاده می‌کند که نسبت به مدل اینتل کمی عجیب است ولی می‌توان با دستور set disassembly-flavor intel آنرا تغییر داد! در شکل ۸ ساختار تابع main برنامه‌ی شکل ۲ به صورت اسمبلی نمایش داده شده است. همانطور که در خط 0+ مشاهده می‌شود، اولین کاری که در تابع انجام شده است، ذخیره کردن مقدار EBP است و پس از آن در خط 1+ مقدار ESP (بالای پشته در ابتدای تابع) در EBP قرار گرفته و به این شکل EBP به محل ذخیره‌ی مقدار قبلی خود اشاره کرده و مرز بین پارامترهای تابع و آدرس برگشت بــا متغیرهای محلی خواهد شد.شکل ۸) ساختار اسمبلی تابع main در gdbخط 3+ باعث می‌شود که Alignment پشته به صورت مضربی از ۱۶بایت قرار داده شود (کاری که gcc انجام داده است و علت آن این است که CPU در هر مرحله خواندن از حافظه ۱۶ بایت بارگذاری می‌کند، هر چند این عدد قابل تغییر است) و در خط بعدی یعنی 6+ فضایی برابر ۳۲ بایت برای پشته‌ی داخل تابع main در نظر گرفته می‌شود. اگر به میزان فضای مورد نیاز دقت کنیم، می‌بینیم که در این تابع دو متغیر محلی a, b تعریف شده‌اند که ۸ بایت برای آن‌ها بر روی پشته مورد نیاز است. از طرف دیگر پس از آن، یک فراخوانی تابع printf وجود دارد که سه پارامتر برای آن ارسال شده است. برای پارامتر اول که یک رشته است، آدرس رشته ارسال می‌شود (۴ بایت) و دو پارامتر دیگر که عددی می‌باشند مقدارشان بر روی پشته کپی می‌شود (در نهایت ۱۲ بایت برای پارامترهای printf) که در مجموع 20=8+12 بایت در این تابع بر روی پشته نیاز است که در خط 6+ به اندازه‌ی نزدیکترین عدد مضرب ۱۶ یعنی ۳۲ بایت فضا رزرو شده است.خطوط 9+ ,17+ مقداردهی اولیه به متغیرهای a, b را نشان داده و از خط 25+ تا خط 41+ قرار دادن پارامترهای تابع printf بر روی پشته قابل مشاهده است. از مقادیر داده شده و با دقت به این نکته که مقدار a برابر 5 و b برابر 7 است، می‌توانیم محل a, b را بر روی پشته تشخیص دهیم. در آدرس  EBP-4 روی پشته که معادل آدرس ESP+0x1c است فضای حافظه برای متغیر b بوده (به مقدار ۷ قرار داده شده در این مکان در خط 17+ دقت کنید) و پایین آن متغیر a در آدرس EBP-8 که معادل ESP+0x18 است بر روی پشته قرار داده می‌شود. با توجه به خطوط 25+ تا 41+ و شیوه‌ی قرارگیری پارامترهای تابع printf بر روی پشته نیز نکات جالبی مشاهده می‌شود. در این خطوط به دلیل عدم امکان تبادل اطلاعات بین دو قسمت از حافظه به صورت مستقیم، ابتدا مقدار متغیرها از حافظه خوانده شده، در رجیستر EAX قرار داده شده و سپس در محل دیگر حافظه که مربوط به پارامترهای printf می‌باشد قرار داده می‌شوند. از راست به چپ، ابتدا از آدرس ESP+0x1c مقدار متغیر b، سپس از ESP+0x18 مقدار متغیر a و درنهایت آدرس رشته‌ی format بر روی پشته قرار داده می‌شوند. در شکل ۹ ساختار پشته قبل از فراخوانی تابع printf نمایش داده شده است.شکل ۹) ساختار پشته قبل از فراخوانی تابع printfبرای بررسی آدرس بازگشت main می‌توانیم در بخشی از کد یک break-point گذاشته و با اجرا کردن برنامه محتویات حافظه در آدرس EBP+4 را مشاهده کرده و آنرا disassemble کنیم. با انجام اینکار مشاهده می‌شود که تابع main پس از اجرا شدن به کتابخانه‌ی libc باز می‌گردد و شروع اجرای آن از این کتابخانه بوده است. در  شکل ۱۰ یک break-point پس از اجرای تابع printf و در خط 53+ و در آدرس 0x08048482 از text segment قرار داده شده و با اجرای برنامه با دستور run محتویات آدرس ارسال شده به عنوان پارامتر اول printf به صورت رشته‌ای نمایش داده شده است (همان format-string). پس از آن نیز آدرس بازگشت main از EBP+4 نمایش داده شده و با disassemble کردن آن تابع libc که main از آنجا فراخوانی شده مشاهده می‌شود. دستور x در محیط gdb محتویات بخشی از حافظه را نمایش می‌دهد. دستور x/s برای نمایش به صورت رشته و x/wx برای نمایش یک کلمه (۳۲ بیت) به صورت عدد مبنای ۱۶ بکار می‌رود.شکل ۱۰) بررسی تابع main برنامه شکل ۲ در زمان اجراامیدوارم تا اینجای بحث خسته نشده باشید و جذابیت بحث براتون حفظ شده باشه، چونکه تازه پس از این مقدمه‌ی طولانی می‌خواهیم وارد مبحث Stack Overflow Attack بشیم!‌ :-D۴) بررسی یک برنامه‌ی آسیب‌پذیربحث Stack Overflow Attack در این خلاصه می‌شود که با نوشتن بیش از حد در یک بخش از حافظه، آدرس بازگشت تابع را تغییر داده و باعث اجرای کد دیگری شد. به عنوان مثال برنامه‌ی ساده‌ی شکل ۱۱ را در نظر بگیرید.شکل ۱۱) ساده‌ترین برنامه‌ی آسیب‌پذیر!در این برنامه به صورت خیلی ساده یک آرایه‌ی کاراکتری تعریف شده و با استفاده از تابع gets اطلاعاتی از ورودی دریافت شده و در این آرایه قرار داده می‌شود. در صورت کامپایل این برنامه با خطایی مبنی بر منسوخ شدن و خطرناک بودن تابع gets مواجه خواهید شد. علت این است که همانطور که در شکل ۱۱ مشخص است، این تابع اندازه‌ی ورودی را چک نمی‌کند. ساختار پشته برای این تابع main و فضایی که برای آرایه در نظر گرفته شده است، در شکل ۱۲ مشاهده می‌شود. دقت کنید که اسم آرایه ابتدای آدرس ذخیره کردن داده را مشخص کرده و با اضافه کردن عددی به آن، به خانه‌های بعدی آرایه دسترسی پیدا کرده و به سمت آدرس‌های بالاتر پیش خواهیم رفت و این به معنی این است که در صورت نوشتن بیش از حد در این آرایه (بیش‌تر از ۱۲۸ کاراکتر) امکان نوشتن در محل ذخیره‌ی EBP و آدرس بازگشت وجود دارد.شکل ۱۲) ساختار پشته برای برنامه‌ی آسیب‌پذیربرای کامپایل کردن این برنامه به صورت زیر عمل می‌کنیم:gcc -fno-stack-protector -z execstack -mpreferred-stack-boundary=2 ovf2.c -o ovf2در این دستور کامپایل چند نکته وجود دارد:سوئیچ fno-stack-protector- برای غیرفعال کردن مکانیزمی به نام canary است که برای تشخیص Overflow وجود دارد و ما برای فراهم کردن امکان تست ساده‌ای که انجام می‌دهیم آنرا غیرفعال می‌کنیم.سوئیچ zexecstack- برای دادن مجوز اجرا به پشته است که کدی که جایگزین خواهیم کرد امکان اجرا داشته و با استثنای سیستم‌عامل مواجه نشود.سوئیچ mpreferred-stack-boundary=2- باعث می‌شود که alignment پشته بجای ۱۶ بایتی که در بخش قبلی داشتیم برابر ۴ بایت باشد که حدالامکان برای رسیدن از ابتدای بافر (متغیر رشته‌ای str) تا آدرس بازگشت همان ۱۲۸بایت به اضافه‌ی ۴ بایت برای EBP یعنی در مجموع ۱۳۲ بایت نیاز باشد (البته باز هم ممکن است به این مقدار چند بایت، بسته به کامپایلر و توزیعی که استفاده می‌کنید اضافه شود. پس از ابتدای رشته تا آدرس بازگشت، حداقل ۱۳۲ بایت نیاز خواهید داشت و برای بدست آوردن مقدار دقیق آن باید مشابه چیزی که در ادامه می‌آید در محیط gdb تست کنید).با این توضیحات بیایید تست کنیم و ببینیم اگر بجای آدرس بازگشت BBBB قرار دهیم چه اتفاقی رخ خواهد داد. برای اینکار باید ۱۳۶ بایت (به صورت کاراکتری ۱۲۸ کاراکتر برای آرایه، ۴ کاراکتر برای مقدار EBP و در نهایت ۴ بایت برای آدرس بازگشت) در بافر بنویسیم که چهار بایت آخر آن BBBB بوده و ۱۳۲ بایت ابتدایی آن اهمیتی ندارد. برای تولید این رشته از پایتون به صورت زیر استفاده می‌کنیم:python -c &amp;quotprint( &#039;A&#039;*132 + &#039;BBBB&#039; )&amp;quot  &gt;  /tmp/inpاین دستور ۱۳۲ کاراکتر A و چهار کاراکتر B را پشت سرهم قرار داده و در فایل tmp/inp/ ذخیره می‌کند. در محیط gdb از این فایل به عنوان ورودی استفاده کرده و نتیجه را در شکل ۱۳ مشاهده می‌کنیم.شکل ۱۳) اجرای overflow و تغییر آدرس بازگشت از mainبا اجرای برنامه در محیط gdb امکان مشاهده‌ی آدرس بازگشت وجود دارد و همانطور که در تصویر مشخص است، این آدرس برابر 0x42424242 است که ASCII همان BBBB می‌باشد. به دلیل اینکه این آدرس به جای درستی اشاره نکرده و پس از اتمام کار main امکان بازگشت به محل خاصی وجود ندارد، این اجرا Segmentation Fault می‌دهد.۵) اجرای کد به کمک آسیب‌پذیریالان که موفق به اجرای موفقیت آمیز Stack Overflow شدیم، بیایید یک برنامه را اجرا کنیم. برای اجرای یک برنامه‌ی خارجی معمولا یک گزینه‌ی مناسب اجرای bin/bash/ و بدست آوردن یک shell است، که امکان دسترسی به کلیه‌ی دستورات را فراهم می‌کند. برای انجام اینکار باید برنامه‌ی اجرای shell در جایی از حافظه بارگذاری شده و سپس آدرس آن بجای آدرس بازگشت از main قرار گیرد. با کمی دقت مشخص است که بهترین محل ذخیره‌ی برنامه‌ی جدید، در ادامه‌ی stack و بعد از آدرس بازگشت می‌باشد. برنامه باید به صورت باینری که همان Opcode است ذخیره شود. مثلا در صورت نیاز به NOP (که البته نیاز هم خواهد شد!) باید 0x90 و به عبارت دقیق‌تر “x90\” بر روی پشته قرار داده شود.نکته: به برنامه‌ای که به صورت Opcode در حافظه قرار گرفته و از این طریق اجرا شود Shellcode می‌گویند.اما سوال مهم این است که آدرسی که باید به آن پرش صورت بگیرد چه آدرسی است؟ برای پیدا کردن این آدرس، در ابتدای main و پس از قرار دادن مقدار ESP در EBP می‌توانیم این مقدار را برداشته و از آن برای آدرس بازگشت جدید استفاده نمود. ولی هنوز یک مشکل باقی است! برای اطلاع از این مشکل من در شکل ۱۴ آدرس stack را در سه بار اجرای یک برنامه نمایش داده‌ام. عدد بعد از proc/ شناسه یا PID پروسه است و maps نگاشت حافظه را در بر دارد.شکل ۱۴) آدرس stack در چندبار اجرای برنامه آسیب‌پذیرهمانطوری که مشاهده می‌شود، آدرس بخشی از حافظه‌ی پروسه که stack در آن قرار دارد، در هر اجرا متفاوت است. این تغییر باعث می‌شود که امکان بدست آوردن آدرس stack و پرش به آن برای اجرای کد، پس از انجام Overflow امکان‌پذیر نباشد. در سیستم‌عامل‌های فعلی برای جلوگیری از نفوذ به برنامه‌ها و تشخیص آدرس دقیق ساختارهای پروسه‌ها از امکانی به نام (ASLR (Address Space Layout Randomization استفاده می‌شود. این مورد باعث می‌شود که Segmentهای مختلف برنامه در هر اجرا، آدرس‌های متفاوتی در حافظه داشته باشند. در لینوکس این قابلیت از طریق proc/sys/kernel/randomize_va_space/ کنترل می‌شود که می‌تواند یکی از سه عدد زیر را داشته باشد:0: غیر فعال1: فعال بودن برای کتابخانه‌ها و پشته2: فعال بودن برای کتابخانه‌ها، پشته و heapنکته: دقت کنید که بررسی ASLR و مشاهده‌ی mapping نمایش داده شده در شکل ۱۴ را از داخل gdb انجام ندهید. gdb برای راحتی در debug برنامه‌ها این مورد را غیر فعال می‌کند.برای سادگی در تست برنامه‌ی آسیب‌پذیر، ما ASLR را به صورت زیر غیر فعال می‌کنیم.غیر فعال کردن ASLR در لینوکسمورد دیگری که باید به آن توجه شود این است که در هربار اجرای برنامه‌ها به دلیل پارامترهای مختلفی مثل مسیر اجرای برنامه، ممکن است پشته ساختاری دقیقا مشابه دفعه‌ی قبل نداشته و چند بایت کمتر یا بیشتر از دفعه‌ی قبل بر روی آن قرار داشته باشد، پس نمی‌توانیم یک آدرس دقیق برای ابتدای برنامه‌ی مورد نظر خود (همان اجرای bin/bash/) در نظر بگیریم. برای رفع این مشکل نیز قبل از داده‌ی مربوط به اجرای bin/bash/ یکسری NOP قرار داده و به وسط آن‌ها اشاره می‌کنیم. به این صورت اجرای برنامه مختل نشده و با اجرا کردن تعداد کمتر یا بیشتری از دستورات NOP به ابتدای Shellcode خواهیم رسید. به دلیل اینکه نوشتن یک Shellcode برای اجرای bin/bash/ توضیحات جداگانه‌ای لازم دارد فعلا در مورد این قسمت توضیحاتی ارائه نشده و می‌توانید یک Shellcode آماده از یکی از سایت‌های shell-storm.org و یا www.exploit-db.com دانلود کنید.در نهایت برای بدست آوردن آدرس بازگشت مشابه شکل ۱۵، برنامه را در gdb باز کرده، پس از تغییر EBP یک break-point گذاشته و آنرا اجرا می‌کنیم. آدرس EBP+4 که محل ذخیره‌ شدن آدرس بازگشت از main است را به عنوان base آدرس Shellcode استخراج کرده و با اضافه کردن مقداری به آن آدرسی که باید بجای آدرس بازگشت از main برای اجرای Shellcode قرار گیرد را بدست می‌آوریم.نکته: ممکن است آدرس‌های نمایش داده شده در این مقاله با چیزی که شما تست می‌کنید متفاوت باشد.شکل ۱۵) بدست آوردن محل ذخیره آدرس بازگشت main و base آدرس shellcodeبا جمع‌بندی و کنار هم قرار دادن موارد ذکر شده در یک اسکریپت پایتون امکان اجرای Shellcode وجود خواهد داشت. در شکل ۱۶ اسکریپت نوشته شده مشاهده می‌شود. در این اسکریپت از struct.pack برای تولید آدرس نهایی جایگزین main-return استفاده شده است که little-endian بودن را لحاظ کرده و نیازی به برعکس نوشتن آدرس به صورت byte-string را نداشته باشیم!شکل ۱۶) اسکریپت پایتون برای اجرای shellcodeدر این اسکریپت ۳۰۰ بار NOP قبل از shellcode قرار داده شده و آدرس بازگشت از main برابر با ۲۰۰بایت بعد از محل آدرس بازگشت تنظیم شده است. در شکل ۱۷ فضای پشته پس از ارسال خروجی این اسکریپت به عنوان ورودی برنامه‌ی شکل ۱۱ مشاهده می‌شود.شکل ۱۷) ساختار پشته پس از ارسال خروجی اسکریپت پایتون به برنامه‌ی آسیب‌پذیربا ذخیره کردن خروجی پایتون در فایلی مثل tmp/inp_shell/ و اجرای برنامه‌ی دوم در محیط gdb با این ورودی، می‌توانیم اجرا شدن bin/bash/ را مشابه شکل ۱۸ مشاهده نماییم.شکل ۱۸) اجرا شدن موفقیت آمیز shellcode در gdb۶) جمع‌بندیسوالی که ممکن است برایتان پیش آید این است که خب این چه مشکل امنیتی می‌تواند داشته باشد؟ و با اجرا شدن این bash چه اتفاقی رخ خواهد داد؟ برای پاسخ به این سوال و اتمام این مقاله، فرض کنید که نرم‌افزاری دارید که setuid بر روی آن تنظیم شده و owner فایل آن نیز root باشد (یا حتی از آن ساده‌تر، کلا با کاربر root اجرا شده باشد و شما بتوانید ورودی به آن بدهید که منجر به Stack Overflow شود!!!). معنی این مورد این است که Effective User در زمان اجرا شدن این برنامه کاربر root بوده و برنامه تحت مجوزهای آن اجرا می‌شود. اگر چنین برنامه‌ای آسیب پذیر بوده و امکان نفوذ به آن فراهم شده باشد، شما می‌توانید به یک root shell دسترسی پیدا کرده و کنترل کامل سیستم را پیدا کنید. برای تست این موضوع من setuid را بر روی برنامه‌ی دوم تست خودمان فعال کرده و Overflow اجرای bash را خارج از gdb  اجرا می‌کنم. این مورد در شکل ۱۹ مشاهده می‌شود.شکل ۱۹) تنظیم کردن setuid و اجرای Overflow خارج از gdbدر شکل مشخص است که انجام Overflow با خطای Segmentation Fault مواجه نشده و برنامه اجرا شده است، ولی چرا Shell نمایش داده نشد؟!موضوع این است که به دلیل بسته شدن پروسه‌ی پدر که همان برنامه‌ی ovf2 می‌باشد، bash فرزند نیز بسته شده است! برای جلوگیری از این مورد می‌توانیم از دستور cat استفاده کنیم (این دستور ورودی دریافت شده را به خروجی ارسال کرده و تا زمان عدم دریافت signal باز خواهد ماند) و با باز نگه داشتن یک stream امکان تایپ دستور و دریافت نتیجه را داشته باشیم. در شکل ۲۰ نحوه‌ی استفاده نمایش داده شده است. دقت کنید که امکان استفاده از تمامی دستورات لینوکس وجود داشته و کاربر نیز root گزارش داده می‌شود! نکته: در این خروجی پس از اجرای Overflow دستورات وارد شده به رنگ سبز و خروجی دستور آبی رنگ است.شکل ۲۰) بدست آوردن root-shell به کمک برنامه‌ی آسیب‌پذیردر این مقاله سعی کردم مقدمات حملات Stack Overflow را به صورت کامل و با پیش‌نیازهایی که برای درک نحوه‌ی کار آن لازم است شرح داده و مثالی عملی از نحوه‌ی اجرای آن نمایش دهم که برای افرادی که هیچ آشنایی با این مباحث ندارند نقطه‌ی شروع مناسبی فراهم شود.امیدوارم مفید بوده باشه، موفق باشید.</description>
                <category>ابوالفضل کاظمی</category>
                <author>ابوالفضل کاظمی</author>
                <pubDate>Sun, 29 Mar 2020 15:36:29 +0430</pubDate>
            </item>
            </channel>
</rss>