زبان اسمبلی در مهندسی معکوس
برای یاد گیری کرک یا به صورت کلی مهندسی معکوس نرم افزار ما نیازمند یادگیری یه سری پیش زمینه ها هستیم.اولینش آشنایی با زبان اسمبلی هستش,نیاز نیست که برنامه نویسی با این زبان رو یاد بگیرید همینکه با دستورات مهمش آشنا باشید می تونید کار خودتون رو پیش ببرید.
OllyDbg و IDA Pro دوتا از ابزارهای رایج دیس اسمبل کردن فایل های باینری
برای استخراج دستورات اسمبلی از زبان سطح ماشین هستند. پس عملکرد نرم افزار به زبان اسمبلی ازطریق این ابزارها قابل رویت هستش
در اینجا قصد آموزش کامل این زبان رو نداریم و فقط یک توضیح کلی در مورد بخش های اولیه و پرکاربرد این زبان را می دهیم.
پیش زمینه:
BIT : کوچکترین قسمت داده ای که یا 0 هستش یا 1
مثال: 00000001 = 1 00000010 = 2 00000011 = 3
BYTE: یک بایت حاوی 8 بیت است که می تواند 255 حالت را بسازد.(0 تا 255)
WORD: یک WORD حاوی 2 بایت یا 16 بیت است.و بیشترین حالت ممکن آن 0FFFFh (یا 65535) است.
DOUBLE WORD: حاوی 2 تا WORD یا به عبارتی 32 بیت است و بیشترین مقدار ممکن آن 0FFFFFFFF (یا 4294967295) است.
KILOBYTE: یک کیلوبایت برابر با 1024 بایت (32*32) است.
MEGABYTE: محتوی 1024*1024 یا 1,048,578 بایت است.
قسمت اول: رجیسترها
در IA-32 هشت رجیستر عمومی وجود داره:
EAX: Extended Accumulator Register
EBX: Extended Base Register
ECX: Extended Counter Register
EDX: Extended Data Register
ESI: Extended Source Index
EDI: Extended Destination Index
EBP: Extended Base Pointer
ESP: Extended Stack Pointer
البته بغیر از اینها رجیسترهای دیگه ای هم وجود داره مثل EIP که حاوی اشاره گری به دستور جاری هستش.البته لازم نیست زیاد نگران باقی رجیسترها باشید.
توجه کنید که همه نام ها با حرف E شروع شده اند که این بمعنی 32 بیتی بودن آنهاست.
نکته:همانطور که میبینید در شکل بالا رجیسترهای ESI,EDI,ESP,EBP با باقی رجیسترها متفاوت هستن و حالت 8بیتی ندارند.
یکی از رجیسترهای مهم دیگه که بالا در موردش توضیح ندادیم رجیستر FLAGS هستش که به رجیستر وضعیت هم شناخته میشه و ازون برای نگهداری اطلاعاتی درباره وضعیت پردازنده استفاده میشه. توی سیستم های 32 بیتی این رجیستر هم 32 بیت هستش البته لازم نیست به همه پرچم های این رجیستر اهمیت بدید توی مهندسی معکوس 3 تا ازینها خیلی پرکاربرده (ZF-OF-CF) که اغلب اوقات وضعیت پرش ها رو مشخص می کنه.
CF: پرچم نقلی هستش, اگه عملیات رقم نقلی تولید کنه این پرچم 1 میشه
ZF: زمانی که نتیجه عملیات صفر بشه این پرچم 1 میشه
OF: اگه یه عملیات محاسباتی باعث سرزیز شدن بشه این پرچم 1 میشه.
قسمت دوم :دستورات
بیشتر دستورات دو تا عملوند (مثل add EAX, EBX) دارند اما برخی هم یکی (مثل not EAX) یا سه تا (مثل IMUL EAX, EDX, 64) عملوند دارند.
زمانی که شما یک دستور با DWORD PTR [XXX] مشاهده می کنید بمعنی مقدار DWORD در آفست حافظه [XXX] هستش.توجه داشته باشید که بایت ها در حافظه بصورت معکوس (یا Little Endian) ذخیره می شوند.
در زیر راه های استفاده از دستور add رو با دو عملوند می بینید:
add eax,ebx ;; Register, Register
add eax,123 ;; Register, Value
add eax,dword ptr [404000] ;; Register, Dword Pointer
[value] d
add eax,dword ptr [eax] ;; Register, Dword Pointer [register]d
add eax,dword ptr [eax+00404000] ;; Register, Dword Pointer [register+value]d
add dword ptr [404000],eax ;; Dword Pointer [value], Register
add dword ptr [404000],123 ;; Dword Pointer [value], Value
add dword ptr [eax],eax ;; Dword Pointer [register], Register
add dword ptr [eax],123 ;; Dword Pointer [register], Value
add dword ptr [eax+404000],eax ;; Dword Pointer [register+value], Register
add dword ptr [eax+404000],123 ;; Dword Pointer [register+value], value
در ادامه برخی از رایج ترین دستورات رو توضیح میدیم:
test arg1, arg2
این دستور برای انجام AND بیتی روی دو تا عملوند استفاده میشه.فقط به این نکته توجه کنید که نتیجه این دستور در جایی نگهداری نمیشه و فقط روی پرچم های زیر تاثیر میزاره.
CF, OF, PF, SF, ZF
cmp arg1, arg2
این دستور عمل تفریق رو روی دوتا عملوند انجام میده و نتیجه هم در جایی نگهداری نمیشه.فقط اگه نتیجه مقایسه صفر باشه روی پرچم ZF تاثیر میزاره و این پرچم یک میشه.پرچم های دیگه ای که ازین دستور متاثر میشند:
CF, AF, OF, PF, SF
jmp loc
این دستور رجیستر EIP رو با آدرس مشخص شده در این دستور بارگزاری میکنه و در نتیجه ادامه روند اجرا از آدرس loc ادامه پیدا میکنه
je loc
اگر دوتا عملوند دستور CMP قبل از je با هم مساوی باشند EIP با آدرس مشخص شده در این دستور بارگزاری میشه و نتیجش پرش کردن به اون آدرس هستش
یا به عبارتی jump if Zero Flag == 1
jne loc
اگه دوتا عملوند دستور CMP قبل از jne با هم مساوی نباشند EIP با آدرس مشخص شده در این دستور بارگزاری میشه و نتیجش پرش کردن به اون آدرس هستش
یا به عبارتی jump if Zero Flag == 0
jg loc
اگه اولین عملوند دستور CMP قبل از je بزرگتر از دومی باشه پرش انجام میشه
یا به عبارتی jump if ZF=0 and SF=OF
jge loc
اگه اولین عملوند دستور CMP قبل از jge بزرگتر یا مساوی دومی باشه پرش انجام میشه
یا به عبارتی jump if SF=OF
ja loc
اگه اولین عملوند دستور CMP قبل از ja بزرگتر از دومی باشه پرش انجام میشه
یا به عبارتی jump if CF=0 and ZF=0
jae loc
مثل دستور jge, اگه اولین عملوند دستور CMP قبل از jae بزرگتر یا مساوی دومی باشه پرش انجام میشه
یا به عبارتی jump if CF=0
jl loc
اگه اولین عملوند دستور CMP قبل از jl کوچکتر از دومی باشه پرش انجام میشه
یا به عبارتی jump if SF != OF
jle loc
اگه اولین عملوند دستور CMP قبل از jle کوچکتر یا مساوی دومی باشه پرش انجام میشه
یا به عبارتی jump if SF != OF or ZF = 1
jo loc
اگه دستور
محاسباتی قبل از jo پرچم OF
را یک کنه این پرش انجام میشه
jnz loc
اگه دستور محاسباتی قبل از jnz پرچم ZF رو صفر کنه این پرش انجام میشه
jz loc
اگه دستور محاسباتی قبل از jz پرچم ZF رو یک کند این پرش انجام میشه
call proc
ازین دستور برای فراخوانی زیرروال ها استفاده میشه البته قبل و بعد از اجرای این دستور یه سری اتفاقات میافته که میتونید برای اطلاعات بیشتر جستجو کنید.
ret [val]
این دستور مقدار بعدی روی پشته را داخل رجیستر EIP بارگزاری میکنه برای جزئیات بیشتر در مورد این دستور جستجو کنید.
loop arg
این دستور مقدار ECX رو کاهش میده و پرشی به آدرس مشخص شده در arg انجام میده
enter arg
این دستور فضایی روی پشته تخصیص میده و قاب پشته رو میسازه
leave
این دستور قاب پشته جاری رو تخریب میکنه و قاب پشته قبلی رو بازیابی می کنهhlt
این دستور در پردازنده مکث ایجاد میکنه
nop
این دستور هیچ کاری انجام نمیده
wait
این دستور منتظر میمونه تا CPU آخرین محاسباتش رو به پایان برسونه
MOV arg1 arg2
این دستور دو تا عملوند میگیره و arg2 رو به arg1 انتقال میده
ADD arg1 arg2
این دستور arg2 رو به arg1 اضافه میکنه و نتیجه رو داخل arg1 نگهداری میکنه پرچم هایی که روشون تاثیر میزاره:
CF, AF, OF, PF, SF, ZF
SUB arg1 arg2
این دستور arg2 رو از arg1 کم میکنه و نتیجه رو داخل arg1 نگهداری میکنه, پرچم هایی که روشون تاثیر میزاره:
CF, AF, OF, PF, SF, ZF
MUL arg
این دستور arg رو با مقدار رجیستر EAX ضرب میکنه و نتیجه رو در EDX:EAX نگهداری میکنه که 32 بیت پایینی در EAX و 32 بیت بالایی EDX هستش
DIV arg
این دستور مقدار EDX:EAX بر arg تقسیم میکنه و خارج قسمت رو در EAX و باقی مانده رو در EDX نگهداری می کنه
SHR Op arg
مقدار OP رو به تعداد arg به راست شیفت می کنه
SHL Op arg
مقدار OP رو به تعداد arg به چپ شیفت می کنه
ماشاله دستورات اسمبلی تمومی نداره:)
یه مقاله کمکی که می تونید دانلودش کنید
نکته: شاید کسایی که به مهندسی معکوس علاقه دارن و میخان وارد این حرفه بشن سخت ترین مرحله از نظرشون همین یادگیری اسمبلی باشه:)
پیشنهاد میکنم که به خوندن همین پست در مورد زبان اسمبلی اکتفا کنن (شاید بازم مطالب گفته شده توی این پست رو هم متوجه نشن, ولی اشکالی نداره) و سعی کنن در حین reverse کردن تلاش بیشتر به یادگیریش کنن.وگرنه با حفظ شدن عملکرد دستورات آبی براتون گرم نمیشه:)
موفق باشید.