ReverseEngineering
1.32K subscribers
50 photos
11 videos
106 files
888 links
Download Telegram
بخش بیست و هفتم بافر اورفلو


libFuzzer و Coverage Guided


Fuzzing
تا اینجا فهمیدیم Fuzzing یعنی دادن تعداد زیادی ورودی مختلف به برنامه و منتظر موندن تا یک جایی خرابکاری کنه😁
ولی Fuzzer های جدید فقط ورودی رندوم تولید نمیکنن بعضی از اونها بررسی میکنن هر ورودی برنامه رو از چه مسیر هایی عبور داده و همین باعث میشه کم کم ورودی‌ های جالب‌ تر تولید کنه

Coverage Guided یعنی چی

فرض کن یک برنامه این شکلیه

Input
↓
Check 1
↓
Check 2
↓
Hidden Function
اگر ورودی اول فقط به Check 1 برسه

سعی میکنه ورودی مسیر رو تغییر بده تا Fuzzer جدیدی باز بشه
مثلا به Check 2 برسه
بعد دوباره از همون ورودی استفاده میکنه و تغییرات بیشتری میده

هدف اینه که قسمت‌ های بیشتری از برنامه اجرا بشن چون خب ظاهرا ما تصمیم گرفتیم برای پیدا کردن باگ باید به همه جای برنامه سرک بکشیم 😅

libFuzzer
چیکار میکنه
libFuzzer
یک موتور Fuzzing برای برنامه‌های C و ++C است که با LLVM و Clang کار میکنه ما یک تابع مشخص به اون میدیم
بعد خودش بار ها و بار ها اون تابع رو با ورودی‌ های مختلف اجرا میکنه
هر ورودی که باعث رسیدن به مسیر جدیدی بشه ارزشمند تر میشه

تابع اصلی Fuzzing

معمولا چیزی شبیه این داریم:
C
#include <stdint.h>
#include <stddef.h>

int LLVMFuzzerTestOneInput(
const uint8_t *data,
size_t size
)
{
return 0;
}



توضیح کد زیر
این تابع هدف Fuzzer هست
هر بار libFuzzer یک ورودی جدید تولید میکنه محتوای ورودی داخل data قرار میگیره و اندازه اون داخل size قرار میگیره

یک مثال ساده:
C
#include <stdint.h>
#include <stddef.h>
#include <string.h>

int LLVMFuzzerTestOneInput(
const uint8_t *data,
size_t size
)
{
if (size >= 5)
{
if (memcmp(data, "HELLO", 5) == 0)
{
volatile int x = 1;
(void)x;
}
}

return 0;
}




اینجا چه اتفاقی میوفته
Fuzzer
ورودی‌ های مختلف رو امتحان میکنه

مثلا

AAAAA


بعد

HELAA


بعد شاید

HELLO


وقتی ورودی به HELLO برسه
یک مسیر جدید از برنامه اجرا میشه
Coverage Guided Fuzzing
این مسیر جدید رو تشخیص میده
و اون ورودی رو نگه میداره تا از اون برای پیدا کردن مسیرهای بعدی استفاده کنه

کامپایل با Clang
shell
clang -g -fsanitize=fuzzer,address file23_fuzz.c -o file23_fuzz



اینجا دو چیز با هم فعال شده

libFuzzer
+
AddressSanitizer


libFuzzer
ورودی تولید میکنه
ASan
مراقب خطا های حافظه هست
این ترکیب برای پیدا کردن Memory Bug خیلی قدرتمنده

اجرای Fuzzer
shell
./file23_fuzz


بعد برنامه شروع میکنه به تولید و تغییر ورودی‌ ها
اگر ورودی باعث کرش بشه معمولا همون ورودی ذخیره میشه

تا بتونیم بعدا دوباره بررسیش کنیم

چرا برای ما مهمه؟

فرض کنید یک برنامه پیچیده دارید
ولی دقیقا نمیدونید چه ورودی باعث رسیدن به یک تابع حساس میشه

Fuzzer
میتونه با امتحان کردن ورودی‌ های مختلف مسیر های جدید رو پیدا کنه

بعد شما میتونید همون مسیر ها رو داخل Ghidra یا IDA بررسی کنید

یعنی:

Fuzzing
↓
New Code Path
↓
Crash یا Behavior
↓
Ghidra / IDA
↓
Assembly Analysis


Coverage Guided Fuzzing
فقط دنبال کرش نیست
دنبال مسیر های جدید هم هست
هر مسیر جدید یعنی بخش جدیدی از برنامه که ارزش بررسی داره و وقتی libFuzzer رو با ASan ترکیب میکنیم هم میتونیم ورودی‌ های هوشمندانه‌ تر تولید کنیم هم Memory Bug ها رو سریع‌ تر تشخیص بدیم

