قسمت سی و دوم بافر اورفلو
Program Slicing
فقط کدهایی که واقعا به درد تحلیل میخورن
یعنی یک داده رو از نقطه ورود تا محل استفاده دنبال کردیم
حالا فرض کنید برنامه چند هزار دستور داره
قرار نیست همه رو خط به خط بخونیم
Program Slicing
کمک میکنه فقط قسمت هایی از برنامه رو که روی یک مقدار مشخص تأثیر دارن جدا کنیم
Program Slicing
فرض کنید یک مقدار داریم
user_input
و میخوایم بدونیم چه قسمتهایی از برنامه روی این مقدار تأثیر میذارن
مثلا:
Input
↓
Function A
↓
Function B
↓
Function C
↓
Crash
به جای بررسی کل برنامه
فقط همین مسیر رو بررسی میکنیم
دو نوع اصلی
Forward Slice
از یک مقدار شروع میکنیم و جلو میریم
سؤال اصلی
این مقدار کجا استفاده میشه
مثلا:
input
↓
buffer
↓
memcpy
↓
target
Backward Slice
از یک نقطه مهم شروع میکنیم و به عقب برمیگردیم
مثلا Crash داریم
Crash
↑
memcpy
↑
buffer
↑
input
سوال اصلی
این مقدار از کجا اومده
یک مثال ساده:
فرض کنید کد اینه
C
int process(int input)
{
int a = input;
int b = a + 10;
int c = b * 2;
int unrelated = 500;
return c;
}
اگر هدف ما مقدار
c باشهلازم نیست
unrelated رو بررسی کنیمچون روی
c تاثیری ندارهSlice
تقریبا این بخشه
input
↓
a
↓
b
↓
c
در Reverse Engineering چرا مهمه؟
فرض کنید یک Crash دارید
و Stack Trace به یک تابع بزرگ میرسه
مثلا:
process_packet()
این تابع ممکنه صدها خط کد داشته باشه
ولی Crash فقط به یک مقدار خاص وابسته باشه
مثلا:
length
حالا به جای بررسی کل تابع
مسیر
length رو دنبال میکنیمlength
↓
check
↓
calculation
↓
buffer operation
↓
Crash
وابستگی دادهای
مثلا:
C
int size = input_length;
int copy_size = size + 8;
memcpy(buffer, input, copy_size);
اینجا
copy_size به size وابسته استو
size هم به input_lengthپس مسیر داده اینه
input_length
↓
size
↓
copy_size
↓
memcpy
این دقیقا چیزی هست که موقع Slicing میخوایم پیدا کنیم
وابستگی کنترلی هم مهمه
گاهی مقدار مستقیما منتقل نمیشه ولی روی تصمیم برنامه تاثیر میذاره
مثلا:
C++
if (size > 100)
{
process_large_input();
}
اینجا
size تعیین میکنه کدوم مسیر اجرا بشهپس علاوه بر Data Dependency
یک Control Dependency هم داریم
در Ghidra یا IDA
وقتی یک متغیر یا Register مهم پیدا کردید
میتونی References و مسیرهای
استفاده از اون رو بررسی کنید
مثلا:
Variable
↓
References
↓
Functions
↓
Instructions
بعد کم کم Slice موردنظر رو تشکیل میدید
در ابزارهای پیشرفتهتر هم میشه این کار رو به شکل خودکارتر انجام داد
تفاوت Data Flow و Slicing
خیلی ساده
Data Flow
میگه داده چطور حرکت میکنه
Program Slicing
میگه برای تحلیل یک مقدار خاص دقیقا کدوم قسمتهای برنامه مهم هستن
پس
Data Flow
↓
مسیر حرکت داده
Program Slicing
↓
بخش مرتبط با یک داده یا نتیجه خاص
وقتی با یک باینری بزرگ روبهرو شدیم
لازم نیست کل برنامه رو بررسی کنیم
میتونیم یک مقدار مهم مثل
Input
Length
Pointer
Crash Value
رو انتخاب کنیم
بعد مسیر وابستگیهای اون رو پیدا کنیم
در نتیجه حجم زیادی از کد که ارتباطی با مسئله ما نداره کنار گذاشته میشه
و تحلیل خیلی سریعتر میشه
@reverseengine