Unmasking EvilTokens: Getting to the root of device code phishing
https://www.microsoft.com/en-us/security/blog/2026/09/22/unmasking-eviltokens-getting-to-the-root-of-device-code-phishing/
https://www.microsoft.com/en-us/security/blog/2026/09/22/unmasking-eviltokens-getting-to-the-root-of-device-code-phishing/
Microsoft News
Unmasking EvilTokens: Getting to the root of device code phishing
EvilTokens has quickly become one of the top PhaaS platforms, enabling device code phishing attacks through AI-assisted lures, automated infrastructure, and token theft. In collaboration with partners, Microsoft Digital Crimes Unit (DCU) facilitated a disruption…
ClickLock Stealer: Paste Once, Lose Everything
https://www.group-ib.com/blog/clicklock-stealer-macos-malware
https://www.group-ib.com/blog/clicklock-stealer-macos-malware
Group-IB
ClickLock Stealer: Paste Once, Lose Everything
Analysis of ClickLock, a modular macOS stealer delivered via ClickFix that uses fake dialogs, kill loops, and a GSocket backdoor to steal passwords, browser data, and crypto wallets.
❤1
باز سازی Inheritance و Multiple Inheritance در Assembly
تا اینجا یاد گرفتیم چطور از روی Memory Access ها و Offset ها یک Struct ناشناخته رو بازسازی کنیم
ولی وقتی با برنامه های + +C طرف میشیم قضیه یک مقدار پیچیده تر میشه
چون ممکنه یک Object فقط چند Field ساده نداشته باشه
ممکنه:
Class
↓
Inheritance
↓
Virtual Functions
↓
vptr
↓
vtable
داشته باشیم
اینجاست که باید از روی Assembly بفهمیم:
این Object چه Class ای هست؟
از چه Class ای ارثبری کرده؟
Virtual Function
ها کجا قرار دارن؟
و Offset های مختلف مربوط به کدوم بخش Object هستن؟
Virtual Function
رو بفهمیم
فرض کنید Source Code این شکلی باشه:
چون "speak" یک Virtual Function هست Compiler معمولا برای این Object یک Pointer به جدول Virtual Function ها در نظر میگیره
به این Pointer معمولا میگیم:
vptr
و جدول:
vtable
به شکل مفهومی:
Object
│
├── vptr
│ │
│ ▼
│ vtable
│ │
│ ├── speak()
│ ├── ...
│
└── Other Fields
یعنی Object معمولا یک Pointer داره که به اطلاعات Virtual Functionهای مربوط به نوع Dynamic Object اشاره میکنه
البته جزئیات دقیق Layout به Compiler و ABI وابسته است.
داخل Assembly دنبال چه چیزی بگردیم؟
فرض کنید داخل Constructor چنین چیزی ببینیم:
mov qword ptr [rcx], offset vtable_Dog
اینجا یک سرنخ خیلی مهم داریم
یعنی مقدار ابتدای Object تنظیم شده:
Object + 0x00
↓
vptr
↓
vtable_Dog
پس میتونیم فعلا بگیم:
Dog Object
+0x00 vptr
+0x08 unknown
+0x10 unknown
هنوز نمیدونیم Field های بعدی چی هستن
ولی حداقل یک چیز مهم فهمیدیم:
+0x00
احتمالا vptr است
vtable
دقیقا چیه؟
خیلی ساده اگر بخوایم نگاهش کنیم:
vptr
│
▼
vtable
│
├── Function Pointer
├── Function Pointer
├── Function Pointer
└──
هر Entry در vtable معمولا یک Pointer به یک Function یا بخشی از مکانیزم Virtual Dispatch هست
مثلا فرض کنید Assembly این شکلی باشه:
بیایم مرحله به مرحله نگاه کنیم
اول:
]
یعنی:
RCX
↓
Object
↓
+0x00
↓
vptr
بعد:
]
یعنی از جدول مربوط به vptr یک Entry در Offset مشخص خونده شده
بعد:
call rax
یعنی اون مقدار برای Call استفاده شده
پس یک مسیر مفهومی داریم:
Object
↓
+0x00
↓
vptr
↓
vtable
↓
Function Pointer
↓
call
این یکی از الگوهای خیلی مهم در Reverse Engineering برنامههای C++ هست.
از روی این الگو چطور Virtual Function رو پیدا کنیم؟
فرض کنید این Assembly رو داریم:
میتونیم فعلا این مدل رو بسازیم:
Object
|
+0x00 → vptr
|
↓
vtable
|
+0x18 → function pointer
اما یک نکته مهم:
نباید سریع بگیم:
+0x18 = Function X
چون هنوز نمیدونیم Entry دقیقا مربوط به چه Virtual Function ای هست
باید خود vtable و Reference های مربوط به اون رو بررسی کنیم
حالا Inheritance وارد میشه
فرض کنید داریم:
از نظر مفهومی:
Animal
↑
|
Dog
Dog
بخشهایی از Animal رو به ارث میبره
ممکنه Object چیزی شبیه این داشته باشه:
Dog Object
+0x00 vptr
+0x08 Animal-related field
+0x0C Dog-related field
این Layout دقیق نیست و صرفا یک مدل مفهومیه
چون Layout واقعی به Compiler، ABI، Alignment و نوع Inheritance بستگی داره
ولی نکته مهم اینه که:
Base Class
+
Derived Class
میتونن در یک Object واحد حضور داشته باشن
از روی Constructor میتونیم رابطه رو پیدا کنیم
فرض کن یک Constructor داریم:
و بعد در Constructor کلاس دیگه ای میبینیم:
call Animal::Animal
mov qword ptr [rcx], offset vtable_Dog
اینجا یک سرنخ خیلی مهم داریم
یعنی Constructor مربوط به Dog ابتدا بخش مربوط به Base Class رو مقدار دهی میکنه و بعد وضعیت Virtual Dispatch مربوط به Derived Class رو تنظیم میکنه
به شکل مفهومی:
Dog Constructor
|
├── Animal Constructor
|
└── Dog-specific initialization
این یکی از چیزهایی هست که میتونه به ما کمک کنه رابطه بین Class ها رو پیدا کنیم
@reverseengine
تا اینجا یاد گرفتیم چطور از روی Memory Access ها و Offset ها یک Struct ناشناخته رو بازسازی کنیم
ولی وقتی با برنامه های + +C طرف میشیم قضیه یک مقدار پیچیده تر میشه
چون ممکنه یک Object فقط چند Field ساده نداشته باشه
ممکنه:
Class
↓
Inheritance
↓
Virtual Functions
↓
vptr
↓
vtable
داشته باشیم
اینجاست که باید از روی Assembly بفهمیم:
این Object چه Class ای هست؟
از چه Class ای ارثبری کرده؟
Virtual Function
ها کجا قرار دارن؟
و Offset های مختلف مربوط به کدوم بخش Object هستن؟
Virtual Function
رو بفهمیم
فرض کنید Source Code این شکلی باشه:
class Animal {
public:
virtual void speak();
};
و:
class Dog : public Animal {
public:
void speak() override;
};
چون "speak" یک Virtual Function هست Compiler معمولا برای این Object یک Pointer به جدول Virtual Function ها در نظر میگیره
به این Pointer معمولا میگیم:
vptr
و جدول:
vtable
به شکل مفهومی:
Object
│
├── vptr
│ │
│ ▼
│ vtable
│ │
│ ├── speak()
│ ├── ...
│
└── Other Fields
یعنی Object معمولا یک Pointer داره که به اطلاعات Virtual Functionهای مربوط به نوع Dynamic Object اشاره میکنه
البته جزئیات دقیق Layout به Compiler و ABI وابسته است.
داخل Assembly دنبال چه چیزی بگردیم؟
فرض کنید داخل Constructor چنین چیزی ببینیم:
mov qword ptr [rcx], offset vtable_Dog
اینجا یک سرنخ خیلی مهم داریم
یعنی مقدار ابتدای Object تنظیم شده:
Object + 0x00
↓
vptr
↓
vtable_Dog
پس میتونیم فعلا بگیم:
Dog Object
+0x00 vptr
+0x08 unknown
+0x10 unknown
هنوز نمیدونیم Field های بعدی چی هستن
ولی حداقل یک چیز مهم فهمیدیم:
+0x00
احتمالا vptr است
vtable
دقیقا چیه؟
خیلی ساده اگر بخوایم نگاهش کنیم:
vptr
│
▼
vtable
│
├── Function Pointer
├── Function Pointer
├── Function Pointer
└──
هر Entry در vtable معمولا یک Pointer به یک Function یا بخشی از مکانیزم Virtual Dispatch هست
مثلا فرض کنید Assembly این شکلی باشه:
mov rax, [rcx]
mov rax, [rax+0x10]
call rax
بیایم مرحله به مرحله نگاه کنیم
اول:
mov rax, [rcx
]
یعنی:
RCX
↓
Object
↓
+0x00
↓
vptr
بعد:
mov rax, [rax+0x10
]
یعنی از جدول مربوط به vptr یک Entry در Offset مشخص خونده شده
بعد:
call rax
یعنی اون مقدار برای Call استفاده شده
پس یک مسیر مفهومی داریم:
Object
↓
+0x00
↓
vptr
↓
vtable
↓
Function Pointer
↓
call
این یکی از الگوهای خیلی مهم در Reverse Engineering برنامههای C++ هست.
از روی این الگو چطور Virtual Function رو پیدا کنیم؟
فرض کنید این Assembly رو داریم:
mov rax, [rcx]
mov rax, [rax+0x18]
call rax
میتونیم فعلا این مدل رو بسازیم:
Object
|
+0x00 → vptr
|
↓
vtable
|
+0x18 → function pointer
اما یک نکته مهم:
نباید سریع بگیم:
+0x18 = Function X
چون هنوز نمیدونیم Entry دقیقا مربوط به چه Virtual Function ای هست
باید خود vtable و Reference های مربوط به اون رو بررسی کنیم
حالا Inheritance وارد میشه
فرض کنید داریم:
class Animal {
public:
virtual void speak();
};
class Dog : public Animal {
public:
void speak() override;
};
از نظر مفهومی:
Animal
↑
|
Dog
Dog
بخشهایی از Animal رو به ارث میبره
ممکنه Object چیزی شبیه این داشته باشه:
Dog Object
+0x00 vptr
+0x08 Animal-related field
+0x0C Dog-related field
این Layout دقیق نیست و صرفا یک مدل مفهومیه
چون Layout واقعی به Compiler، ABI، Alignment و نوع Inheritance بستگی داره
ولی نکته مهم اینه که:
Base Class
+
Derived Class
میتونن در یک Object واحد حضور داشته باشن
از روی Constructor میتونیم رابطه رو پیدا کنیم
فرض کن یک Constructor داریم:
mov qword ptr [rcx], offset vtable_Animal
و بعد در Constructor کلاس دیگه ای میبینیم:
call Animal::Animal
mov qword ptr [rcx], offset vtable_Dog
اینجا یک سرنخ خیلی مهم داریم
یعنی Constructor مربوط به Dog ابتدا بخش مربوط به Base Class رو مقدار دهی میکنه و بعد وضعیت Virtual Dispatch مربوط به Derived Class رو تنظیم میکنه
به شکل مفهومی:
Dog Constructor
|
├── Animal Constructor
|
└── Dog-specific initialization
این یکی از چیزهایی هست که میتونه به ما کمک کنه رابطه بین Class ها رو پیدا کنیم
@reverseengine
ReverseEngineering
باز سازی Inheritance و Multiple Inheritance در Assembly تا اینجا یاد گرفتیم چطور از روی Memory Access ها و Offset ها یک Struct ناشناخته رو بازسازی کنیم ولی وقتی با برنامه های + +C طرف میشیم قضیه یک مقدار پیچیده تر میشه چون ممکنه یک Object فقط چند Field…
Reconstructing Inheritance and Multiple Inheritance in Assembly
So far we have learned how to reconstruct an unknown Struct from Memory Accesses and Offsets
But when we deal with C++ programs, the matter becomes a bit more complicated
Because an Object may not have just a few simple fields
We may have:
Class
↓
Inheritance
↓
Virtual Functions
↓
vptr
↓
vtable
This is where we need to understand from the Assembly:
What Class is this Object?
What Class does it inherit from?
Where are the Virtual Functions
And which part of the Object do the different Offsets belong to?
Understand Virtual Functions
Suppose the Source Code is like this:
class Animal {
public:
virtual void speak();
};
And:
class Dog : public Animal {
public:
void speak() override;
};
Because "speak" is a Virtual Function, the Compiler usually considers a Pointer to the Virtual Function Table for this Object
We usually call this Pointer:
vptr
and the table:
vtable
Conceptually:
Object
│
├── vptr
│ │
│ ▼
│ vtable
│ │
│ ├── speak()
│ ├── ...
│
└── Other Fields
That is, the Object usually has a Pointer that points to the Virtual Function information related to the Dynamic Object type
Of course, the exact details of the Layout depend on the Compiler and ABI.
What should we look for inside the Assembly?
Suppose we see something like this inside the Constructor:
mov qword ptr [rcx], offset vtable_Dog
Here we have a very important clue
That is, the initial value of Object is set:
Object + 0x00
↓
vptr
↓
vtable_Dog
So we can say for now:
Dog Object
+0x00 vptr
+0x08 unknown
+0x10 unknown
We still don't know what the next fields are
But at least we know one important thing:
+0x00
is probably vptr
What exactly is vtable?
Very simply, if we want to look at it:
vptr
│
▼
vtable
│
├── Function Pointer
├── Function Pointer
├── Function Pointer
└──
Each entry in the vtable is usually a pointer to a function or part of the Virtual Dispatch mechanism
For example, suppose the assembly looks like this:
Let's look at it step by step
First:
mov rax, [rcx
]
That is:
RCX
↓
Object
↓
+0x00
↓
vptr
Then:
That is, an entry at a specific offset is read from the vptr table
Then:
call rax
That is, that value is used for Call
So we have a conceptual path:
Object
↓
+0x00
↓
vptr
↓
vtable
↓
Function Pointer
↓
call
This is one of the most important patterns in Reverse Engineering C++ programs.
How do we find the Virtual Function from this pattern?
Suppose we have this Assembly:
We can create this model for now:
Object
|
+0x00 → vptr
|
↓
vtable
|
+0x18 → function pointer
But one important point:
We should not say quickly:
+0x18 = Function X
Because we still do not know exactly what Virtual Function Entry is related to
We should check the vtable itself and its References
Now Inheritance comes in
Suppose we have:
class Animal {
public:
virtual void speak();
};
class Dog : public Animal {
public:
void speak() override;
};
Conceptually:
Animal
↑
|
Dog
Dog
inherits parts of Animal
The Object might look something like this:
Dog Object
+0x00 vptr
+0x08 Animal-related field
+0x0C Dog-related field
This Layout is not exact and is just a conceptual model
Because the actual Layout depends on the Compiler, ABI, Alignment and Inheritance type
But the important point is that:
Base Class
+
Derived Class
can be present in a single Object
We can find the relationship from the Constructor
Suppose we have a Constructor:
mov qword ptr [rcx], offset vtable_Animal
And then in the Constructor we see another class:
call Animal::Animal
mov qword ptr [rcx], offset vtable_Dog
Here we have a very important clue
That is, the Constructor related to Dog first initializes the part related to the Base Class and then the Virtual Dispatch state related to Sets the Derived Class
Conceptually:
Dog Constructor
|
├── Animal Constructor
|
└── Dog-specific initialization
So far we have learned how to reconstruct an unknown Struct from Memory Accesses and Offsets
But when we deal with C++ programs, the matter becomes a bit more complicated
Because an Object may not have just a few simple fields
We may have:
Class
↓
Inheritance
↓
Virtual Functions
↓
vptr
↓
vtable
This is where we need to understand from the Assembly:
What Class is this Object?
What Class does it inherit from?
Where are the Virtual Functions
And which part of the Object do the different Offsets belong to?
Understand Virtual Functions
Suppose the Source Code is like this:
class Animal {
public:
virtual void speak();
};
And:
class Dog : public Animal {
public:
void speak() override;
};
Because "speak" is a Virtual Function, the Compiler usually considers a Pointer to the Virtual Function Table for this Object
We usually call this Pointer:
vptr
and the table:
vtable
Conceptually:
Object
│
├── vptr
│ │
│ ▼
│ vtable
│ │
│ ├── speak()
│ ├── ...
│
└── Other Fields
That is, the Object usually has a Pointer that points to the Virtual Function information related to the Dynamic Object type
Of course, the exact details of the Layout depend on the Compiler and ABI.
What should we look for inside the Assembly?
Suppose we see something like this inside the Constructor:
mov qword ptr [rcx], offset vtable_Dog
Here we have a very important clue
That is, the initial value of Object is set:
Object + 0x00
↓
vptr
↓
vtable_Dog
So we can say for now:
Dog Object
+0x00 vptr
+0x08 unknown
+0x10 unknown
We still don't know what the next fields are
But at least we know one important thing:
+0x00
is probably vptr
What exactly is vtable?
Very simply, if we want to look at it:
vptr
│
▼
vtable
│
├── Function Pointer
├── Function Pointer
├── Function Pointer
└──
Each entry in the vtable is usually a pointer to a function or part of the Virtual Dispatch mechanism
For example, suppose the assembly looks like this:
mov rax, [rcx]
mov rax, [rax+0x10]
call rax
Let's look at it step by step
First:
mov rax, [rcx
]
That is:
RCX
↓
Object
↓
+0x00
↓
vptr
Then:
mov rax, [rax+0x10
]
That is, an entry at a specific offset is read from the vptr table
Then:
call rax
That is, that value is used for Call
So we have a conceptual path:
Object
↓
+0x00
↓
vptr
↓
vtable
↓
Function Pointer
↓
call
This is one of the most important patterns in Reverse Engineering C++ programs.
How do we find the Virtual Function from this pattern?
Suppose we have this Assembly:
mov rax, [rcx]
mov rax, [rax+0x18]
call rax
We can create this model for now:
Object
|
+0x00 → vptr
|
↓
vtable
|
+0x18 → function pointer
But one important point:
We should not say quickly:
+0x18 = Function X
Because we still do not know exactly what Virtual Function Entry is related to
We should check the vtable itself and its References
Now Inheritance comes in
Suppose we have:
class Animal {
public:
virtual void speak();
};
class Dog : public Animal {
public:
void speak() override;
};
Conceptually:
Animal
↑
|
Dog
Dog
inherits parts of Animal
The Object might look something like this:
Dog Object
+0x00 vptr
+0x08 Animal-related field
+0x0C Dog-related field
This Layout is not exact and is just a conceptual model
Because the actual Layout depends on the Compiler, ABI, Alignment and Inheritance type
But the important point is that:
Base Class
+
Derived Class
can be present in a single Object
We can find the relationship from the Constructor
Suppose we have a Constructor:
mov qword ptr [rcx], offset vtable_Animal
And then in the Constructor we see another class:
call Animal::Animal
mov qword ptr [rcx], offset vtable_Dog
Here we have a very important clue
That is, the Constructor related to Dog first initializes the part related to the Base Class and then the Virtual Dispatch state related to Sets the Derived Class
Conceptually:
Dog Constructor
|
├── Animal Constructor
|
└── Dog-specific initialization
Private Memory و
Image-backed Memory و
VAD
تا اینجا درباره RW → RX صحبت کردیم
این Memory اصلا متعلق به چیه؟
وقتی یک Process رو بررسی میکنید همه Memory هایی که میبینید یک جنس نیستن
برای تحلیل Memory-based Behavior باید بدونیم هر Region چطور ساخته شده و چه رابطهای با فایل روی دیسک داره
Image-backed Memory
وقتی یک PE مثل:
notepad.exe
ntdll.dll
kernel32.dll
داخل Process قرار میگیره بخش هایی از Virtual Address Space به یک Image File روی دیسک مرتبط میشن
بهصورت مفهومی:
PE File
│
▼
Image Mapping
│
▼
Process Virtual Address Space
مثلا یک Executable ممکنه Section هایی مثل این داشته باشه:
notepad.exe
├── .text
├── .rdata
├── .data
└── ...
در Memory این بخشها معمولا به Image مربوط به همان Module مرتبط هستن پس وقتی یک Region از نوع Image باشه یکی از سوالهای مهم اینه:
این Region به کدوم Module روی دیسک مربوطه؟
Private Memory
نوع دیگری از Memory به شکل Private ایجاد میشه
این Memory مستقیما یک Image Mapping از فایل PE مشخص نیست
مثلا Process ممکنه برای موارد مختلف از Private Memory استفاده کنه:
Process
│
├── Image-backed Memory
│
└── Private Memory
├── Heap
├── Runtime Data
├── Temporary Allocations
└── Other Private Allocations
نکته مهم:
Private Memory ≠ Malware
برنامه های کاملا Legitimate هم دائما از Private Memory استفاده میکنن
پس صرفا دیدن
Type = Private
هیچ چیزی رو اثبات نمیکنه
پس چرا Private Memory برای تحلیل امنیتی مهمه؟
چون باید چند ویژگی رو کنار هم بررسی کنیم
مثلا:
Private Memory
+
Executable Permission
+
Unusual Thread
+
Unexpected Call Stack
هر کدوم به تنهایی میتونه کاملا طبیعی باشه
ولی وقتی چند Evidence کنار هم قرار میگیرن ارزش Investigation بیشتر میشه
مثلا یک Region:
Private
RX
به تنهایی نمیگه:
Malicious
اما میتونه یک نقطه مناسب برای بررسی بیشتر باشه
تحلیلگر باید Context رو بررسی کنه
VAD
حالا میرسیم به VAD
Virtual Address Descriptor
VAD
یکی از ساختارهای داخلی
Windows
برای توصیف محدودههای یک Virtual Address Space Process هست
به صورت مفهومی:
Process Virtual Address Space
0x0000
│
├── Region A
├── Region B
├── Region C
├── Region D
└── ...
0xFFFF
VAD ها به Kernel کمک میکنن اطلاعات
مربوط به محدوده های Virtual Memory یک Process رو مدیریت و توصیف کنه
مثلا اینکه یک محدوده کجاست و چه ویژگی هایی داره
Memory Region
فقط یک Address نیست
فرض کنید در Debugger یا Memory Analysis چنین چیزی ببینید:
Address: 0x7...
Size: 0x...
Protection: RX
Type: Private
اگر فقط Address رو ببینید اطلاعات زیادی از دست میدید
باید چند تا سوال بپرسید:
این Region کجاست؟
↓
چه اندازهای داره؟
↓
Protection اون چیه؟
↓
Private
هشت یا Image؟
↓
به چه Mapping یا Allocation ای مربوطه؟
↓
چه Thread هایی به اون دسترسی یا اجرای مرتبط دارن؟
↓
در طول زمان چه اتفاقی برای اون افتاده؟
اینجا Memory Analysis از نگاه کردن به چند Hex Value تبدیل میشه به تحلیل رفتار
VAD در Memory Forensics
در Memory Forensics میتونیم ساختار های Virtual Memory مربوط به Process رو بررسی کنیم
بهصورت مفهومی:
Process
│
├── Image
│ ├── .text
│ └── .data
│
├── DLLs
│
├── Heap
│
└── Private Regions
حالا فرض کنید یک Region پیدا کردیم که نیاز به بررسی بیشتری داره
میتونیم بپرسیم:
Protection چیه؟
↓
Private هست یا Image?
↓
به Module شناختهشده ای مرتبطه؟
↓
محتواش چه شکلیه؟
↓
Thread ی با اون ارتباط داره
↓
Call Stack چه چیزی نشون میده؟
↓
وضعیت Region در طول زمان تغییر کرده؟
نکته مهم اینه که VAD بهتنهایی جواب همه این سؤالها رو نمیده
VAD
یکی از منابع Evidence در تحلیل Memory هست
یک سناریوی تحلیلی
فرض کنید داخل یک Memory Dump این وضعیت رو داریم:
Process: example.exe
Region A
Image
RX
Region B
Private
RW
Region C
Private
RX
Region A خیلی عادی به نظر میرسه
چون به یک Image مربوطه و Permission اون هم برای اجرای Code قابل انتظاره
Image-backed Memory و
VAD
تا اینجا درباره RW → RX صحبت کردیم
این Memory اصلا متعلق به چیه؟
وقتی یک Process رو بررسی میکنید همه Memory هایی که میبینید یک جنس نیستن
برای تحلیل Memory-based Behavior باید بدونیم هر Region چطور ساخته شده و چه رابطهای با فایل روی دیسک داره
Image-backed Memory
وقتی یک PE مثل:
notepad.exe
ntdll.dll
kernel32.dll
داخل Process قرار میگیره بخش هایی از Virtual Address Space به یک Image File روی دیسک مرتبط میشن
بهصورت مفهومی:
PE File
│
▼
Image Mapping
│
▼
Process Virtual Address Space
مثلا یک Executable ممکنه Section هایی مثل این داشته باشه:
notepad.exe
├── .text
├── .rdata
├── .data
└── ...
در Memory این بخشها معمولا به Image مربوط به همان Module مرتبط هستن پس وقتی یک Region از نوع Image باشه یکی از سوالهای مهم اینه:
این Region به کدوم Module روی دیسک مربوطه؟
Private Memory
نوع دیگری از Memory به شکل Private ایجاد میشه
این Memory مستقیما یک Image Mapping از فایل PE مشخص نیست
مثلا Process ممکنه برای موارد مختلف از Private Memory استفاده کنه:
Process
│
├── Image-backed Memory
│
└── Private Memory
├── Heap
├── Runtime Data
├── Temporary Allocations
└── Other Private Allocations
نکته مهم:
Private Memory ≠ Malware
برنامه های کاملا Legitimate هم دائما از Private Memory استفاده میکنن
پس صرفا دیدن
Type = Private
هیچ چیزی رو اثبات نمیکنه
پس چرا Private Memory برای تحلیل امنیتی مهمه؟
چون باید چند ویژگی رو کنار هم بررسی کنیم
مثلا:
Private Memory
+
Executable Permission
+
Unusual Thread
+
Unexpected Call Stack
هر کدوم به تنهایی میتونه کاملا طبیعی باشه
ولی وقتی چند Evidence کنار هم قرار میگیرن ارزش Investigation بیشتر میشه
مثلا یک Region:
Private
RX
`به تنهایی نمیگه:
Malicious
اما میتونه یک نقطه مناسب برای بررسی بیشتر باشه
تحلیلگر باید Context رو بررسی کنه
VAD
حالا میرسیم به VAD
Virtual Address Descriptor
VAD
یکی از ساختارهای داخلی
Windows
برای توصیف محدودههای یک Virtual Address Space Process هست
به صورت مفهومی:
Process Virtual Address Space
0x0000
│
├── Region A
├── Region B
├── Region C
├── Region D
└── ...
0xFFFF
VAD ها به Kernel کمک میکنن اطلاعات
مربوط به محدوده های Virtual Memory یک Process رو مدیریت و توصیف کنه
مثلا اینکه یک محدوده کجاست و چه ویژگی هایی داره
Memory Region
فقط یک Address نیست
فرض کنید در Debugger یا Memory Analysis چنین چیزی ببینید:
Address: 0x7...
Size: 0x...
Protection: RX
Type: Private
اگر فقط Address رو ببینید اطلاعات زیادی از دست میدید
باید چند تا سوال بپرسید:
این Region کجاست؟
↓
چه اندازهای داره؟
↓
Protection اون چیه؟
↓
Private
هشت یا Image؟
↓
به چه Mapping یا Allocation ای مربوطه؟
↓
چه Thread هایی به اون دسترسی یا اجرای مرتبط دارن؟
↓
در طول زمان چه اتفاقی برای اون افتاده؟
اینجا Memory Analysis از نگاه کردن به چند Hex Value تبدیل میشه به تحلیل رفتار
VAD در Memory Forensics
در Memory Forensics میتونیم ساختار های Virtual Memory مربوط به Process رو بررسی کنیم
بهصورت مفهومی:
Process
│
├── Image
│ ├── .text
│ └── .data
│
├── DLLs
│
├── Heap
│
└── Private Regions
حالا فرض کنید یک Region پیدا کردیم که نیاز به بررسی بیشتری داره
میتونیم بپرسیم:
Protection چیه؟
↓
Private هست یا Image?
↓
به Module شناختهشده ای مرتبطه؟
↓
محتواش چه شکلیه؟
↓
Thread ی با اون ارتباط داره
↓
Call Stack چه چیزی نشون میده؟
↓
وضعیت Region در طول زمان تغییر کرده؟
نکته مهم اینه که VAD بهتنهایی جواب همه این سؤالها رو نمیده
VAD
یکی از منابع Evidence در تحلیل Memory هست
یک سناریوی تحلیلی
فرض کنید داخل یک Memory Dump این وضعیت رو داریم:
Process: example.exe
Region A
Image
RX
Region B
Private
RW
Region C
Private
RX
Region A خیلی عادی به نظر میرسه
چون به یک Image مربوطه و Permission اون هم برای اجرای Code قابل انتظاره
Region B
هم میتونه کاملا طبیعی باشه
چون Private RW داخل Process ها بسیار رایجه
اما Region C:
Private
RX
ارزش بررسی بیشتری پیدا میکنه
حالا اطلاعات بیشتری به دست میاریم:
Region C
↓
Private
↓
RX
↓
Thread Start Address → داخل Region
↓
Call Stack → غیرمعمول
اینجا Region C تبدیل میشه به یک Investigation Target
ولی هنوز نمیگیم:
Malware Found
چون ممکنه توضیح Legitimate دیگه ای وجود داشته باشه
مثلا Runtimeها JIT Compilatio، Instrumentation یا بعضی تکنیک های نرمافزاری هم میتونن Memory Executable ایجاد کنن
پس:
Suspicious
≠
Malicious
Permission فقط یک تکیه از پازله
تا اینجا درباره Permission زیاد صحبت کردیم
مثلا:
RW
RX
RWX
ولی یک تحلیل درست فقط Permission رو نگاه نمیکنه
مثلا این دو Region رو مقایسه کنید:
Region A
Image
RX
Known Module
Normal Thread Activity
و:
Region B
Private
RX
Unknown Mapping
Unusual Thread Start
Unexpected Call Stack
هر دو ممکنه Executable باشن
ولی Context کاملا متفاوتی دارن
پس یک مدل بهتر اینه:
Memory Type
+
Protection
+
Content
+
Thread
+
Call Stack
+
Module Relationship
+
Timeline
هرچه Evidence ها به یک داستان مشترک اشاره کنن Confidence تحلیل بالاتر میره
چرا Timeline مهمه؟
فرض کنید یک Region الان این شکلیه:
Private
RX
فقط با دیدن وضعیت فعلی نمیدونیم قبلش چه اتفاقی افتاده
ممکنه اول:
RW
بوده و بعد Permission اون تغییر کرده باشه
یا از اول:
RX
ایجاد شده باشه
یا یک Runtime legitimate اون رو ایجاد کرده باشه
پس اگر Telemetry کافی داشته باشیم
Timeline
میتونه خیلی مهم باشه:
Allocation
↓
Memory Write
↓
Protection Change
↓
Thread Creation
↓
Execution
این زنجیره از یک Snapshot ساده اطلاعات بیشتری میده
از دید Detection
حالا میتونیم چند Evidence رو کنار هم بذاریم:
Process
│
├── Memory Region
│ ├── Private
│ └── Executable
│
├── Thread
│ └── Start Address → Region
│
└── Call Stack
└── Unexpected Frames
هیچکدوم به تنهایی لزوما نشونه حمله نیست
ولی ارتباط بین اونها میتونه برای Detection مهم باشه
این دقیقا یکی از ایدههای اصلی
Behavioral Detection
هست:
یک Event
↓
ممکنه عادی باشه
چند Event مرتبط
↓
Context
↓
Behavior
از دید ردتیمرا
از دید مهاجم هم این قسمت یک درس مهم داره
Memory هم Telemetry
ایجاد میکنه
یعنی این تصور:
No File
=
No Detection
اشتباهه چون ممکنه فایل واضحی روی Disk وجود نداشته باشه ولی وضعیت Process و Memory همچنان Evidence ایجاد کنه
مدل کامل تر:
Disk
+
Process
+
Memory
+
Thread
+
Call Stack
+
Timeline
↓
Behavioral Evidence
پس درک Memory فقط برای کسی که Detection مینویسه مهم نیست
برای Red Team هم مهمه که بدونه کد و Data بعد از ورود به Process چه ردپای قابل مشاهده ای ایجاد میکنن
یک نکته مهم درباره Private و Image
این دو دسته رو بیش از حد ساده نکنیم
در Windows Memory Management نوع یک Region فقط با این جمله تعیین نمیشه که:
Private = چیزی که برنامه خودش ساخته
Image =
چیزی که حتما مستقیم از Disk خونده شده
جزئیات Memory Manager Section Objectها Mappingها و نوع Allocation مهم هستن
پس وقتی در ابزارهای Forensics یا Debugger چیزی مثل:
`
Type = Private
میبینید باید دقیقا بدونید اون ابزار بر اساس چه اطلاعاتی این Classification رو انجام داده
هدف ما در RE و Memory Forensics این نیست که یک Label رو حفظ کنیم
هدف اینه که بدونیم پشت اون Label چه Memory Behavior ای وجود داره
API / Syscall
↓
Memory Allocation
↓
Memory Protection
↓
Memory Type
↓
Thread
↓
Call Stack
↓
VAD
↓
Behavioral Analysis
سه مفهوم رو فعلا خوب نگه دارید:
Image-backed Memory
↓
Memory
مرتبط با Image Mapping
Private Memory
↓
Memory
که به یک Image Mapping معمولی وابسته نیست
VAD
↓
ساختارهای Kernel برای توصیف محدوده های Virtual Address Space
و مهمتر از همه:
Private
≠
Malicious
Executable
≠
Malicious
RW → RX
≠
Malicious
چیزی که مهمه ترکیب Evidence ها و Context هست
@reverseengine
هم میتونه کاملا طبیعی باشه
چون Private RW داخل Process ها بسیار رایجه
اما Region C:
Private
RX
ارزش بررسی بیشتری پیدا میکنه
حالا اطلاعات بیشتری به دست میاریم:
Region C
↓
Private
↓
RX
↓
Thread Start Address → داخل Region
↓
Call Stack → غیرمعمول
اینجا Region C تبدیل میشه به یک Investigation Target
ولی هنوز نمیگیم:
Malware Found
چون ممکنه توضیح Legitimate دیگه ای وجود داشته باشه
مثلا Runtimeها JIT Compilatio، Instrumentation یا بعضی تکنیک های نرمافزاری هم میتونن Memory Executable ایجاد کنن
پس:
Suspicious
≠
Malicious
Permission فقط یک تکیه از پازله
تا اینجا درباره Permission زیاد صحبت کردیم
مثلا:
RW
RX
RWX
ولی یک تحلیل درست فقط Permission رو نگاه نمیکنه
مثلا این دو Region رو مقایسه کنید:
Region A
Image
RX
Known Module
Normal Thread Activity
و:
Region B
Private
RX
Unknown Mapping
Unusual Thread Start
Unexpected Call Stack
هر دو ممکنه Executable باشن
ولی Context کاملا متفاوتی دارن
پس یک مدل بهتر اینه:
Memory Type
+
Protection
+
Content
+
Thread
+
Call Stack
+
Module Relationship
+
Timeline
هرچه Evidence ها به یک داستان مشترک اشاره کنن Confidence تحلیل بالاتر میره
چرا Timeline مهمه؟
فرض کنید یک Region الان این شکلیه:
Private
RX
فقط با دیدن وضعیت فعلی نمیدونیم قبلش چه اتفاقی افتاده
ممکنه اول:
RW
بوده و بعد Permission اون تغییر کرده باشه
یا از اول:
RX
ایجاد شده باشه
یا یک Runtime legitimate اون رو ایجاد کرده باشه
پس اگر Telemetry کافی داشته باشیم
Timeline
میتونه خیلی مهم باشه:
Allocation
↓
Memory Write
↓
Protection Change
↓
Thread Creation
↓
Execution
این زنجیره از یک Snapshot ساده اطلاعات بیشتری میده
از دید Detection
حالا میتونیم چند Evidence رو کنار هم بذاریم:
Process
│
├── Memory Region
│ ├── Private
│ └── Executable
│
├── Thread
│ └── Start Address → Region
│
└── Call Stack
└── Unexpected Frames
هیچکدوم به تنهایی لزوما نشونه حمله نیست
ولی ارتباط بین اونها میتونه برای Detection مهم باشه
این دقیقا یکی از ایدههای اصلی
Behavioral Detection
هست:
یک Event
↓
ممکنه عادی باشه
چند Event مرتبط
↓
Context
↓
Behavior
از دید ردتیمرا
از دید مهاجم هم این قسمت یک درس مهم داره
Memory هم Telemetry
ایجاد میکنه
یعنی این تصور:
No File
=
No Detection
اشتباهه چون ممکنه فایل واضحی روی Disk وجود نداشته باشه ولی وضعیت Process و Memory همچنان Evidence ایجاد کنه
مدل کامل تر:
Disk
+
Process
+
Memory
+
Thread
+
Call Stack
+
Timeline
↓
Behavioral Evidence
پس درک Memory فقط برای کسی که Detection مینویسه مهم نیست
برای Red Team هم مهمه که بدونه کد و Data بعد از ورود به Process چه ردپای قابل مشاهده ای ایجاد میکنن
یک نکته مهم درباره Private و Image
این دو دسته رو بیش از حد ساده نکنیم
در Windows Memory Management نوع یک Region فقط با این جمله تعیین نمیشه که:
Private = چیزی که برنامه خودش ساخته
Image =
چیزی که حتما مستقیم از Disk خونده شده
جزئیات Memory Manager Section Objectها Mappingها و نوع Allocation مهم هستن
پس وقتی در ابزارهای Forensics یا Debugger چیزی مثل:
`
Type = Private
میبینید باید دقیقا بدونید اون ابزار بر اساس چه اطلاعاتی این Classification رو انجام داده
هدف ما در RE و Memory Forensics این نیست که یک Label رو حفظ کنیم
هدف اینه که بدونیم پشت اون Label چه Memory Behavior ای وجود داره
API / Syscall
↓
Memory Allocation
↓
Memory Protection
↓
Memory Type
↓
Thread
↓
Call Stack
↓
VAD
↓
Behavioral Analysis
سه مفهوم رو فعلا خوب نگه دارید:
Image-backed Memory
↓
Memory
مرتبط با Image Mapping
Private Memory
↓
Memory
که به یک Image Mapping معمولی وابسته نیست
VAD
↓
ساختارهای Kernel برای توصیف محدوده های Virtual Address Space
و مهمتر از همه:
Private
≠
Malicious
Executable
≠
Malicious
RW → RX
≠
Malicious
چیزی که مهمه ترکیب Evidence ها و Context هست
@reverseengine
ReverseEngineering
Private Memory و Image-backed Memory و VAD تا اینجا درباره RW → RX صحبت کردیم این Memory اصلا متعلق به چیه؟ وقتی یک Process رو بررسی میکنید همه Memory هایی که میبینید یک جنس نیستن برای تحلیل Memory-based Behavior باید بدونیم هر Region چطور ساخته شده…
Private Memory and
Image-backed Memory and
VAD
So far we have talked about RW → RX
What does this Memory even belong to?
When you examine a process, not all the memory you see is the same
To analyze memory-based behavior, we need to know how each region is built and what its relationship is to the file on disk
Image-backed Memory
When a PE such as:
notepad.exe
ntdll.dll
kernel32.dll
is placed inside the process, parts of the Virtual Address Space are linked to an Image File on disk
Conceptually:
PE File
│
▼
Image Mapping
│
▼
Process Virtual Address Space
For example, an Executable may have sections like this:
notepad.exe
├── .text
├── .rdata
├── .data
└── ...
In memory, these sections are usually linked to the Image related to the same Module, so when a Region is of the Image type, one of the important questions is:
Which Module on disk does this Region relate to?
Private Memory
Another type of memory is created as Private
This memory is not directly mapped to an image from the PE file. For example, a process may use Private Memory for various purposes:
Process
│
├── Image-backed Memory
│
└── Private Memory
├── Heap
├── Runtime Data
├── Temporary Allocations
└── Other Private Allocations
Important note:
Private Memory ≠ Malware
Completely legitimate programs use Private Memory all the time
So just seeing
Type = Private
doesn't prove anything
So why is Private Memory important for security analysis?
Because we need to examine several features together
For example:
Private Memory
+
Executable Permission
+
Unusual Thread
+
Unexpected Call Stack
Each one can be completely normal on its own
But when several Evidences are put together, the Investigation value increases
For example, a Region:
Private
RX`
Alone does not say:
Malicious
But it can be a good point for further investigation
The analyst should examine the Context
VAD
Now we come to VAD
Virtual Address Descriptor
VAD is one of the internal structures
Windows
to describe the limits of a Virtual Address Space Process
Conceptually:
Process Virtual Address Space
0x0000
│
├── Region A
├── Region B
├── Region C
├── Region D
└── ...
0xFFFF
VADs help the Kernel manage and describe information
regarding the Virtual Memory limits of a Process
For example, that a Where is the region and what are its properties
Memory Region
Not just an Address
Suppose you see something like this in Debugger or Memory Analysis:
Address: 0x7...
Size: 0x...
Protection: RX
Type: Private
If you only see the Address, you will miss a lot of information
You should ask a few questions:
Where is this Region?
↓
How big is it?
↓
What is its Protection?
↓
Private
Is it an Octal or an Image?
↓
What Mapping or Allocation does it belong to?
↓
What Threads are accessing or executing it?
↓
What happened to it over time?
Here Memory Analysis goes from looking at a few Hex Values to Behavioral Analysis
VAD in Memory Forensics
In Memory Forensics we can examine the Virtual Memory structures related to the Process
Conceptually:
Process
│
├── Image
│
├── .text
│
└── .data
│
├── DLLs
│
├── Heap
│
└── Private Regions
Now suppose we find a Region that needs further investigation
We can ask:
What is the Protection?
↓
Is it Private or Image?
↓
Is it associated with a known Module?
↓
What does its content look like?
↓
Is there a Thread associated with it
↓
What does the Call Stack show?
↓
Has the Region's state changed over time?
The important point is that VAD alone does not answer all these questions
VAD is one of the sources of evidence in memory analysis
Analysis scenario
Suppose we have this situation in a memory dump:
Process: example.exe
Region A
Image
RX
Region B
Private
RW
Region C
Private
RX
Region A looks very normal
Because it is related to an image and its permission is expected for executing code
Image-backed Memory and
VAD
So far we have talked about RW → RX
What does this Memory even belong to?
When you examine a process, not all the memory you see is the same
To analyze memory-based behavior, we need to know how each region is built and what its relationship is to the file on disk
Image-backed Memory
When a PE such as:
notepad.exe
ntdll.dll
kernel32.dll
is placed inside the process, parts of the Virtual Address Space are linked to an Image File on disk
Conceptually:
PE File
│
▼
Image Mapping
│
▼
Process Virtual Address Space
For example, an Executable may have sections like this:
notepad.exe
├── .text
├── .rdata
├── .data
└── ...
In memory, these sections are usually linked to the Image related to the same Module, so when a Region is of the Image type, one of the important questions is:
Which Module on disk does this Region relate to?
Private Memory
Another type of memory is created as Private
This memory is not directly mapped to an image from the PE file. For example, a process may use Private Memory for various purposes:
Process
│
├── Image-backed Memory
│
└── Private Memory
├── Heap
├── Runtime Data
├── Temporary Allocations
└── Other Private Allocations
Important note:
Private Memory ≠ Malware
Completely legitimate programs use Private Memory all the time
So just seeing
Type = Private
doesn't prove anything
So why is Private Memory important for security analysis?
Because we need to examine several features together
For example:
Private Memory
+
Executable Permission
+
Unusual Thread
+
Unexpected Call Stack
Each one can be completely normal on its own
But when several Evidences are put together, the Investigation value increases
For example, a Region:
Private
RX`
Alone does not say:
Malicious
But it can be a good point for further investigation
The analyst should examine the Context
VAD
Now we come to VAD
Virtual Address Descriptor
VAD is one of the internal structures
Windows
to describe the limits of a Virtual Address Space Process
Conceptually:
Process Virtual Address Space
0x0000
│
├── Region A
├── Region B
├── Region C
├── Region D
└── ...
0xFFFF
VADs help the Kernel manage and describe information
regarding the Virtual Memory limits of a Process
For example, that a Where is the region and what are its properties
Memory Region
Not just an Address
Suppose you see something like this in Debugger or Memory Analysis:
Address: 0x7...
Size: 0x...
Protection: RX
Type: Private
If you only see the Address, you will miss a lot of information
You should ask a few questions:
Where is this Region?
↓
How big is it?
↓
What is its Protection?
↓
Private
Is it an Octal or an Image?
↓
What Mapping or Allocation does it belong to?
↓
What Threads are accessing or executing it?
↓
What happened to it over time?
Here Memory Analysis goes from looking at a few Hex Values to Behavioral Analysis
VAD in Memory Forensics
In Memory Forensics we can examine the Virtual Memory structures related to the Process
Conceptually:
Process
│
├── Image
│
├── .text
│
└── .data
│
├── DLLs
│
├── Heap
│
└── Private Regions
Now suppose we find a Region that needs further investigation
We can ask:
What is the Protection?
↓
Is it Private or Image?
↓
Is it associated with a known Module?
↓
What does its content look like?
↓
Is there a Thread associated with it
↓
What does the Call Stack show?
↓
Has the Region's state changed over time?
The important point is that VAD alone does not answer all these questions
VAD is one of the sources of evidence in memory analysis
Analysis scenario
Suppose we have this situation in a memory dump:
Process: example.exe
Region A
Image
RX
Region B
Private
RW
Region C
Private
RX
Region A looks very normal
Because it is related to an image and its permission is expected for executing code
Region B
It could be perfectly normal,
as Private RW memory is very common within processes.
However, Region C:
Private
RX
warrants further investigation.
Let's gather more information:
Region C
↓
Private
↓
RX
↓
Thread Start Address → Inside the region
↓
Call Stack → Unusual
At this point, Region C becomes an investigation target.
But we don't immediately conclude:
Malware Found
because there might be a legitimate explanation.
For instance, runtimes, JIT compilation, instrumentation, or certain software techniques can also create executable memory.
So:
Suspicious
≠
Malicious
Permissions are just one piece of the puzzle.
We’ve discussed permissions quite a bit so far—
such as:
RW
RX
RWX
—but a proper analysis looks beyond just permissions.
Compare these two regions, for example:
Region A
Image
RX
Known Module
Normal Thread Activity
And:
Region B
Private
RX
Unknown Mapping
Unusual Thread Start
Unexpected Call Stack
Both might be executable,
but they exist in completely different contexts.
So, a better model considers:
Memory Type
+
Protection
+
Content
+
Thread
+
Call Stack
+
Module Relationship
+
Timeline
The more the evidence points to a consistent narrative, the higher the confidence in the analysis.
Why is the timeline important? Suppose a memory region currently looks like this:
Private
RX
Just by looking at the current state, we don't know what happened previously.
It might have initially been:
RW
...and its permissions were subsequently changed,
or it might have been created as:
RX
from the start,
or created by a legitimate runtime process.
So, if we have sufficient telemetry,
the timeline
can be crucial:
Allocation
↓
Memory Write
↓
Protection Change
↓
Thread Creation
↓
Execution
This chain provides far more information than a simple snapshot.
From a detection perspective:
We can now piece together several pieces of evidence:
Process
│
├── Memory Region
│ ├── Private
│ └── Executable
│
├── Thread
│ └── Start Address → Region
│
└── Call Stack
└── Unexpected Frames
None of these individually necessarily indicates an attack,
but the relationship between them can be vital for detection.
This is precisely one of the core concepts
of behavioral detection:
A single event
↓
might be benign
Multiple related events
↓
Context
↓
Behavior
From a Red Teamer's perspective—
and from an attacker's perspective, too—there is an important lesson here:
Memory also generates telemetry.
This means the notion that:
No File
=
No Detection
is incorrect; there might not be an obvious file on disk, yet the state of the process and memory can still generate evidence.
A more comprehensive model:
Disk
+
Process
+
Memory
+
Thread
+
Call Stack
+
Timeline
↓
Behavioral Evidence
So, understanding memory isn't just important for those writing detection rules;
it is also crucial for Red Teamers to understand what visible traces code and data leave behind after entering a process.
An important point regarding "Private" and "Image" types:
Let's not oversimplify these two categories.
In Windows memory management, the type of a region... It isn't determined solely by a statement like:
Private = Something created by the program itself
Image = Something read directly from disk
Details regarding the Memory Manager, Section Objects, mappings, and allocation types are crucial.
So, when you see something like
Our goal in Reverse Engineering (RE) and memory forensics isn't just to memorize a label;
The goal is to understand the memory behavior underlying that label.
API / Syscall
↓
Memory Allocation
↓
Memory Protection
↓
Memory Type
↓
Thread
↓
Call Stack
↓
VAD
↓
Behavioral Analysis
Keep these three concepts firmly in mind for now:
It could be perfectly normal,
as Private RW memory is very common within processes.
However, Region C:
Private
RX
warrants further investigation.
Let's gather more information:
Region C
↓
Private
↓
RX
↓
Thread Start Address → Inside the region
↓
Call Stack → Unusual
At this point, Region C becomes an investigation target.
But we don't immediately conclude:
Malware Found
because there might be a legitimate explanation.
For instance, runtimes, JIT compilation, instrumentation, or certain software techniques can also create executable memory.
So:
Suspicious
≠
Malicious
Permissions are just one piece of the puzzle.
We’ve discussed permissions quite a bit so far—
such as:
RW
RX
RWX
—but a proper analysis looks beyond just permissions.
Compare these two regions, for example:
Region A
Image
RX
Known Module
Normal Thread Activity
And:
Region B
Private
RX
Unknown Mapping
Unusual Thread Start
Unexpected Call Stack
Both might be executable,
but they exist in completely different contexts.
So, a better model considers:
Memory Type
+
Protection
+
Content
+
Thread
+
Call Stack
+
Module Relationship
+
Timeline
The more the evidence points to a consistent narrative, the higher the confidence in the analysis.
Why is the timeline important? Suppose a memory region currently looks like this:
Private
RX
Just by looking at the current state, we don't know what happened previously.
It might have initially been:
RW
...and its permissions were subsequently changed,
or it might have been created as:
RX
from the start,
or created by a legitimate runtime process.
So, if we have sufficient telemetry,
the timeline
can be crucial:
Allocation
↓
Memory Write
↓
Protection Change
↓
Thread Creation
↓
Execution
This chain provides far more information than a simple snapshot.
From a detection perspective:
We can now piece together several pieces of evidence:
Process
│
├── Memory Region
│ ├── Private
│ └── Executable
│
├── Thread
│ └── Start Address → Region
│
└── Call Stack
└── Unexpected Frames
None of these individually necessarily indicates an attack,
but the relationship between them can be vital for detection.
This is precisely one of the core concepts
of behavioral detection:
A single event
↓
might be benign
Multiple related events
↓
Context
↓
Behavior
From a Red Teamer's perspective—
and from an attacker's perspective, too—there is an important lesson here:
Memory also generates telemetry.
This means the notion that:
No File
=
No Detection
is incorrect; there might not be an obvious file on disk, yet the state of the process and memory can still generate evidence.
A more comprehensive model:
Disk
+
Process
+
Memory
+
Thread
+
Call Stack
+
Timeline
↓
Behavioral Evidence
So, understanding memory isn't just important for those writing detection rules;
it is also crucial for Red Teamers to understand what visible traces code and data leave behind after entering a process.
An important point regarding "Private" and "Image" types:
Let's not oversimplify these two categories.
In Windows memory management, the type of a region... It isn't determined solely by a statement like:
Private = Something created by the program itself
Image = Something read directly from disk
Details regarding the Memory Manager, Section Objects, mappings, and allocation types are crucial.
So, when you see something like
Type = Private in forensics tools or debuggers, you need to know exactly what information the tool used to make that classification.Our goal in Reverse Engineering (RE) and memory forensics isn't just to memorize a label;
The goal is to understand the memory behavior underlying that label.
API / Syscall
↓
Memory Allocation
↓
Memory Protection
↓
Memory Type
↓
Thread
↓
Call Stack
↓
VAD
↓
Behavioral Analysis
Keep these three concepts firmly in mind for now:
Image-backed Memory
↓
Memory associated with an image mapping
Private Memory
↓
Memory not dependent on a standard image mapping
VAD
↓
Kernel structures describing virtual address space ranges
And most importantly:
Private ≠ Malicious
Executable ≠ Malicious
RW → RX ≠ Malicious
What matters is the combination of evidence and context.
@reverseengine
↓
Memory associated with an image mapping
Private Memory
↓
Memory not dependent on a standard image mapping
VAD
↓
Kernel structures describing virtual address space ranges
And most importantly:
Private ≠ Malicious
Executable ≠ Malicious
RW → RX ≠ Malicious
What matters is the combination of evidence and context.
@reverseengine
قسمت سی و چهارم بافر اورفلو
Dynamic Taint Analysis دنبال کردن داده در زمان اجرای واقعی برنامه
تا اینجا بیشتر از روی Assembly و ساختار برنامه مسیر داده رو بررسی کردیم
یعنی برنامه رو اجرا نکردیم و سعی کردیم از روی کد بفهمیم داده ممکنه از کجا وارد بشه و به کجا برسه
این میشه
Static Analysis
حالا میخوایم یک قدم جلوتر بریم
برنامه رو واقعا اجرا کنیم و ببینیم داده در همین اجرای مشخص دقیقا
از چه مسیرهایی عبور میکنه
اینجا میرسیم به
Dynamic Taint Analysis
یا DTA
فرق Static و Dynamic
در Static Analysis برنامه اجرا نمیشه
ما از روی کد و Assembly بررسی میکنیم
Source
↓
Instruction
↓
Function
↓
Sink
ولی در Dynamic Analysis برنامه واقعا اجرا میشه
Program Running
↓
Input
↓
Execution
↓
Track Data
↓
Sink
یعنی به جای اینکه فقط بگیم این مسیر ممکنه وجود داشته باشه
میبینیم در این اجرای مشخص واقعا چه اتفاقی افتاده
یک مثال ساده
فرض کنید برنامه اینه
C
#include <stdio.h>
#include <string.h>
void process(char *input)
{
char buffer[32];
strcpy(buffer, input);
printf("%s\n", buffer);
}
برنامه اجرا میشه و این ورودی رو دریافت میکنه
hello
حالا سوال ما اینه
این
hello از لحظه ورود به برنامه چه مسیری رو طی کرده؟اینجاست که Taint Analysis وارد میشه
Source
اول باید مشخص کنیم داده از کجا وارد برنامه شده
مثلا:
stdin
یا مثلا
read()
recv()
fread()
پس میتونیم بگیم
stdin
↓
TAINTED DATA
یعنی دادهای که از Source وارد شده فعلا به عنوان Tainted علامتگذاری شده
Taint Propagation
حالا فرض کنید برنامه این کار رو انجام بده
C
char *input = get_input();
char *data = input;
char *ptr = data;
از دید Taint Tracking مسیر وابستگی داده میتونه مفهومی شبیه این باشه
input
↓
Tainted
↓
data
↓
Tainted
↓
ptr
↓
Tainted
یعنی مقدار موجود در
ptr از دادهای میاد که از Source وارد شدهالبته در ابزارهای واقعی داستان کمی پیچیده تره
چون Taint فقط یک برچسب ساده روی متغیر نیست
ابزار باید انتقال و تغییر داده رو در Registerها و Memory هم دنبال کنه
Taint
روی بخشی از داده
یک نکته مهم اینه که همیشه کل داده Tainted نیست
فرض کنید ورودی اینه
ABCDEFGH
ممکنه یک عملیات فقط به چهار بایت اول وابسته باشه
ABCD
در این حالت میتونیم مفهومی شبیه این داشته باشیم
A → Tainted
B → Tainted
C → Tainted
D → Tainted
E → Clean
F → Clean
G → Clean
H → Clean
یعنی Taint میتونه در سطح بخشی از یک مقدار هم دنبال بشه
این موضوع مخصوصا در تحلیل Parser ها و Protocol ها مهمه
چون میتونیم بفهمیم کدوم قسمت از Input واقعا روی یک عملیات خاص تأثیر گذاشته
Taint
در Register ها
حالا بریم سراغ چیزی که در Reverse Engineering بیشتر باهاش سر و کار داریم
Assembly
فرض کنید هنگام اجرای برنامه این دستور اجرا میشه
mov rax,[rbp-40h]
و Memory در آدرس
[rbp-40h]
حاوی داده Tainted باشه
پس بعد از اجرای دستور
[rbp-40h]
↓
RAX
حالا دستور بعدی
mov rcx,rax
باعث میشه وابستگی Taint هم دنبال بشه
Memory
↓
RAX
↓
RCX
بعد فرض کنید داریم
mov [rdi],rcx
حالا مسیر داده میشه
Input
↓
Memory
↓
RAX
↓
RCX
↓
Memory Write
اینجا دیگه فقط از روی Assembly حدس نزدیم
داریم مسیر داده رو در یک اجرای واقعی دنبال میکنیم
یک نکته مهم درباره Taint و Pointer
اینجا باید حواسمون باشه
Taint
کردن یک Pointer حتما به معنی Taint شدن دادهای که Pointer بهش اشاره میکنه نیست
مثلا:
Pointer
↓
0x12345678
ممکنه خود مقدار Pointer تحت تاثیر Input باشه
ولی محتوای آدرس
0x12345678
حتما به همون شکل Tainted نیست
این تفاوت در تحلیل Memory خیلی مهمه
چون
Pointer Taint
و
Pointee Data Taint
دو مفهوم متفاوت هستن
Dynamic Analysis
چه مزیتی داره؟
فرض کنید یک تابع 50 مسیر مختلف داره
در Static Analysis ممکنه لازم باشه تعداد زیادی از این مسیرها رو بررسی کنیم
ولی Dynamic Analysis روی اجرای فعلی فقط چیزی رو میبینه که واقعا اجرا شده
مثلا:
Function
├── Path A
├── Path B
├── Path C
└── Path D
اگر Input ما باعث اجرای Path C بشه
Input
↓
Path C
↓
Sink
همین مسیر واقعی رو مشاهده میکنیم
ولی اینجا یک مشکل داریم
Dynamic Analysis همه چیز رو نمیبینه
فرض کنید برنامه این شکلیه
Input
↓
if (condition)
↓
Path B
اگر شرط برقرار نشه
Path B
اصلا اجرا نمیشه
پس Dynamic Analysis نمیتونه درباره مسیری که اجرا نشده با همان قطعیت مسیر اجراشده اطلاعات بده
به همین دلیل معمولا این دو روش کنار هم ارزش بیشتری دارن
Static Analysis
+
Dynamic Analysis
Static
کمک میکنه بفهمیم چه مسیر ها و وابستگی هایی ممکنه وجود داشته باشن
Dynamic
کمک میکنه بفهمیم در یک
اجرای مشخص واقعا چه اتفاقی افتاده
ابزارها
برای Dynamic Binary Analysis و Taint Tracking ابزارها و Framework های مختلفی وجود دارن
Intel Pin
DynamoRIO
Triton
angr
PANDA
ولی نباید فکر کنیم همه این ابزارها دقیقا یک کار رو انجام میدن مدل Instrumentation و قابلیت های هرکدوم متفاوته
مثلا:
Triton
برای Dynamic Binary Analysis امکاناتی مثل Taint Analysis و Symbolic Execution داره
ارتباط با Fuzzing
اینجا داستان جالب تر میشه
ما قبلا داشتیم
Fuzzer
↓
Interesting Input
↓
Crash
حالا میتونیم از Dynamic Taint Analysis برای بررسی مسیر داده استفاده کنیم
مثلا:
Interesting Input
↓
Tainted Data
↓
Parser
↓
Variable
↓
Memory Operation
↓
Crash
پس فقط نمیگیم برنامه Crash کرد
بلکه میتونیم بررسی کنیم دادهای که از Input وارد شده در طول اجرای برنامه چه مسیر هایی رو طی کرده و کجا در ایجاد مشکل نقش داشته
Static + Dynamic
یک Workflow منطقی برای تحلیل میتونه این شکلی باشه
Fuzzing
↓
Crash
↓
Crash Triage
↓
Static Analysis
↓
Find Source / Sink
↓
Dynamic Analysis
↓
Trace Data
↓
Root Cause
اینجا کم کم از یک Crash ساده میرسیم به درک بهتر مسیر ایجاد Bug
Dynamic Taint Analysis
یعنی داده یا وابستگی داده ای مشخص رو هنگام اجرای واقعی برنامه دنبال کنیم
مفاهیم اصلی:
Source
↓
Taint
↓
Propagation
↓
Register / Memory
↓
Control Flow
↓
Sink
و نکته مهم اینه که Static Analysis و Dynamic Analysis رقیب هم نیستن
Static
بیشتر جواب این سوال رو میده
چه مسیر هایی ممکنه وجود داشته باشه؟
Dynamic
بیشتر جواب این سوال رو میده
در این اجرای مشخص واقعا چه مسیری طی شد؟
وقتی این دو رو کنار Fuzzing و Crash Analysis بذاریم
میتونیم از
Crash
بریم به
Input
↓
Data Flow
↓
Instruction
↓
Memory Operation
↓
Crash
↓
Root Cause
و این دقیقا همون جاییه که تحلیل یک Crash از یک اتفاق ساده تبدیل میشه به فهمیدن علت واقعی Bug
@reverseengine
به همین دلیل معمولا این دو روش کنار هم ارزش بیشتری دارن
Static Analysis
+
Dynamic Analysis
Static
کمک میکنه بفهمیم چه مسیر ها و وابستگی هایی ممکنه وجود داشته باشن
Dynamic
کمک میکنه بفهمیم در یک
اجرای مشخص واقعا چه اتفاقی افتاده
ابزارها
برای Dynamic Binary Analysis و Taint Tracking ابزارها و Framework های مختلفی وجود دارن
Intel Pin
DynamoRIO
Triton
angr
PANDA
ولی نباید فکر کنیم همه این ابزارها دقیقا یک کار رو انجام میدن مدل Instrumentation و قابلیت های هرکدوم متفاوته
مثلا:
Triton
برای Dynamic Binary Analysis امکاناتی مثل Taint Analysis و Symbolic Execution داره
ارتباط با Fuzzing
اینجا داستان جالب تر میشه
ما قبلا داشتیم
Fuzzer
↓
Interesting Input
↓
Crash
حالا میتونیم از Dynamic Taint Analysis برای بررسی مسیر داده استفاده کنیم
مثلا:
Interesting Input
↓
Tainted Data
↓
Parser
↓
Variable
↓
Memory Operation
↓
Crash
پس فقط نمیگیم برنامه Crash کرد
بلکه میتونیم بررسی کنیم دادهای که از Input وارد شده در طول اجرای برنامه چه مسیر هایی رو طی کرده و کجا در ایجاد مشکل نقش داشته
Static + Dynamic
یک Workflow منطقی برای تحلیل میتونه این شکلی باشه
Fuzzing
↓
Crash
↓
Crash Triage
↓
Static Analysis
↓
Find Source / Sink
↓
Dynamic Analysis
↓
Trace Data
↓
Root Cause
اینجا کم کم از یک Crash ساده میرسیم به درک بهتر مسیر ایجاد Bug
Dynamic Taint Analysis
یعنی داده یا وابستگی داده ای مشخص رو هنگام اجرای واقعی برنامه دنبال کنیم
مفاهیم اصلی:
Source
↓
Taint
↓
Propagation
↓
Register / Memory
↓
Control Flow
↓
Sink
و نکته مهم اینه که Static Analysis و Dynamic Analysis رقیب هم نیستن
Static
بیشتر جواب این سوال رو میده
چه مسیر هایی ممکنه وجود داشته باشه؟
Dynamic
بیشتر جواب این سوال رو میده
در این اجرای مشخص واقعا چه مسیری طی شد؟
وقتی این دو رو کنار Fuzzing و Crash Analysis بذاریم
میتونیم از
Crash
بریم به
Input
↓
Data Flow
↓
Instruction
↓
Memory Operation
↓
Crash
↓
Root Cause
و این دقیقا همون جاییه که تحلیل یک Crash از یک اتفاق ساده تبدیل میشه به فهمیدن علت واقعی Bug
@reverseengine
ReverseEngineering
قسمت سی و چهارم بافر اورفلو Dynamic Taint Analysis دنبال کردن داده در زمان اجرای واقعی برنامه تا اینجا بیشتر از روی Assembly و ساختار برنامه مسیر داده رو بررسی کردیم یعنی برنامه رو اجرا نکردیم و سعی کردیم از روی کد بفهمیم داده ممکنه از کجا وارد بشه و به…
Part 34 Buffer Overflow
Dynamic Taint Analysis Tracking Data During Actual Program Execution
So far, we have mostly examined the data path based on the assembly and program structure
That is, we did not execute the program and tried to understand from the code where the data might come from and where it might end up
This is called
Static Analysis
Now we want to go one step further
to actually execute the program and see exactly what paths the data takes in this specific execution
Here we come to
Dynamic Taint Analysis
or DTA
Difference between Static and Dynamic
In Static Analysis, the program is not executed
We examine from the code and assembly
Source
↓
Instruction
↓
Function
↓
Sink
But in Dynamic Analysis, the program is actually executed
Program Running
↓
Input
↓
Execution
↓
Track Data
↓
Sink
That is, instead of just saying that this path might exist
We see what actually happened in this specific execution
A simple example
Suppose the program is
C
#include <stdio.h>
#include <string.h>
void process(char *input)
{
char buffer[32];
strcpy(buffer, input);
printf("%s\n", buffer);
}
The program runs and receives this input
hello
Now our question is
What path has this hello taken since entering the program?
This is where Taint Analysis comes in
Source
First we need to specify where the data entered the program from
For example:
stdin
Or for example
read()
recv()
fread()
Then we can say
stdin
↓
TAINTED DATA
That is, the data entered from Source is currently marked as Tainted
Taint Propagation
Now suppose the program does this
C
char *input = get_input();
char *data = input;
char *ptr = data;
From the perspective of Taint Tracking, the data dependency path can be a concept like this
input
↓
Tainted
↓
data
↓
Tainted
↓
ptr
↓
Tainted
That is, the value in ptr comes from data that was entered from Source
Of course, in real tools, the story is a little more complicated
Because Taint is not just a simple label on a variable
The tool must also track data transfers and changes in Registers and Memory
Taint
on a part of the data
An important point is that not always the entire data is Tainted
Suppose the input is
ABCDEFGH
An operation may depend only on the first four bytes
ABCD
In this case, we can have a concept like this
A → Tainted
B → Tainted
C → Tainted
D → Tainted
E → Clean
F → Clean
G → Clean
H → Clean
That is, Taint can be tracked at the level of a part of a value
This is especially true in the analysis of Parsers and Protocols Important
Because we can understand which part of the Input actually affected a particular operation
Taint
in Registers
Now let's move on to what we deal with most in Reverse Engineering
Assembly
Suppose that when the program is executed, this instruction is executed
mov rax,[rbp-40h]
and the Memory at the address
[rbp-40h]
contains Tainted data
So after executing the instruction
[rbp-40h]
↓
RAX
Now the next instruction
mov rcx,rax
will also follow the Taint dependency
Memory
↓
RAX
↓
RCX
Then suppose we have
mov [rdi],rcx
Now the data path is
Input
↓
Memory
↓
RAX
↓
RCX
↓
Memory Write
Here we are not just guessing from the Assembly
We are following the data path in a real execution
An important point about Taint and Pointer
Here we need to be careful Okay
Taint
a Pointer does not necessarily mean that the data that the Pointer points to is tainted
For example:
Pointer
↓
0x12345678
The value of the Pointer itself may be affected by Input
But the content of the address
0x12345678
is not necessarily tainted in the same way
This difference is very important in Memory Analysis
Because
Pointer Taint
and
Pointee Data Taint
are two different concepts
What is the advantage of Dynamic Analysis
?
Suppose a function has 50 different paths
In Static Analysis we may need to examine a large number of these paths
But Dynamic Analysis only sees what is actually executed on the current execution
For example:
Function
├── Path A
├── Path B
├── Path C
└── Path D
If our Input causes Path C to be executed
Input
↓
Path C
↓
Sink
ReverseEngineering
قسمت سی و چهارم بافر اورفلو Dynamic Taint Analysis دنبال کردن داده در زمان اجرای واقعی برنامه تا اینجا بیشتر از روی Assembly و ساختار برنامه مسیر داده رو بررسی کردیم یعنی برنامه رو اجرا نکردیم و سعی کردیم از روی کد بفهمیم داده ممکنه از کجا وارد بشه و به…
We see the same actual path
But here we have a problem
Dynamic Analysis does not see everything
Suppose the program looks like this
Input
↓
if (condition)
↓
Path B
If the condition is not met
Path B
will not be executed at all
But here we have a problem
Dynamic Analysis does not see everything
Suppose the program looks like this
Input
↓
if (condition)
↓
Path B
If the condition is not met
Path B
will not be executed at all
Therefore, dynamic analysis cannot provide information about an unexecuted path with the same level of certainty as it does for an executed one.
For this reason, these two methods are generally more valuable when used together:
Static Analysis
+
Dynamic Analysis
Static
Helps us understand what paths and dependencies might exist.
Dynamic
Helps us understand what actually happened during a
specific execution.
Tools
There are various tools and frameworks available for Dynamic Binary Analysis and Taint Tracking:
Intel Pin
DynamoRIO
Triton
angr
PANDA
However, we shouldn't assume all these tools do exactly the same thing; their instrumentation models and capabilities differ.
For example:
Triton
Offers features like Taint Analysis and Symbolic Execution for Dynamic Binary Analysis.
Connection to Fuzzing
This is where it gets interesting.
Previously, we had:
Fuzzer
↓
Interesting Input
↓
Crash
Now, we can use Dynamic Taint Analysis to examine the data path.
For example:
Interesting Input
↓
Tainted Data
↓
Parser
↓
Variable
↓
Memory Operation
↓
Crash
So, we don't just say the program crashed;
we can also trace the path the input data took during execution and identify where it contributed to the issue.
Static + Dynamic
A logical analysis workflow might look like this:
Fuzzing
↓
Crash
↓
Crash Triage
↓
Static Analysis
↓
Find Source / Sink
↓
Dynamic Analysis
↓
Trace Data
↓
Root Cause
Here, we gradually move from a simple crash to a better understanding of the path that caused the bug.
Dynamic Taint Analysis
Means tracking specific data or data dependencies during the actual execution of the program.
Key concepts:
Source
↓
Taint
↓
Propagation
↓
Register / Memory ↓
Control Flow
↓
Sink
An important point is that static analysis and dynamic analysis are not rivals.
Static analysis
primarily answers this question:
What possible paths might exist?
Dynamic analysis
primarily answers this question:
What path was actually taken during this specific execution?
When we combine these two with fuzzing and crash analysis,
we can trace a path from
Crash
to
Input
↓
Data Flow
↓
Instruction
↓
Memory Operation
↓
Crash
↓
Root Cause
And this is precisely the point where analyzing a crash shifts from observing a simple event to understanding the bug's root cause.
@reverseengine
For this reason, these two methods are generally more valuable when used together:
Static Analysis
+
Dynamic Analysis
Static
Helps us understand what paths and dependencies might exist.
Dynamic
Helps us understand what actually happened during a
specific execution.
Tools
There are various tools and frameworks available for Dynamic Binary Analysis and Taint Tracking:
Intel Pin
DynamoRIO
Triton
angr
PANDA
However, we shouldn't assume all these tools do exactly the same thing; their instrumentation models and capabilities differ.
For example:
Triton
Offers features like Taint Analysis and Symbolic Execution for Dynamic Binary Analysis.
Connection to Fuzzing
This is where it gets interesting.
Previously, we had:
Fuzzer
↓
Interesting Input
↓
Crash
Now, we can use Dynamic Taint Analysis to examine the data path.
For example:
Interesting Input
↓
Tainted Data
↓
Parser
↓
Variable
↓
Memory Operation
↓
Crash
So, we don't just say the program crashed;
we can also trace the path the input data took during execution and identify where it contributed to the issue.
Static + Dynamic
A logical analysis workflow might look like this:
Fuzzing
↓
Crash
↓
Crash Triage
↓
Static Analysis
↓
Find Source / Sink
↓
Dynamic Analysis
↓
Trace Data
↓
Root Cause
Here, we gradually move from a simple crash to a better understanding of the path that caused the bug.
Dynamic Taint Analysis
Means tracking specific data or data dependencies during the actual execution of the program.
Key concepts:
Source
↓
Taint
↓
Propagation
↓
Register / Memory ↓
Control Flow
↓
Sink
An important point is that static analysis and dynamic analysis are not rivals.
Static analysis
primarily answers this question:
What possible paths might exist?
Dynamic analysis
primarily answers this question:
What path was actually taken during this specific execution?
When we combine these two with fuzzing and crash analysis,
we can trace a path from
Crash
to
Input
↓
Data Flow
↓
Instruction
↓
Memory Operation
↓
Crash
↓
Root Cause
And this is precisely the point where analyzing a crash shifts from observing a simple event to understanding the bug's root cause.
@reverseengine
Pointer Aliasing
تا اینجا فهمیدیم داده میتونه از Register بره داخل Memory و دوباره از طریق Pointer برگرده
حالا یک مشکل مهم تر داریم
ممکنه دو Pointer مختلف به یک قسمت از Memory اشاره کنن
مثلا:
int value = 100;
int *p = &value;
int *q = &value;
اینجا p و q دو Pointer مختلف هستن
ولی هر دو به یک Memory اشاره میکنن
value
|
0x5000
/ \
/ \
p q
پس اگر این کار رو انجام بدیم
*p = 200;
مقدار value تغییر میکنه
و وقتی بعدا این رو بخونیم
printf("%d", *q);
مقدار 200 رو میبینیم
چون p و q در واقع به یک محل اشاره میکنن
به این وضعیت میگیم
Pointer Aliasing
این موضوع برای Reverse Engineering خیلی مهمه
چون ممکنه داخل یک تابع ببینید
mov [rax], 200
و چند خط پایین تر ببینید
mov ecx, [rbx]
در نگاه اول ممکنه فکر کنید این دو دستور هیچ ارتباطی ندارن
ولی اگر
RAX = 0x5000
RBX = 0x5000
باشه
هر دو دارن با یک Memory مشترک کار میکنن
RAX ─────┐
↓
0x5000
↑
|
RBX ─────┘
پس تغییر از طریق RAX میتونه مستقیما روی چیزی که از طریق RBX خونده میشه اثر بذاره
اینجا Data Flow دیگه فقط بین Register ها نیست
باید رابطه Pointer ها و Memory Address ها رو هم دنبال کنید
یک مثال ساده تر
int value = 10;
int *p = &value;
int *q = p;
*p = 50;
int x = *q;
مسیر واقعی داده
value
↓
p
↓
q
↓
Memory
↓
50
↓
x
اما از نظر Address
p ──┐
├──> value
q ──┘
پس p و q دو مسیر متفاوت برای رسیدن به یک داده هستن
در Reverse Engineering
وقتی با Pointerها کار میکنید فقط نپرسد
این Pointer کجاست؟
باید بپرسید
این Pointer به کجا اشاره میکنه؟
چه Pointer های دیگه ای ممکنه به همین Address اشاره کنن؟
چه کسی مقدار اون Memory رو تغییر داده؟
چه کسی بعدا اون مقدار رو خونده؟
این سوالها کمک میکنن ارتباط هایی رو پیدا کنی که از روی Control Flow معمولی مشخص نیستن
تمرین:
این کد رو بررسی کنید
int value = 10;
int *p = &value;
int *q = p;
*p = 100;
int result = *q;
مسیر Data Flow رو رسم کنید
value
↓
p
↓
q
↓
*p = 100
↓
*q
↓
result
بعد مشخص کن چرا result برابر 100 میشه
در مرحله بعد میریم سراغ یک موضوع مهمتر
Memory Aliasing
در توابع مختلف
اونجا دیگه دو Pointer داخل یک تابع نداریم
یک تابع Memory رو تغییر میده و یک تابع دیگه از طریق Pointer متفاوت همون Memory رو میخونه
و دقیقا همینجا تحلیل Data Flow در باینری های واقعی شروع میکنه به دردسر درست کردن
@reverseengine
تا اینجا فهمیدیم داده میتونه از Register بره داخل Memory و دوباره از طریق Pointer برگرده
حالا یک مشکل مهم تر داریم
ممکنه دو Pointer مختلف به یک قسمت از Memory اشاره کنن
مثلا:
int value = 100;
int *p = &value;
int *q = &value;
اینجا p و q دو Pointer مختلف هستن
ولی هر دو به یک Memory اشاره میکنن
value
|
0x5000
/ \
/ \
p q
پس اگر این کار رو انجام بدیم
*p = 200;
مقدار value تغییر میکنه
و وقتی بعدا این رو بخونیم
printf("%d", *q);
مقدار 200 رو میبینیم
چون p و q در واقع به یک محل اشاره میکنن
به این وضعیت میگیم
Pointer Aliasing
این موضوع برای Reverse Engineering خیلی مهمه
چون ممکنه داخل یک تابع ببینید
mov [rax], 200
و چند خط پایین تر ببینید
mov ecx, [rbx]
در نگاه اول ممکنه فکر کنید این دو دستور هیچ ارتباطی ندارن
ولی اگر
RAX = 0x5000
RBX = 0x5000
باشه
هر دو دارن با یک Memory مشترک کار میکنن
RAX ─────┐
↓
0x5000
↑
|
RBX ─────┘
پس تغییر از طریق RAX میتونه مستقیما روی چیزی که از طریق RBX خونده میشه اثر بذاره
اینجا Data Flow دیگه فقط بین Register ها نیست
باید رابطه Pointer ها و Memory Address ها رو هم دنبال کنید
یک مثال ساده تر
int value = 10;
int *p = &value;
int *q = p;
*p = 50;
int x = *q;
مسیر واقعی داده
value
↓
p
↓
q
↓
Memory
↓
50
↓
x
اما از نظر Address
p ──┐
├──> value
q ──┘
پس p و q دو مسیر متفاوت برای رسیدن به یک داده هستن
در Reverse Engineering
وقتی با Pointerها کار میکنید فقط نپرسد
این Pointer کجاست؟
باید بپرسید
این Pointer به کجا اشاره میکنه؟
چه Pointer های دیگه ای ممکنه به همین Address اشاره کنن؟
چه کسی مقدار اون Memory رو تغییر داده؟
چه کسی بعدا اون مقدار رو خونده؟
این سوالها کمک میکنن ارتباط هایی رو پیدا کنی که از روی Control Flow معمولی مشخص نیستن
تمرین:
این کد رو بررسی کنید
int value = 10;
int *p = &value;
int *q = p;
*p = 100;
int result = *q;
مسیر Data Flow رو رسم کنید
value
↓
p
↓
q
↓
*p = 100
↓
*q
↓
result
بعد مشخص کن چرا result برابر 100 میشه
در مرحله بعد میریم سراغ یک موضوع مهمتر
Memory Aliasing
در توابع مختلف
اونجا دیگه دو Pointer داخل یک تابع نداریم
یک تابع Memory رو تغییر میده و یک تابع دیگه از طریق Pointer متفاوت همون Memory رو میخونه
و دقیقا همینجا تحلیل Data Flow در باینری های واقعی شروع میکنه به دردسر درست کردن
@reverseengine
ReverseEngineering
Pointer Aliasing تا اینجا فهمیدیم داده میتونه از Register بره داخل Memory و دوباره از طریق Pointer برگرده حالا یک مشکل مهم تر داریم ممکنه دو Pointer مختلف به یک قسمت از Memory اشاره کنن مثلا: int value = 100; int *p = &value; int *q = &value; اینجا p…
Pointer Aliasing
So far we have understood that data can go from Register to Memory and back again through Pointer
Now we have a more important problem
Two different Pointers may point to the same part of Memory
For example:
int value = 100;
int *p = &value;
int *q = &value;
Here p and q are two different Pointers
But both point to the same Memory
value
|
0x5000
/ \
/ \
p q
So if we do this
*p = 200;
the value of value will change
And when we read this later
printf("%d", *q);
We see the value 200
Because p and q actually point to the same location
We call this situation
Pointer Aliasing
This is very important for Reverse Engineering
Because you might see inside a function
mov [rax], 200
And a few lines down
mov ecx, [rbx]
At first glance you might think these two instructions have nothing to do
But if
RAX = 0x5000
RBX = 0x5000
Well
They are both working with the same memory
RAX ─────┐
↓
0x5000
↑
|
RBX ─────┘
So a change through RAX can directly affect what is read through RBX
Here the Data Flow is not just between Registers
You should also follow the relationship between Pointers and Memory Addresses
A simpler example
int value = 10;
int *p = &value;
int *q = p;
*p = 50;
int x = *q;
Actual data path
value
↓
p
↓
q
↓
Memory
↓
50
↓
x
But in terms of Address
p ──┐
├──> value
q ──┘
So p and q are two different paths to reach the same data
In Reverse Engineering
When working with Pointers, don't just ask
Where is this Pointer?
You should ask
Where does this Pointer point to?
What other Pointers might point to the same Address?
Who changed the value of that Memory?
Who later read that value?
These questions help you find connections that are not obvious from normal Control Flow
Exercise:
Examine this code
int value = 10;
int *p = &value;
int *q = p;
*p = 100;
int result = *q;
Draw the Data Flow path
value
↓
p
↓
q
↓
*p = 100
↓
*q
↓
result
Then determine why result is equal to 100
Next, we will move on to a more important topic
Memory Aliasing
In different functions
There we no longer have two pointers inside a function
One function modifies the Memory and another function reads the same Memory through a different Pointer
And this is exactly where Data Flow analysis in real binaries starts to cause trouble
@reverseengine
So far we have understood that data can go from Register to Memory and back again through Pointer
Now we have a more important problem
Two different Pointers may point to the same part of Memory
For example:
int value = 100;
int *p = &value;
int *q = &value;
Here p and q are two different Pointers
But both point to the same Memory
value
|
0x5000
/ \
/ \
p q
So if we do this
*p = 200;
the value of value will change
And when we read this later
printf("%d", *q);
We see the value 200
Because p and q actually point to the same location
We call this situation
Pointer Aliasing
This is very important for Reverse Engineering
Because you might see inside a function
mov [rax], 200
And a few lines down
mov ecx, [rbx]
At first glance you might think these two instructions have nothing to do
But if
RAX = 0x5000
RBX = 0x5000
Well
They are both working with the same memory
RAX ─────┐
↓
0x5000
↑
|
RBX ─────┘
So a change through RAX can directly affect what is read through RBX
Here the Data Flow is not just between Registers
You should also follow the relationship between Pointers and Memory Addresses
A simpler example
int value = 10;
int *p = &value;
int *q = p;
*p = 50;
int x = *q;
Actual data path
value
↓
p
↓
q
↓
Memory
↓
50
↓
x
But in terms of Address
p ──┐
├──> value
q ──┘
So p and q are two different paths to reach the same data
In Reverse Engineering
When working with Pointers, don't just ask
Where is this Pointer?
You should ask
Where does this Pointer point to?
What other Pointers might point to the same Address?
Who changed the value of that Memory?
Who later read that value?
These questions help you find connections that are not obvious from normal Control Flow
Exercise:
Examine this code
int value = 10;
int *p = &value;
int *q = p;
*p = 100;
int result = *q;
Draw the Data Flow path
value
↓
p
↓
q
↓
*p = 100
↓
*q
↓
result
Then determine why result is equal to 100
Next, we will move on to a more important topic
Memory Aliasing
In different functions
There we no longer have two pointers inside a function
One function modifies the Memory and another function reads the same Memory through a different Pointer
And this is exactly where Data Flow analysis in real binaries starts to cause trouble
@reverseengine
_assembly-language-for-x86-processors-8-ed.pdf
11.5 MB
Assembly Language for x86 Processors
_security_automation_with_python_practical_python_solutions_for.pdf
40 MB
Security Automation with Python
From BlackCat to Panda Workshop: Inside the Evolving C2 Panel Behind RATHat
https://www.cleafy.com/cleafy-labs/from-blackcat-to-panda-workshop-inside-the-evolving-c2-panel-behind-rathat
@reverseengine
https://www.cleafy.com/cleafy-labs/from-blackcat-to-panda-workshop-inside-the-evolving-c2-panel-behind-rathat
@reverseengine
Cleafy
From BlackCat to Panda Workshop: Inside the Evolving C2 Panel Behind RATHat | Cleafy
Cleafy's TIR team analyzed the operator infrastructure behind RATHat, an Android banking trojan that abuses Accessibility Services to enable wireless debugging on the victim's phone, obtain a shell, and deploy a hidden native service outside the app. The…
NeedyMantis: Unpacking a post-compromise malware family used in targeted operations
https://microsoft.com/en-us/security/blog/2026/09/28/needymantis-unpacking-a-post-compromise-malware-family-used-in-targeted-operations
@reverseengine
https://microsoft.com/en-us/security/blog/2026/09/28/needymantis-unpacking-a-post-compromise-malware-family-used-in-targeted-operations
@reverseengine
Microsoft News
NeedyMantis: Unpacking a post-compromise malware family used in targeted operations
Microsoft Threat Intelligence identified NeedyMantis, a modular post-compromise malware framework used in targeted intrusions that combines custom loaders, encrypted archives, and extensible components to maintain long-term access and support follow-on operations.