تمرین:

تابع بالا رو کمی تغییر بدید و یک شرط جدید برای یک ورودی خاص اضافه کنید بعد فکر کنید Fuzzer چطور باید قدم به قدم ورودی رو تغییر بده تا به اون مسیر جدید برسه

@reverseengine
❤1
ReverseEngineering
بخش بیست و هفتم بافر اورفلو libFuzzer و Coverage Guided Fuzzing تا اینجا فهمیدیم Fuzzing یعنی دادن تعداد زیادی ورودی مختلف به برنامه و منتظر موندن تا یک جایی خرابکاری کنه😁 ولی Fuzzer های جدید فقط ورودی رندوم تولید نمیکنن بعضی از اونها بررسی میکنن هر ورودی…
Part 27 Buffer Overflow


libFuzzer and Coverage Guided

Fuzzing

So far, we have understood that Fuzzing means giving a lot of different inputs to the program and waiting for it to mess up somewhere😁
But new Fuzzers don't just generate random inputs. Some of them check what paths each input has taken in the program, which makes it gradually generate more interesting inputs

What does Coverage Guided mean

Suppose a program looks like this

Input
↓
Check 1
↓
Check 2
↓
Hidden Function


If the first input only reaches Check 1

It tries to change the input path so that a new Fuzzer opens

For example,

it reaches Check 2

Then it uses the same input again and makes more changes

The goal is to run more parts of the program because apparently we decided to go everywhere in the program to find the bug 😅

libFuzzer
What does libFuzzer do

A Fuzzing Engine for C Programs And it's C++ that works with LLVM and Clang. We give it a specific function.

Then it runs that function over and over again with different inputs.
Each input that leads to a new path becomes more valuable.

The main Fuzzing function

Usually we have something like this:

C
#include <stdint.h>
#include <stddef.h>

int LLVMFuzzerTestOneInput(
const uint8_t *data,
size_t size
)
{
return 0;
}


Explanation of the code below
This function is the target of the Fuzzer
Every time libFuzzer generates a new input, the content of the input is placed in data and its size is placed in size

A simple example:

C
#include <stdint.h>
#include <stddef.h>
#include <string.h>

int LLVMFuzzerTestOneInput(
const uint8_t *data,
size_t size
)
{
if (size >= 5)
{
if (memcmp(data, "HELLO", 5) == 0)
{
volatile int x = 1;
(void)x;
}
}

return 0;
}


What happens here
The Fuzzer
trys different inputs

For example

AAAA


then

HELAA


then maybe

HELLO


When the input reaches HELLO
a new path is executed from the program

Coverage Guided Fuzzing
detects this new path

and stores that input to use for finding subsequent paths

Compile with Clang:

shell
clang -g -fsanitize=fuzzer,address file23_fuzz.c -o file23_fuzz


Here two things are enabled together

libFuzzer
+
AddressSanitizer


libFuzzer
generates input

ASan
watches for memory errors

This combination is very powerful for finding memory bugs

Run Fuzzer

shell
./file23_fuzz


Then the program starts generating and modifying inputs

If the input causes a crash, usually the same input is saved

so that we can check it again later Let's do

Why is it important to us?

Suppose you have a complex program

But you don't know exactly what input will cause a critical function to be reached

Fuzzer
can find new paths by trying different inputs

Then you can examine those paths in Ghidra or IDA

That is:

Fuzzing
↓
New Code Path
↓
Crash or Behavior
↓
Ghidra / IDA
↓
Assembly Analysis


Coverage Guided Fuzzing
It doesn't just look for crashes
It also looks for new paths
Each new path is a new part of the program that is worth examining and when we combine libFuzzer with ASan we can both generate smarter inputs and detect memory bugs faster

Exercise:

Change the above function a little and add a new condition for a specific input then think about how the Fuzzer should change the input step by step to reach that new path

@reverseengine
❤1
Anniversary of Cyrus the Great of the Achaemenids
❤6
Vtable
چیه و چرا داخل ++C اهمیت داره؟

پست قبل درباره‌ی Function Pointer صحبت کردیم و دیدیم که برنامه میتونه آدرس یک تابع رو نگه داره و بعدا از طریق اون تابعی رو اجرا کنه

حالا در ++C یک مفهوم مشابه ولی پیچیده‌ تر داریم:

Vtable

یا:

Virtual Table

این موضوع برای فهم ساختار داخلی Object های C++ خیلی مهمه



Virtual Function

در ++C ممکنه یک کلاس تابعی داشته باشه که با کلمه‌ی virtual تعریف شده:

