تشخیص ساختارهای برنامه نویسی در اسمبلی
اکثر مواقع نیاز نیست که ما تک تک دستورات رو آنالیز کنیم (مگر در مواقع ضروری), چونکه این کار واقعا خسته کننده هستش و ممکنه یه برنامه هزارها یا حتی میلیون ها دستور دیس اسمبل شده داشته باشه.پس برای رفع این مشکل ما میایم و دستورات رو بصورت گروهی بررسی میکنیم تا یه تصویر کلی از عاملیت برنامه داشته باشیم و بعد تنها روی دستورات خاصی که مد نظرمون هستش زوم می کنیم و آنالیز رو انجام میدیم.
همانطور که میدونید بیشتر نرم افزارها با زبان های سطح بالایی مثل C یا C++ توسعه داده میشند و اگه ما با دستورات اسمبلی تولید شده توسط کامپایلرهای این زبانها برای هر سازه آشنا باشیم پس می تونیم خیلی راحتتر کدهای دیس اسمبل شده ی برنامه های نوشته شده با این زبان هارو بررسی کنیم. -- خودمم نفهمیدم چی گفتم :) (منظورم از سازه همون دستورات برنامه نویسی استفاده شده توی کد هستش,مثل حلقه, عملیات محاسباتی و...)
پس ما قراره که سازه های زبان C رو در اسمبلی مورد بررسی قرار بدیم.مثلا حلقه ها, تعریف انواع متغیرها و... البته به این نکته هم توجه داشته باشید که نسخه کامپایلر و تنظیماتش هم روی کد اسمبلی تولید شدش تاثیر میزاره و همه مشابه هم نیستند.
بهتره ادامه بحث رو بصورت عملی پیش ببریم. ما میخوایم در ادامه کد دیس اسمبل شده متغیرهای محلی و متغیرهای عمومی رو بررسی کنیم
متغیرهای عمومی (Global variables) میتونند توی هر تابعی در برنامه قابل دسترس باشند اما متغیرهای محلی (Local variables) تنها توی تابعی که تعریف شدن میتونند قابل دسترس باشند.
توی مثال زیر متغیرها بصورت عمومی تعریف شدن:
int x = 1;
int y = 2;
void main()
{
x = x + y;
printf("Total = %d\n", x);
{
اما توی مثال زیر متغیر ها بصورت محلی تعریف شدن:
void main()
{
int x = 1;
int y = 2;
x = x + y;
printf("Total = %d\n", x);
{
همانطور که میبینید تفاوت زیادی ایجاد نشده و روی نتیجه هم تاثیری نداره و جواب هر دو مثال یکی هستش. اما در کد دیس اسمبل شده ببینیم چه اتفاقی افتاده.
متغیرهای عمومی در کد دیس اسمبل شده زیر:
00401003 mov eax, dword_40CF60
00401008 add eax, dword_40C000
0040100E mov dword_40CF60, eax
00401013 mov ecx, dword_40CF60
00401019 push ecx
0040101A push offset aTotalD ;"total = %d\n"
0040101F call printf
متغیرهای محلی در کد دیس اسمبل شده زیر:
00401006 mov [ebp+var_4], 0
0040100D mov [ebp+var_8], 1
00401014 mov eax, [ebp+var_4]
00401017 add eax, [ebp+var_8]
0040101A mov [ebp+var_4], eax
0040101D mov ecx, [ebp+var_4]
00401020 push ecx
00401021 push offset aTotalD ; "total = %d\n"
00401026 call printf
تفاوت عمده اینه که متغیرهای عمومی با آدرس های حافظه ارجاع شده اند اما متغیرهای محلی با آدرس های پشته .
حالا کدهای دیس اسبمل شده رو خط به خط توضیح میدم.اول کد دیس اسمبل شده متغیر عمومی:
خط اول:
dword_40CF60 متغیر عمومی X و مکان حافظه ای در 0x40CF60 هستش که به eax انتقال پیدا میکنه.به عبارت کلی متغیر X به eax انتقال پیدا میکنه
خط دوم:
dword_40C000 متغیر عمومی Y هستش که در مکان حافظه ی 0x40C000 قرار داره که با دستور add به X اضافه میشه و نتیجه در eax ذخیره میشه.
Eax = X + Y
خط سوم:
نتیجه جمع X + Y که در eax هستش به dword_40CF60 (که همون X) هستش انتقال پیدا میکنه پس یعنی X = X + Y
خط چهارم:
برای اینکه تابع printf فراخوانی بشه اول باید آرگومانهاشو روی پشته قرار بدیم تا باهاشون فراخوانی بشه (البته یادتون باشه که آرگومانهای این تابع از راست به چپ روی پشته قرار میگیرند).پس ابتدا نتیجه جمع روی به ecx انتقال میدیم.
خط پنجم:
سپس ecx رو روی پشته قرار میدیم (که همون جواب جمع یعنی X هستش).
خط ششم:
آفست رشته روی پشته قرار میگیره
خط هفتم:
توی این خط تابع printf با دوتا آرگومانی که بهش فرستادیم فراخوانی میشه
خوب حالا کد دیس اسمبل شده متغیر محلی رو بررسی میکنیم ولی قبلش یه نکته رو بگم اونم اینه که کد دیس اسمبل شده متغیر محلی بالا حالت labeling برنامه IDA Pro هستش و این کد بدون labeling بشکل زیر هستش:
00401006 mov dword ptr [ebp-4], 0
0040100D mov dword ptr [ebp-8], 1
00401014 mov eax, [ebp-4]
00401017 add eax, [ebp-8]
0040101A mov [ebp-4], eax
0040101D mov ecx, [ebp-4]
00401020 push ecx
00401021 push offset aTotalD ; "total = %d\n"
00401026 call printf
البته تفاوت چندانی نداره فقط خوانایی حالت labeling بیشتره.
خط اول:
بخاطر اینکه متغیرهای محلی توی پشته قرار دارند پس میتونیم با استفاده از ebp که مبنای قاب پشته هستش و آفست منفی, به متغیرهای محلی دسترسی پیدا کنیم.اگه از آفست مثبت استفاده کنیم به آرگومانها و چیزهای دیگه ای که خارج از مبحث ما هستش میرسیم.
پس به عبارت کلی با استفاده از [ebp-4] میتونیم به متغیر محلی اول دسترسی داشته باشیم. و در این خط میبینیم که مقدار 0 به متغیر اول یعنی X انتقال پیدا میکنه
خط دوم:
برای دسترسی به متغیرهای بعدی در پشته باید 4 بایت دیگه به آفست قبلی اضافه کنیم پس یعنی متغیر محلی دوم [ebp-8] هستش. و در اینجا مقدار 1 به متغیر دوم یعنی Y انتقال داده میشه
خط سوم:
متغیر X که حاوی مقدار 0 هستش به eax انتقال پیدا میکنه
خط چهارم:
متغیر Y که حاوی مقدار 1 هستش به مقدار eax که حاوی مقدار 0 هستش اضافه میشه و نتیجه توی eax ذخیره میشه پس یعنی eax = X + Y
خط پنجم:
مقدار eax به متغیر X انتقال داده میشه یعنی X = X + Y
خط ششم:
متغیر X که حاوی جمع X + Y هستش به ecx انتقال داده میشه
خط هفتم:
ecx روی پشته قرار میگیره تا در ادامه به تابع printf ارسال بشه
خط هشتم:
آفست رشته روی پشته قرار میگیره
خط نهم:
تابع printf با دو آرگومان بالا که روی پشته هستن فراخوانی میشه
میتونید به عنوان تمرین بیشتر این سورس رو توی کامپایلرهای مختلف کامپایل کنید
و نتجه هارو باهم مقایسه کنید و ببینید که کدوم کامپایلر یا چه نسخه ای از کامپایلر خوانایی بیشتر, یا کمتری داره.
کدهای دیس اسمبل بالا خروجی IDA Pro هستش امکان داره همین توی دیس اسمبلرهای دیگه مثل Olly , Win32DSM و ... این خروجی متفاوت باشه
میتونید برای اطلاعات بیشتر فصل 7 کتاب کرک و تکنیک های نفوذ انتشارات ناقوس رو بخونید بدک نیست لینک این فصلشو اینجا میزارم