روی Memory و Pointer ها
تا اینجا Taint Analysis رو روی متغیرهای ساده انجام دادیم
ولی توی برنامه واقعی داستان یکم پیچیده تر میشه چون داده فقط بین چند تا متغیر جابه جا نمیشه
ممکنه وارد Memory بشه
داخل یه Buffer قرار بگیره
از طریق Pointer جابه جا بشه
و بعد چند تا تابع مختلف از همون Memory استفاده کنن
اینجاست که Taint Analysis یکم سخت تر میشه
فرض کنید اینو داریم
C
char buffer[32];
buffer[0] = input;
اینجا داده ای که از input اومده وارد Memory شده
پس میتونیم بگیم
input
↓
buffer[0]
حالا فرض کنید یه Pointer به این Buffer داریم
C
char *p = buffer;
بعد هم
char x = p[0];
اینجا داده دوباره از Memory خونده شده
پس مسیرمون میشه
input
↓
buffer[0]
↓
p[0]
↓
x
پس Taint فقط بین Register و Variable حرکت نمیکنه
ممکنه مسیرش از چند مرحله مختلف رد بشه
Register
↓
Memory
↓
Pointer
↓
Memory
↓
Register
این موضوع توی Reverse Engineering خیلی مهمه
چون وقتی Assembly رو نگاه میکنید ممکنه اصلا چیزی به اسم input یا buffer نبینید
مثلا ممکنه همچین چیزی ببینید
mov [rbp-40h], eax
lea rcx, [rbp-40h]
movzx eax, byte ptr [rcx]
اینجا باید خودتون رابطه بین این دستورها رو پیدا کنید
یعنی
EAX
↓
[rbp-40h]
↓
RCX
↓
[RCX]
↓
EAX
یعنی داده یه بار وارد Memory شده
بعد یه Pointer به اون قسمت ساخته شده
و دوباره داده از طریق همون Pointer خونده شده
Pointer
خودش Tainted هست یا داده ای که بهش اشاره میکنه
اینجا باید خیلی دقت کنیم
فرض کنید
C
char *p = buffer;
char x = *p;
ممکنه Buffer حاوی داده Tainted باشه
ولی خود Pointer یعنی p لزوما Tainted نیست
مثلا:
Address = Clean
Data at Address = Tainted
این دوتا با هم فرق دارن
پس وقتی Memory رو تحلیل میکنید همیشه دو تا سوال جدا از خودتون بپرسید
این Address از کجا اومده
محتوای این Address از کجا اومده
مثلا ممکنه داشته باشیم
Pointer
0x500000
خود این Address ممکنه کاملا عادی و قابل اعتماد باشه
ولی محتوای
Memory[0x500000]
ممکنه از ورودی کاربر اومده باشه
پس Address میتونه Clean باشه
ولی Data داخل اون Address Tainted باشه
یه مثال ساده تر:
C
int process(char *input)
{
char buffer[32];
buffer[0] = input[0];
int x = buffer[0] + 10;
return x;
}
اگر input رو Tainted کنیم
مسیر داده میشه
input[0]
↓
buffer[0]
↓
x
↓
return
پس حتی اگر داده برای یه مدت داخل Stack قرار گرفته باشه
Taint
خودش از بین نمیره
تا وقتی که داده جدید همچنان به اون مقدار وابسته باشه
حالا همین رو توی Assembly ببینیم
ممکنه چیزی شبیه این ببینیم
movzx eax, byte ptr [rcx]
mov byte ptr [rbp-20h], al
movzx eax, byte ptr [rbp-20h]
add eax, 10
اینجا باید مسیر رو خودمون از روی دستورها بسازیم
[RCX]
↓
AL
↓
[rbp-20h]
↓
EAX
↓
ADD 10
اگر
[RCX] حاوی داده Tainted باشهپس این مسیر هم باید توی تحلیل ما دنبال بشه
چرا Pointer Analysis مهم میشه
چون توی برنامه های بزرگ ممکنه داده مستقیم از یه Variable به Variable بعدی نره
ممکنه مسیر این شکلی باشه
Input
↓
Buffer
↓
Pointer
↓
Function A
↓
Memory
↓
Pointer
↓
Function B
↓
Comparison
اگر فقط Registerها رو نگاه کنی ممکنه وسط مسیر ارتباط داده رو گم کنی
برای همین توی Reverse Engineering خیلی مهم
Data Flow و Memory Analysis
خیلی به هم وابسته هستن
یه نکته مهم
هر Memory Access به معنی انتقال Taint نیست
مثلا:
C++
int x = 100;
int y = x + 20;
اینجا هیچ داده ای از Input نیومده
پس
x = Clean
y = Clean
ولی اگر داشته باشیم
C++
int x = input;
int y = x + 20;
اینجا داریم
x = Tainted
y = Tainted
پس همیشه باید منشا داده رو دنبال کنیم
فقط اینکه یه مقدار وارد Memory شده به تنهایی کافی نیست
باید ببینیم اون مقدار از کجا اومده و بعد کجا استفاده شده
تمرین:
این کد رو بررسی کنید
C
int process(char *input)
{
char buffer[32];
buffer[0] = input[0];
int x = buffer[0] ^ 0x55;
int clean = 500;
clean += 20;
int result = x + 10;
return result;
}
فرض کنید
input[0] نقطه شروع Taint باشهمسیر رو پیدا کنید
input[0]
↓
?
↓
?
↓
?
↓
return
بعد مشخص کنید
clean چرا نباید وارد این مسیر بشه@reverseengine