class Animal {
public:
virtual void speak() {
std::cout << "Animal";
}
};

حالا یک کلاس دیگه از اون ارثبری میکنه:

class Dog : public Animal {
public:
void speak() override {
std::cout << "Dog";
}
};

اینجا نکته اینه که اگر با یک Pointer از نوع Animal به یک Object از نوع Dog اشاره کنیم برنامه باید بدونه در نهایت کدوم نسخه از ()speak اجرا میشه

یعنی:

Animal Pointer
│
▼
Dog Object
│
▼
Dog::speak()

برای حل این مسئله معمولا از مکانیزمی مثل Vtable استفاده میکنیم



Vtable
رو میتونیم یک جدول در نظر بگیریم که شامل آدرس توابع Virtual یک کلاس هست

به شکل ساده:

Vtable
+----------------------+
| &function_1 |
+----------------------+
| &function_2 |
+----------------------+
| &function_3 |
+----------------------+

هر Object از کلاسی که Virtual Function دارده معمولا به این جدول مرتبطه

برای این ارتباط کامپایلر معمولا چیزی شبیه یک Pointer داخلی به نام:

vptr

داخل Object قرار میده

پس یک Object ممکنه به شکل مفهومی این طوری باشه:

Object
+----------------------+
| vptr --------------+----------+
+----------------------+ |
| Data | ▼
| Data | Vtable
+----------------------+ +------------------+
| &Virtual Function|
+------------------+
| &Virtual Function|
+------------------+

vptr به Vtable
مربوط به نوع واقعی Object اشاره میکنه



وقتی Virtual Function صدا زده میشه چه اتفاقی میوفته؟

فرض کنید این کد رو داریم:

Animal *animal = new Dog();
animal->speak();

برنامه نمیتونه فقط بر اساس نوع Pointer تصمیم بگیره چون نوع واقعی Object ممکنه Dog باشه

پس به‌صورت مفهومی این مسیر رو طی میکنه:

animal
│
▼
Object
│
▼
vptr
│
▼
Vtable
│
▼
آدرس speak()
│
▼
اجرای تابع

یعنی برنامه از اطلاعات داخلی Object کمک میگیره تا بفهمه کدوم تابع باید اجرا بشه




چرا Vtable برای Binary Analysis مهمه؟

وقتی یک برنامه‌ی ++C رو Reverse میکنیم Vtable ها میتونن اطلاعات مفیدی درباره‌ ی ساختار برنامه به ما بدن

مثلا با پیدا کردن یک Vtable ممکنه بتونیم حدس بزنیم:

یک Class چه Virtual Function هایی داره

چه Class هایی به هم مرتبطن

ساختار Object تقریبا چجوریه

کدوم توابع به یک Class مربوط میشن


برای همین Vtable ها در Reverse برنامه‌های ++C اهمیت زیادی دارن

بعضی وقتا اسم توابع از بین رفته و Symbol ها هم وجود ندارن چون انسان‌ ها ظاهرا تصمیم گرفتن برنامه‌ها رو بدون برچسب ول بکنن در این شرایط ساختار هایی مثل Vtable میتونن سرنخ مهمی باشن



ارتباط Vtable با Memory Corruption

نکته‌ی مهم اینجاست که vptr خودش یک Pointer هست

یعنی Object ممکنه چیزی شبیه این داشته باشه:

Object
+------------------+
| vptr | ← Pointer
+------------------+
| member_1 |
+------------------+
| member_2 |
+------------------+

اگر یک آسیب‌ پذیری حافظه باعث خراب شدن داده‌های Object بشه در تحلیل باید بررسی کنیم:

کدوم قسمت‌های Object تحت تاثیر قرار گرفتن؟



اگر یک Pointer مهم در ساختار Object قرار داشته باشه تغییر دادن اون میتونه رفتار بعدی برنامه رو تغییر بده

اما باز هم باید تفاوت مهمی رو یادمون باشه:

خراب شدن یک Object لزوما به معنی کنترل اجرای برنامه نیست



باید بررسی کنیم برنامه بعدا با داده‌ی خراب‌ شده چکاری انجام میده و چه مکانیزم‌ های محافظتی وجود داره




Vtable با Function Pointer
چه فرقی داره؟

از نظر مفهوم هر دو به آدرس توابع مربوطن

اما تفاوت دارن

Function Pointer

یک متغیر مستقیما آدرس یک تابع رو نگه میداره:

Function Pointer
│
▼
Address of Code

Vtable
❤2
یک Object معمولا اول به یک جدول اشاره میکنه و اون جدول شامل آدرس توابع هست:

Object
│
▼
vptr
│
▼
Vtable
│
├── Function A
├── Function B
└── Function C

بنابراین Vtable یک سطح ساختارمند تر برای مدیریت توابع Virtual فراهم میکنه



Vtable
دقیقاً یک استاندارد رسمی ++C هست؟

یک نکته‌ ی مهم:

خود مفهوم Vtable به شکل دقیق در استاندارد C++ تعریف نشده

Vtable
در واقع یک روش رایج برای پیاده‌ سازی Polymorphism توسط کامپایلر هاست

یعنی ممکنه جزئیات پیاده‌سازی بین:

GCC

Clang

MSVC


متفاوت باشه

اما ایده‌ی کلی یعنی استفاده از اطلاعاتی برای پیدا کردن تابع درست در زمان اجرا در عمل بسیار رایجه

برای همین هنگام Reverse باید همیشه به ABI و کامپایلری که برنامه با اون ساخته شده توجه کنیم





Vtable
معمولا جدولی شامل آدرس Virtual Function های یک کلاس در ++C هست Object هایی که از Virtual Function استفاده میکنن اغلب یک Pointer داخلی مثل vptr دارن که اونها رو به Vtable مربوط میکنه موقع اجرای یک Virtual Function برنامه از این ساختار برای پیدا کردن تابع مناسب استفاده میکنه

شناخت Vtable هم در Reverse کردن برنامه‌ های ++C مهمه و هم برای درک اینکه Object ها در حافظه چجوری سازماندهی میشن

@reverseengine
❤2
ReverseEngineering
Vtable چیه و چرا داخل ++C اهمیت داره؟ پست قبل درباره‌ی Function Pointer صحبت کردیم و دیدیم که برنامه میتونه آدرس یک تابع رو نگه داره و بعدا از طریق اون تابعی رو اجرا کنه حالا در ++C یک مفهوم مشابه ولی پیچیده‌ تر داریم: Vtable یا: Virtual Table این…
Vtable

What is it and why is it important in C++?

In the previous post, we talked about Function Pointer and saw that the program can hold the address of a function and later execute a function through it

Now in C++ we have a similar but more complex concept:

Vtable

Or:

Virtual Table

This is very important to understand the internal structure of C++ Objects

Virtual Function

In C++, a class may have a function that is defined with the virtual keyword:

class Animal {
public:
virtual void speak() {
std::cout << "Animal";
}
};

Now another class inherits from it:

class Dog : public Animal {
public:
void speak() override {
std::cout << "Dog";
}
};

The point here is that if we point to an Object of type Dog with a Pointer of type Animal, the program needs to know which version of speak() will be executed in the end

That is:

Animal Pointer
│
▼
Dog Object
│
▼
Dog::speak()

To solve this problem, we usually use a mechanism like Vtable

We can consider Vtable
as a table that contains the addresses of Virtual functions of a class

Simply:

Vtable
+--------------------+
| &function_1 |
+-----------------------+
| &function_2 |
+-----------------------+
| &function_3 |
+-----------------------+

Every Object of a class that has a Virtual Function is usually linked to this table

For this connection, the compiler usually puts something like an internal Pointer called:

vptr

inside the Object

So an Object may conceptually look like this:

Object
+-----------------------+
| vptr --------------+------+
+----------------------+ |
| Data | ▼
| Data | Vtable
+---------+ +------------------+
| &Virtual Function|
+------------------+
| &Virtual Function|
+------------------+

vptr points to the Vtable
of the actual type of Object

What happens when a Virtual Function is called?

Suppose we have this code:

Animal *animal = new Dog();
animal->speak();

The program cannot decide based on the Pointer type alone because the actual type of Object may be Dog

So conceptually it goes like this:

animal
│
▼
Object
│
▼
vptr
│
▼
Vtable
│
▼
speak() address
│
▼
function execution

That is, the program uses the internal information of the Object to understand which function to execute

Why is the Vtable important for Binary Analysis?

When we reverse a C++ program, Vtables can give us useful information about the program structure.

For example, by finding a Vtable, we may be able to guess:

What virtual functions a class has

What classes are related to each other

What is the approximate structure of an object

Which functions are related to a class

That is why Vtables are so important in reversing C++ programs

Sometimes the function names are missing and the symbols are missing because humans apparently decided to leave programs unlabeled. In these situations, structures like Vtables can be important clues

The relationship of Vtables to Memory Corruption

The important point here is that vptr itself is a Pointer

That is, an Object might look something like this:

Object
+------------------+
| vptr | ← Pointer
+------------------+
| member_1 |
+------------------+
| member_2 |
+------------------+

If a memory vulnerability causes Object data corruption, in the analysis we need to consider:

Which parts of the Object are affected?

If there is an important Pointer in the Object structure, changing it can change the subsequent behavior of the program

But we still need to remember an important difference:

Corruption of an Object does not necessarily mean control of program execution

We need to consider what the program does with the corrupted data later and what protection mechanisms are in place

What is the difference between a Vtable and a Function Pointer?

Conceptually, both are related to the address of functions

But they are different

Function Pointer

A variable directly holds the address of a function:

Function Pointer
│
▼
Address of Code

Vtable
❤1
An Object usually first points to a table, and that table contains the addresses of functions:

Object
│
▼
vptr
│
▼
Vtable
│
├── Function A
├── Function B
└── Function C

So Vtable provides a more structured level for managing Virtual functions

Vtable
Is it really an official C++ standard?

An important note:

The concept of Vtable itself is not precisely defined in the C++ standard

Vtable
is actually a common way for compilers to implement Polymorphism

That is, the implementation details may differ between:

GCC

Clang

MSVC

But the general idea of ​​using information to find the right function at runtime is very common in practice

That is why when reversing, we should always pay attention to the ABI and the compiler with which the program was built

Vtable

Usually a table containing the addresses of Virtual Functions of a class in C++ Objects that use Virtual Functions often have an internal Pointer such as vptr that relates them to the Vtable. When executing a Virtual Function, the program uses this structure to find the appropriate function

Understanding Vtable is important both in reversing C++ programs and in understanding how Objects are organized in memory

@reverseengine
❤1
Data Flow Analysis
دنبال کردن مسیر واقعی داده

تا اینجا بیشتر تمرکزمون روی این بود که برنامه چه دستور هایی اجرا میکنه

ولی از اینجا به بعد یک سؤال مهمتر میپرسیم:

داده از کجا میاد و آخرش کجا میره؟

این دقیقا همون چیزیه که بهش Data Flow Analysis میگیم

فرض کنید این کد رو داریم:
C++
int calculate(int a, int b)
{
    int x = a + b;
    int y = x * 2;
    return y;
}


اگر فقط به ترتیب دستورها نگاه کنیم میگیم:

اول جمع انجام میشه
بعد ضرب انجام میشه
بعد نتیجه برمیگرده

ولی در Data Flow Analysis این شکلی نگاه میکنیم:

a ─┐
├── ADD ──> x ──> MUL ──> y ──> Return
b ─┘



یعنی مسیر خود داده رو دنبال میکنیم
حالا فرض کنید وسط برنامه این دستورها هم وجود داشته باشن:
C++
int temp = 500;
temp ^= 123;
temp += 20;


اگر temp هیچوقت روی y یا خروجی تابع تأثیر نذاره توی مسیر اصلی داده قرار نمیگیره
اینجا Data Flow Analysis خیلی کمک میکنه Junk Code رو از منطق واقعی جدا کنیم
وقتی داخل IDA یا Ghidra یک تابع پیچیده میبینیم لازم نیست از اول تا آخر همه چیز رو حفظ کنیم
یک مقدار مهم رو انتخاب کنید و دنبالش کنید

مثلا اگر ورودی تابع داخل یک رجیستر وارد شده ببینید:

کجا کپی میشه
کجا تغییر میکنه
داخل حافظه ذخیره میشه یا نه
به تابع دیگه‌ ای می‌ره یا نه

و در اخر روی چه چیزی تأثیر میذاره
مثلا در اسمبلی ممکنه چنین چیزی ببینید:

Asm
mov eax, edi
add eax, esi
imul eax, 2



اگر فرض کنیم EDI و ESI ورودی هستن مسیر داده این شکلیه:

EDI ─┐
├──> EAX ──> ADD ──> IMUL
ESI ─┘



نکته مهم اینه که اسم رجیستر به معنی مسیر داده نیست ممکنه یک مقدار از RAX وارد RBX بشه بعد داخل حافظه ذخیره بشه و دوباره داخل RCX برگرده
اگر فقط اسم رجیسترها رو نگاه کنید خیلی زود گم میشید😅

باید خود مقدار رو دنبال کنید
به این مفهوم بعصی وقتا Data Provenance هم میگیم

یعنی بفهمید منشا یک مقدار کجاست

مثلا یک مقدار ممکنه از اینجا اومده باشه:

User Input
↓
Buffer
↓
Function A
↓
Function B
↓
Comparison


این مدل نگاه مخصوصا موقع تحلیل برنامه‌ های Obfuscate شده خیلی مهمه
چون Obfuscation ممکنه مسیر اجرای برنامه رو شلوغ کنه ولی داده هنوز باید از یک جایی وارد بشه و به یک جایی برسه

تمرین:

این تابع رو بررسی کنید:
C
int process(int a)
{
int x = a;
x = x ^ 0x55;
x = x + 10;

int junk = 100;
junk *= 5;

return x;
}



روی کاغذ مسیر a تا خروجی رو بکشید
بعد مشخص کنید junk وارد مسیر اصلی داده میشه یا نه

هدف این تمرین اینه که کم‌ کم وقتی یک تابع رو باز میکنید فقط دستورها رو نبینید
داده رو ببینید که داره بین رجیسترها حافظه و توابع حرکت میکنه

@reverseengine
❤2
ReverseEngineering
Data Flow Analysis دنبال کردن مسیر واقعی داده تا اینجا بیشتر تمرکزمون روی این بود که برنامه چه دستور هایی اجرا میکنه ولی از اینجا به بعد یک سؤال مهمتر میپرسیم: داده از کجا میاد و آخرش کجا میره؟ این دقیقا همون چیزیه که بهش Data Flow Analysis میگیم فرض…
Data Flow Analysis Following the actual data path

So far, we have been mostly focused on what instructions the program executes

But from here on, we ask a more important question:

Where does the data come from and where does it end up?

This is exactly what we call Data Flow Analysis

Suppose we have this code:

C++
int calculate(int a, int b)
{
    int x = a + b;
    int y = x * 2;
    return y;
}


If we just look at the order of the instructions, we would say:

First the addition is done
Then the multiplication is done
Then the result is returned

But in Data Flow Analysis, we look at it like this:

a ─┐
├── ADD ──> x ──> MUL ──> y ──> Return
b ─┘


That is, we follow the path of the data itself
Now suppose there are these instructions in the middle of the program:

C++
int temp = 500;
temp ^= 123;
temp += 20;


If temp never affects y or the output of the function, it will not be in the main data path.
Here Data Flow Analysis helps a lot to separate Junk Code from the real logic.
When we see a complex function in IDA or Ghidra, we do not need to memorize everything from beginning to end.
Choose an important value and follow it.

For example, if the input of the function is entered into a register, see:

Where is it copied?

Where is it changed?

Is it stored in memory or not?

Is it passed to another function or not?

And what does it affect in the end?

For example, in assembly, you might see something like this:

Asm
mov eax, edi
add eax, esi
imul eax, 2


If we assume that EDI and ESI are inputs, the data path looks like this:

EDI ─┐
├──> EAX ──> ADD ──> IMUL
ESI ─┘


The important thing is that the name of the register does not mean the data path. A value from RAX may be entered into RBX, then stored in memory, and then again into RCX. Back
If you just look at the register names, you'll get lost very quickly😅

You have to follow the value itself
Sometimes we also call this Data Provenance

That is, find out where a value comes from

For example, a value might come from:

User Input
↓
Buffer
↓
Function A
↓
Function B
↓
Comparison

This approach is especially important when analyzing obfuscated programs
Because obfuscation may clutter the program's execution path, but the data still has to come from somewhere and go somewhere

Exercise:

Examine this function:

C
int process(int a)
{
int x = a;
x = x ^ 0x55;
x = x + 10;

int junk = 100;
junk *= 5;

return x;
}


Draw a path from a to the output on paper

Then determine whether junk enters the main data path

The goal of this exercise is to gradually see not just the instructions when you open a function

See the data moving between registers, memory, and functions

@reverseengine
❤1
Thread Analysis و Start Address

بعضی وقتا خود Process چیز خاصی نشون نمیده ولی Thread هاش داستان اصلی رو لو میدن

تا اینجا دیدیم که EDR فقط اسم فایل یا Process رو نگاه نمیکنه

یکی از چیزایی که عمیق تر بررسی میشه Thread های داخل Process هستن
هر Process میتونه چند تا Thread داشته باشه

هر Thread هم مسیر اجرای خودش رو داره

برای همین وقتی EDR یا یک تحلیلگر میخواد بفهمه داخل یک Process دقیقا چه خبره بررسی Thread ها میتونه اطلاعات مهمی بهش بده

Start Address
خیلی ساده هر Thread برای اجرا باید از یک نقطه شروع کنه
به اون نقطه میگن Start Address
یعنی EDR میتونه بررسی کنه که Thread از کجای حافظه شروع به اجرا کرده
مثلا ممکنه Start Address داخل یک DLL شناخته شده باشه
یا ممکنه داخل یک قسمت ناشناس از حافظه باشه
اگر داخل یک DLL معمولی باشه باز هم باید Context بررسی بشه
اگر داخل یک قسمت ناشناس از حافظه باشه ممکنه مشکوک تر به نظر برسه
ولی اینجا یک نکته خیلی مهم وجود داره
هر Memory Region ناشناسی لزوما مخرب نیست

بعضی برنامه ها خودشون موقع اجرا کد تولید میکنن یا حافظه رو به شکل خاصی مدیریت میکنن

مثلا JIT Compiler ها مرورگرها و بعضی Runtime ها ممکنه رفتارهایی داشته باشن که از بیرون عجیب به نظر برسه
چون مشخصا انسان ها یک False Positive ساده رو نمیپذیرن و ترجیح میدن سیستم امنیتی برنامه سالمشون رو هم گاهی بکشه😁

EDR
فقط Start Address رو نگاه نمیکنه
در تحلیل واقعی معمولا چند تا چیز با هم بررسی میشه
اول اینکه Thread چه زمانی ساخته شده
مثلا یک برنامه اجرا میشه
چند دقیقه بعد یک اتفاق غیرعادی در حافظه میفته
بلافاصله بعدش یک Thread جدید ساخته میشه
اینجا زمان بندی اتفاق ها مهمه
چون یک Event به تنهایی ممکنه چیز خاصی نباشه ولی وقتی چند Event پشت سر هم اتفاق میفتن داستان فرق میکنه
دوم اینکه چه کسی باعث ایجاد Thread شده
مثلا یک Process با Process دیگه ارتباط برقرار میکنه
بعد داخل اون Process یک Thread جدید ظاهر میشه
همین ارتباط بین دو Process میتونه برای Detection خیلی مهم باشه
بعضی وقت ها خود Thread چیز عجیبی نیست ولی اتفاقاتی که قبل از ایجادش افتاده مشکوک هستن
سوم اینکه Thread دقیقاً از کجا اجرا میشه

EDR
میتونه Memory Region اطراف Start Address رو بررسی کنه
مثلا این حافظه ممکنه مربوط به یک Module باشه
ممکنه Private Memory باشه
ممکنه مربوط به یک Image باشه
یا یک Memory Region دیگه باشه
مثلا اگر Thread از یک Private Executable Region شروع به اجرا کنه ممکنه نیاز به بررسی بیشتری داشته باشه
مخصوصاً اگر قبل از اون یک اتفاق غیرعادی روی همون قسمت از حافظه افتاده باشه

چهارم Call Stack هست

Call Stack
میتونه تا حدی نشون بده Thread از چه مسیر و چه Function هایی به وضعیت الانش رسیده
یعنی EDR فقط نقطه فعلی Thread رو نمیبینه و ممکنه مسیر رسیدن به اون نقطه رو هم بررسی کنه
اگر مسیر اجرا با رفتار معمول اون برنامه جور درنیاد میتونه یک نشونه مشکوک باشه
ولی باز هم یک Call Stack عجیب به تنهایی یعنی برنامه مخرب نیست

ابزارهای Debugging
Instrumentation
و Runtime های مختلف هم میتونن ساختارهایی ایجاد کنن که معمولی به نظر نرسن

مدل فکری Detection
فرض کنید فقط این اتفاق دیده بشه
یک Thread جدید ساخته شده
به تنهایی اطلاعات زیادی بهمون نمیده
ولی حالا فرض کنید این اتفاق ها پشت سر هم افتاده
Process A اجرا میشه
بعد با Process B ارتباط برقرار میکنه
داخل Process B یک اتفاق مربوط به حافظه رخ میده
بعد یک قسمت قابل اجرا در حافظه ظاهر میشه
بعد یک Thread شروع به اجرا میکنه
بعد فعالیت شبکه شروع میشه
اینجا قضیه فرق میکنه
چون EDR فقط یک اتفاق رو نمیبینه
داره رابطه بین اتفاق ها رو میبینه
و این نکته اصلیه

EDR
دنبال یک Event جادویی نیست
دنبال ارتباط بین Event هاست
Thread
ها چرا برای تحلیلگر مهمن
چون میتونن بین تغییراتی که در حافظه اتفاق افتاده و اجرای واقعی ارتباط ایجاد کنن
مثلا ممکنه یک قسمت از حافظه تغییر کرده باشه ولی هنوز مشخص نباشه این تغییر واقعاً استفاده شده یا نه
اما اگر بعدش یک مسیر اجرایی جدید ظاهر بشه و یک Thread از اون مسیر شروع به کار کنه قضیه برای تحلیلگر معنی بیشتری پیدا میکنه
برای تحلیل رفتار یک Process باید این موارد رو کنار هم دید

Process Tree
Cross Process Access
Memory Regions
Memory Permission Changes
Thread Start Address
Call Stack
Timeline


هیچ کدوم از اینا به تنهایی کافی نیست
ولی وقتی همه کنار هم قرار میگیرن Detection قوی تر میشه
Telemetry
Context
Timeline
Correlation


همه اینا وقتی با هم بررسی بشن Detection دقیق تری میدن
و دقیقا به همین دلیله که بحث AV و EDR Evasion فقط عوض کردن یک API نیست
سیستم های دفاعی جدید سعی میکنن اثر و نتیجه عملیات رو ببینن
حتی اگر مسیر انجام اون عملیات تغییر کرده باشه

@reverseengine
ReverseEngineering
Thread Analysis و Start Address بعضی وقتا خود Process چیز خاصی نشون نمیده ولی Thread هاش داستان اصلی رو لو میدن تا اینجا دیدیم که EDR فقط اسم فایل یا Process رو نگاه نمیکنه یکی از چیزایی که عمیق تر بررسی میشه Thread های داخل Process هستن هر Process میتونه…
Thread Analysis and Start Address

Sometimes the Process itself doesn't show anything special, but its Threads reveal the real story

So far we have seen that EDR doesn't just look at the file name or Process

One of the things that is examined more deeply is the Threads inside the Process

Each Process can have several Threads

Each Thread has its own execution path

Therefore, when EDR or an analyst wants to understand what exactly is going on inside a Process, examining the Threads can give him important information

Start Address
Very simply, each Thread must start from a point to execute

That point is called the Start Address
That is, EDR can check where in memory the Thread started executing

For example, the Start Address may be inside a known DLL

Or it may be inside an unknown part of memory

If it is inside a regular DLL, the Context must still be checked

If it is inside an unknown part of memory, it may look more suspicious

But there is a very important point here

Every unknown Memory Region is not necessarily malicious

Some programs generate code themselves when they run or map memory in a special way They manage

For example, JIT Compilers, browsers, and some runtimes may have behaviors that may seem strange from the outside
Because humans obviously don't accept a simple False Positive and prefer the security system to kill their healthy program sometim😁

EDR
doesn't just look at the Start Address
In real analysis, several things are usually checked together
First, when the Thread was created
For example, a program is executed
A few minutes later, an unusual event occurs in memory
Immediately after that, a new Thread is created
The timing of events is important here
Because an Event alone may not be anything special, but when several Events occur in succession, the story is different
Second, who caused the Thread to be created
For example, a Process communicates with another Process
Then a new Thread appears inside that Process
This connection between the two Processes can be very important for Detection
Sometimes the Thread itself is not strange, but the events that occurred before its creation are suspicious
Third, where exactly the Thread is executed

EDR
can look at the Memory Region around the Start Check the address
For example, this memory may be related to a Module
It may be Private Memory
It may be related to an Image
Or another Memory Region
For example, if a Thread starts executing from a Private Executable Region, it may need further investigation
Especially if an unusual event has occurred on the same part of memory before that
The fourth is the Call Stack
The Call Stack
Can show to some extent the path and functions through which the Thread reached its current state
That is, EDR does not only see the current point of the Thread and may also examine the path to reach that point
If the execution path does not match the usual behavior of that program, it can be a suspicious sign
But still, a strange Call Stack alone means that the program is not malicious
Debugging
Instrumentation
And various runtimes can also create structures that do not seem normal
Detection mental model
Suppose this event is only seen
A new Thread is created
It does not give us much information on its own
But now suppose these events happened one after the other
Process A is executed
Then It communicates with Process B
A memory event occurs inside Process B
Then an executable appears in memory
Then a Thread starts executing
Then network activity starts
Here the situation is different
Because EDR does not see just one event
It sees the relationship between events
And this is the main point

EDR
is not looking for a magic Event
It looks for the relationship between Events
Why are Threads important to the analyst
Because they can establish a connection between the changes that occurred in memory and the actual execution
ReverseEngineering
Thread Analysis و Start Address بعضی وقتا خود Process چیز خاصی نشون نمیده ولی Thread هاش داستان اصلی رو لو میدن تا اینجا دیدیم که EDR فقط اسم فایل یا Process رو نگاه نمیکنه یکی از چیزایی که عمیق تر بررسی میشه Thread های داخل Process هستن هر Process میتونه…
For example, a part of memory may have changed but it is not yet clear whether this change was actually used or not
But if a new execution path appears later and a Thread starts running from that path, the case becomes more meaningful for the analyst
To analyze the behavior of a Process, you need to look at these things together

Process Tree
Cross Process Access
Memory Regions
Memory Permission Changes
Thread Start Address
Call Stack
Timeline


None of these are enough on their own
But when they are all put together, the detection is stronger
❤1