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
None of these are enough on their own
But when they are all put together, the detection is stronger
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
Telemetry
Context
Timeline
Correlation
All of these, when considered together, provide more accurate detection
And that's exactly why AV and EDR Evasion is not just about changing an API
New defense systems try to see the effect and outcome of an operation
Even if the path to that operation has changed
@reverseengine
❤1
Copy on Write یا COW
سیستم عامل چطور بدون کپی کردن همه چیز fork میسازه
توی پست های قبلی گفتیم وقتی fork اجرا میشه یک Child Process ساخته میشه
در نگاه اول شاید فکر کنیم سیستم عامل این کارو میکنه
Parent Process
│
▼
کپی کامل حافظه
│
▼
Child Process
یعنی کل حافظه Parent رو برمیداره و دوباره برای Child کپی میکنه
ولی این کار یه مشکل بزرگ داره
فرض کن Parent چند گیگابایت حافظه استفاده کرده
اگه سیستم عامل هر بار که fork اجرا میشه کل حافظه رو کپی کنه
زمان زیادی مصرف میشه
RAM زیادی مصرف میشه
CPU
هم بیخودی درگیر کپی کردن اطلاعات میشه
ولی این بار سیستم عامل یه کم عاقل تر عمل میکنه
راه حل Copy on Write یا COW
سیستم عامل میگه
فعلا چیزی رو کپی نکنید
بعد از fork در ابتدا Parent و Child میتونن از همون Page های حافظه استفاده کنن
به صورت ساده
Physical Memory
│
┌─────────┴─────────┐
│ │
Parent Child
│ │
└─────────┬─────────┘
│
Shared Page
تا وقتی هیچکدوم چیزی رو تغییر نداده باشن لازم نیست یه نسخه جدا ساخته بشه
حالا اگه یکی از اونها بخواد چیزی رو تغییر بده چی میشه
فرض کنید Parent و Child اول کار این مقدار رو دارن
int x = 10
حالا Child میخواد این کارو انجام بده
x = 20
اینجا سیستم عامل متوجه میشه Child میخواد Page مشترک رو تغییر بده
پس تقریبا این اتفاق میفته
قبل از تغییر
Parent
│
▼
┌──────────────┐
│ Page x = 10 │
└──────────────┘
▲
│
Child
یعنی فعلا هر دو به همون Page دسترسی دارن
حالا Child میخواد روی اون Page چیزی بنویسه
سیستم عامل کل حافظه رو کپی نمیکنه
فقط همون Page ای که لازم شده رو کپی میکنه
بعد نتیجه تقریبا این شکلی میشه
Physical Memory
┌──────────────────────┐
│ Page x = 10 │
│ Parent │
└──────────────────────┘
┌──────────────────────┐
│ Page x = 20 │
│ Child │
└──────────────────────┘
پس Parent همچنان x برابر 10 داره
و Child نسخه خودش رو داره که x برابر 20 شده
به همین دلیل اسمش شده Copy on Write
یعنی وقتی نیاز به نوشتن پیش اومد کپی کن
چرا این روش خیلی مهمه
چون توی خیلی از برنامه ها Child بلافاصله بعد از fork میره سراغ exec
یعنی
fork
│
▼
Child
│
▼
exec
یادتون هست exec چیکار میکرد
برنامه فعلی رو با یه برنامه جدید جایگزین میکرد پس اگه سیستم عامل قبل از exec کل حافظه Parent رو کپی میکرد ممکن بود مقدار زیادی از اون حافظه اصلا هیچ وقت استفاده نشه
با COW اتفاق به شکل ساده اینطوریه
fork
│
▼
فعلا حافظه کامل کپی نمیشه
│
▼
Child اجرا میشه
│
▼
exec
│
▼
Program جدید جایگزین میشه
در نتیجه مقدار زیادی زمان و حافظه ذخیره میشه
پشت صحنه چه اتفاقی میفته
بعد از fork سیستم عامل میتونه Page های Parent و Child رو به صورت Shared نگه داره
ولی این Page ها رو برای نوشتن محافظت میکنه
حالا اگه یکی از Process ها بخواد روی همچین Page ای بنویسه
Write Attempt
│
▼
CPU detects protected write
│
▼
Page Fault
│
▼
Kernel handles it
│
▼
Copy the Page
│
▼
Give the writer its own Page
│
▼
Continue execution
یعنی Page Fault همیشه به معنی خراب شدن برنامه نیست
گاهی Page Fault کاملا یه اتفاق طبیعی توی مدیریت حافظه است
این موضوع وقتی برسیم به Virtual Memory و Paging خیلی بیشتر به کارمون میاد
یه نکته مهم
فرض کنید یه برنامه توی Parent یه متغیر رو تغییر میده و انتظار دارید Child هم مقدار جدید رو ببینه
اگه Parent و Child دو Process مستقل باشن معمولا این انتظار درست نیست
بعد از اینکه یکی از Process ها به خاطر COW اون Page رو تغییر بده هر کدوم نسخه خودشون رو دارن
یعنی
Parent Memory
│
▼
Separate Page
Child Memory
│
▼
Separate Page
پس:
Parent Memory ≠ Child Memory
این موضوع برای تحلیل برنامه های چندپردازه ای مهمه
مثلا موقع Debugging ممکنه Parent و Child اول کار رفتار مشابهی داشته باشن
ولی بعد از اینکه یکی از اونها حافظه خودش رو تغییر داد مسیرشون از هم جدا میشه
COW
رو با Shared Memory اشتباه نگیرید
این دوتا شبیه هم به نظر میان ولی یکی نیستن
در COW
اول
│
▼
سیستم عامل چطور بدون کپی کردن همه چیز fork میسازه
توی پست های قبلی گفتیم وقتی fork اجرا میشه یک Child Process ساخته میشه
در نگاه اول شاید فکر کنیم سیستم عامل این کارو میکنه
Parent Process
│
▼
کپی کامل حافظه
│
▼
Child Process
یعنی کل حافظه Parent رو برمیداره و دوباره برای Child کپی میکنه
ولی این کار یه مشکل بزرگ داره
فرض کن Parent چند گیگابایت حافظه استفاده کرده
اگه سیستم عامل هر بار که fork اجرا میشه کل حافظه رو کپی کنه
زمان زیادی مصرف میشه
RAM زیادی مصرف میشه
CPU
هم بیخودی درگیر کپی کردن اطلاعات میشه
ولی این بار سیستم عامل یه کم عاقل تر عمل میکنه
راه حل Copy on Write یا COW
سیستم عامل میگه
فعلا چیزی رو کپی نکنید
بعد از fork در ابتدا Parent و Child میتونن از همون Page های حافظه استفاده کنن
به صورت ساده
Physical Memory
│
┌─────────┴─────────┐
│ │
Parent Child
│ │
└─────────┬─────────┘
│
Shared Page
تا وقتی هیچکدوم چیزی رو تغییر نداده باشن لازم نیست یه نسخه جدا ساخته بشه
حالا اگه یکی از اونها بخواد چیزی رو تغییر بده چی میشه
فرض کنید Parent و Child اول کار این مقدار رو دارن
int x = 10
حالا Child میخواد این کارو انجام بده
x = 20
اینجا سیستم عامل متوجه میشه Child میخواد Page مشترک رو تغییر بده
پس تقریبا این اتفاق میفته
قبل از تغییر
Parent
│
▼
┌──────────────┐
│ Page x = 10 │
└──────────────┘
▲
│
Child
یعنی فعلا هر دو به همون Page دسترسی دارن
حالا Child میخواد روی اون Page چیزی بنویسه
سیستم عامل کل حافظه رو کپی نمیکنه
فقط همون Page ای که لازم شده رو کپی میکنه
بعد نتیجه تقریبا این شکلی میشه
Physical Memory
┌──────────────────────┐
│ Page x = 10 │
│ Parent │
└──────────────────────┘
┌──────────────────────┐
│ Page x = 20 │
│ Child │
└──────────────────────┘
پس Parent همچنان x برابر 10 داره
و Child نسخه خودش رو داره که x برابر 20 شده
به همین دلیل اسمش شده Copy on Write
یعنی وقتی نیاز به نوشتن پیش اومد کپی کن
چرا این روش خیلی مهمه
چون توی خیلی از برنامه ها Child بلافاصله بعد از fork میره سراغ exec
یعنی
fork
│
▼
Child
│
▼
exec
یادتون هست exec چیکار میکرد
برنامه فعلی رو با یه برنامه جدید جایگزین میکرد پس اگه سیستم عامل قبل از exec کل حافظه Parent رو کپی میکرد ممکن بود مقدار زیادی از اون حافظه اصلا هیچ وقت استفاده نشه
با COW اتفاق به شکل ساده اینطوریه
fork
│
▼
فعلا حافظه کامل کپی نمیشه
│
▼
Child اجرا میشه
│
▼
exec
│
▼
Program جدید جایگزین میشه
در نتیجه مقدار زیادی زمان و حافظه ذخیره میشه
پشت صحنه چه اتفاقی میفته
بعد از fork سیستم عامل میتونه Page های Parent و Child رو به صورت Shared نگه داره
ولی این Page ها رو برای نوشتن محافظت میکنه
حالا اگه یکی از Process ها بخواد روی همچین Page ای بنویسه
Write Attempt
│
▼
CPU detects protected write
│
▼
Page Fault
│
▼
Kernel handles it
│
▼
Copy the Page
│
▼
Give the writer its own Page
│
▼
Continue execution
یعنی Page Fault همیشه به معنی خراب شدن برنامه نیست
گاهی Page Fault کاملا یه اتفاق طبیعی توی مدیریت حافظه است
این موضوع وقتی برسیم به Virtual Memory و Paging خیلی بیشتر به کارمون میاد
یه نکته مهم
فرض کنید یه برنامه توی Parent یه متغیر رو تغییر میده و انتظار دارید Child هم مقدار جدید رو ببینه
اگه Parent و Child دو Process مستقل باشن معمولا این انتظار درست نیست
بعد از اینکه یکی از Process ها به خاطر COW اون Page رو تغییر بده هر کدوم نسخه خودشون رو دارن
یعنی
Parent Memory
│
▼
Separate Page
Child Memory
│
▼
Separate Page
پس:
Parent Memory ≠ Child Memory
این موضوع برای تحلیل برنامه های چندپردازه ای مهمه
مثلا موقع Debugging ممکنه Parent و Child اول کار رفتار مشابهی داشته باشن
ولی بعد از اینکه یکی از اونها حافظه خودش رو تغییر داد مسیرشون از هم جدا میشه
COW
رو با Shared Memory اشتباه نگیرید
این دوتا شبیه هم به نظر میان ولی یکی نیستن
در COW
اول
│
▼
❤1
داده ممکنه مشترک باشه
│
▼
یکی میخواد بنویسه
│
▼
نسخه جدا ساخته میشه
ولی در Shared Memory
Parent
│
▼
Shared Memory
▲
│
Child
اگه Parent اطلاعات رو تغییر بده Child میتونه تغییر رو ببینه
پس هدف این دوتا فرق داره
هدف COW اینه که fork بهینه تر انجام بشه و سیستم عامل مجبور نشه بی دلیل کل حافظه رو کپی کنه
هدف Shared Memory اینه که چند Process بتونن واقعا از یه فضای حافظه مشترک استفاده کنن
Copy on Write یا COW
بعد از fork لازم نیست کل حافظه فورا کپی بشه
Parent و Child
میتونن اول از Page های فیزیکی مشترک استفاده کنن
Page
ها برای جلوگیری از نوشتن مستقیم محافظت میشن
وقتی یکی از Process ها بخواد چیزی رو تغییر بده Page Fault اتفاق میفته
Kernel
این اتفاق رو مدیریت میکنه
یه نسخه جدا از اون Page ساخته میشه
Process
ی که اطلاعات رو تغییر داده از نسخه خودش استفاده میکنه
در نتیجه RAM و CPU کمتری مصرف میشه
اگه بخوایم خیلی ساده توی یه جمله بگیم
Copy on Write
یعنی سیستم عامل تا وقتی مجبور نشده چیزی رو کپی نمیکنه و این یکی از مثال های خوبیه که نشون میده Process و Memory چقدر به هم وابسته هستن
@reverseengine
│
▼
یکی میخواد بنویسه
│
▼
نسخه جدا ساخته میشه
ولی در Shared Memory
Parent
│
▼
Shared Memory
▲
│
Child
اگه Parent اطلاعات رو تغییر بده Child میتونه تغییر رو ببینه
پس هدف این دوتا فرق داره
هدف COW اینه که fork بهینه تر انجام بشه و سیستم عامل مجبور نشه بی دلیل کل حافظه رو کپی کنه
هدف Shared Memory اینه که چند Process بتونن واقعا از یه فضای حافظه مشترک استفاده کنن
Copy on Write یا COW
بعد از fork لازم نیست کل حافظه فورا کپی بشه
Parent و Child
میتونن اول از Page های فیزیکی مشترک استفاده کنن
Page
ها برای جلوگیری از نوشتن مستقیم محافظت میشن
وقتی یکی از Process ها بخواد چیزی رو تغییر بده Page Fault اتفاق میفته
Kernel
این اتفاق رو مدیریت میکنه
یه نسخه جدا از اون Page ساخته میشه
Process
ی که اطلاعات رو تغییر داده از نسخه خودش استفاده میکنه
در نتیجه RAM و CPU کمتری مصرف میشه
اگه بخوایم خیلی ساده توی یه جمله بگیم
Copy on Write
یعنی سیستم عامل تا وقتی مجبور نشده چیزی رو کپی نمیکنه و این یکی از مثال های خوبیه که نشون میده Process و Memory چقدر به هم وابسته هستن
@reverseengine
❤1
ReverseEngineering
Copy on Write یا COW سیستم عامل چطور بدون کپی کردن همه چیز fork میسازه توی پست های قبلی گفتیم وقتی fork اجرا میشه یک Child Process ساخته میشه در نگاه اول شاید فکر کنیم سیستم عامل این کارو میکنه Parent Process │ ▼ کپی کامل حافظه │ ▼ Child Process یعنی کل…
Copy on Write or COW
How does the operating system create a fork without copying everything
In previous posts, we said that when a fork is executed, a Child Process is created
At first glance, we might think that the operating system does this
Parent Process
│
▼
Copying the entire memory
│
▼
Child Process
That is, it takes the entire memory of the Parent and copies it again for the Child
But this has a big problem
Suppose how many gigabytes of memory the Parent has used
If the operating system copies the entire memory every time a fork is executed
It takes a lot of time
It uses a lot of RAM
The CPU
also gets unnecessarily busy copying information
But this time the operating system acts a little more wisely
Copy on Write or COW solution
The operating system says
Don't copy anything for now
After the fork, at first the Parent and Child can use the same memory pages
Simply
Physical Memory
│
┌───────────────┐
│ │
Parent Child
│ │
└─�
│
Shared Page
As long as neither of them has changed anything, there is no need to create a separate copy
Now what happens if one of them wants to change something
Suppose Parent and Child initially have this value
int x = 10
Now Child wants to do this
x = 20
Here the operating system understands that Child wants to change the shared Page
So this is what happens
Before changing
Parent
│
▼
┌──────────────┐
│ Page x = 10 │
└───────────┘
▲
│
Child
That is, both of them have access to the same Page for now
Now Child wants to write something to that Page
The operating system does not copy the entire memory
It copies only the Page that is needed
Then the result will be something like this
Physical Memory
┌─
┌───────────────────────────────────
│ Page x = 20 │
│ Child │
└──────────────────────────────────────────� This is how
fork
│
▼
Currently, the entire memory is not copied
│
▼
Child is executed
│
▼
exec
│
▼
New program is replaced
As a result, a lot of time and memory is saved
What happens behind the scenes
After fork, the operating system can keep the Parent and Child Pages as Shared
But it protects these Pages for writing
Now if one of the Processes wants to write to such a Page
Write Attempt
│
▼
CPU detects protected write
│
▼
Page Fault
│
▼
Kernel handles it
│
▼
Copy the Page
│
▼
Give the writer its own Page
│
▼
Continue execution
That is, Page Fault does not always mean that the program is broken
Sometimes Page Fault is a completely normal occurrence in memory management
This will be much more useful when we get to Virtual Memory and Paging
An important point
Suppose a program changes a variable in Parent and you expect Child to see the new value too
If Parent and Child are two independent processes. Usually this expectation is not correct. After one of the processes changes that page due to COW, each has its own version. That is, Parent Memory│
▼
Separate Page
Child Memory│
▼
Separate Page
So:
Parent Memory ≠ Child Memory
This is important for analyzing multiprocess programs. For example, when debugging, Parent and Child may initially behave similarly. But after one of them changes its memory, their paths diverge. Don't confuse COW with Shared Memory. These two look similar, but they are not the same. In COW, the data may be shared. One wants to write. Separate versions are created. But in Shared Memory,
Parent│
▼
Shared Memory▲
│
Child
If Parent changes the data, Child can see the change. So the purpose of these two is different. The purpose of COW is to make fork more efficient. And the operating system does not have to copy the entire memory for no reason
The purpose of Shared Memory is that several processes can actually use a shared memory space
Copy on Write or COW
After fork, the entire memory does not have to be copied immediately
Parent and Child
can use shared physical pages first
Pages
are protected to prevent direct writing
When one of the processes tries to change something, a Page Fault occurs
Kernel
handles this event
A separate copy of that page is created
The process
that has changed the information uses its own copy
How does the operating system create a fork without copying everything
In previous posts, we said that when a fork is executed, a Child Process is created
At first glance, we might think that the operating system does this
Parent Process
│
▼
Copying the entire memory
│
▼
Child Process
That is, it takes the entire memory of the Parent and copies it again for the Child
But this has a big problem
Suppose how many gigabytes of memory the Parent has used
If the operating system copies the entire memory every time a fork is executed
It takes a lot of time
It uses a lot of RAM
The CPU
also gets unnecessarily busy copying information
But this time the operating system acts a little more wisely
Copy on Write or COW solution
The operating system says
Don't copy anything for now
After the fork, at first the Parent and Child can use the same memory pages
Simply
Physical Memory
│
┌───────────────┐
│ │
Parent Child
│ │
└─�
│
Shared Page
As long as neither of them has changed anything, there is no need to create a separate copy
Now what happens if one of them wants to change something
Suppose Parent and Child initially have this value
int x = 10
Now Child wants to do this
x = 20
Here the operating system understands that Child wants to change the shared Page
So this is what happens
Before changing
Parent
│
▼
┌──────────────┐
│ Page x = 10 │
└───────────┘
▲
│
Child
That is, both of them have access to the same Page for now
Now Child wants to write something to that Page
The operating system does not copy the entire memory
It copies only the Page that is needed
Then the result will be something like this
Physical Memory
┌─
┌───────────────────────────────────
│ Page x = 20 │
│ Child │
└──────────────────────────────────────────� This is how
fork
│
▼
Currently, the entire memory is not copied
│
▼
Child is executed
│
▼
exec
│
▼
New program is replaced
As a result, a lot of time and memory is saved
What happens behind the scenes
After fork, the operating system can keep the Parent and Child Pages as Shared
But it protects these Pages for writing
Now if one of the Processes wants to write to such a Page
Write Attempt
│
▼
CPU detects protected write
│
▼
Page Fault
│
▼
Kernel handles it
│
▼
Copy the Page
│
▼
Give the writer its own Page
│
▼
Continue execution
That is, Page Fault does not always mean that the program is broken
Sometimes Page Fault is a completely normal occurrence in memory management
This will be much more useful when we get to Virtual Memory and Paging
An important point
Suppose a program changes a variable in Parent and you expect Child to see the new value too
If Parent and Child are two independent processes. Usually this expectation is not correct. After one of the processes changes that page due to COW, each has its own version. That is, Parent Memory│
▼
Separate Page
Child Memory│
▼
Separate Page
So:
Parent Memory ≠ Child Memory
This is important for analyzing multiprocess programs. For example, when debugging, Parent and Child may initially behave similarly. But after one of them changes its memory, their paths diverge. Don't confuse COW with Shared Memory. These two look similar, but they are not the same. In COW, the data may be shared. One wants to write. Separate versions are created. But in Shared Memory,
Parent│
▼
Shared Memory▲
│
Child
If Parent changes the data, Child can see the change. So the purpose of these two is different. The purpose of COW is to make fork more efficient. And the operating system does not have to copy the entire memory for no reason
The purpose of Shared Memory is that several processes can actually use a shared memory space
Copy on Write or COW
After fork, the entire memory does not have to be copied immediately
Parent and Child
can use shared physical pages first
Pages
are protected to prevent direct writing
When one of the processes tries to change something, a Page Fault occurs
Kernel
handles this event
A separate copy of that page is created
The process
that has changed the information uses its own copy
❤1
ReverseEngineering
Copy on Write یا COW سیستم عامل چطور بدون کپی کردن همه چیز fork میسازه توی پست های قبلی گفتیم وقتی fork اجرا میشه یک Child Process ساخته میشه در نگاه اول شاید فکر کنیم سیستم عامل این کارو میکنه Parent Process │ ▼ کپی کامل حافظه │ ▼ Child Process یعنی کل…
As a result, less RAM and CPU are consumed
To put it very simply in a sentence
Copy on Write
means that the operating system does not copy anything until it is forced to, and this is one of the good examples that shows how interdependent Process and Memory are
@reverseengine
To put it very simply in a sentence
Copy on Write
means that the operating system does not copy anything until it is forced to, and this is one of the good examples that shows how interdependent Process and Memory are
@reverseengine
❤1
بخش بیست و هشتم بافر اورفلو
AFL++ و Instrumentation
چطور Fuzzer میفهمه داخل برنامه چه خبره
توی بخش قبل با libFuzzer و Coverage Guided Fuzzing آشنا شدیم
حالا میریم سراغ AFL++
AFL++
یکی از معروف ترین ابزارهای Fuzzing هست که مخصوصا برای تست برنامه های C و ++C و Binary ها خیلی استفاده میشه
میخوایم بفهمیم AFL++ چطور متوجه میشه یه ورودی برنامه رو وارد یه مسیر جدید کرده
AFL++
فقط ورودی تصادفی نمیفرسته
فرض کن یه برنامه داریم که مسیرهای مختلفی داره
Input
│
▼
┌─────────┐
│ Check A │
└────┬────┘
│
┌────┴────┐
▼ ▼
Path 1 Path 2
│
▼
┌─────────┐
│ Check B │
└────┬────┘
│
┌────┴────┐
▼ ▼
Path 3 Path 4
اگر AFL++ یه ورودی بفرسته و برنامه وارد Path 1 بشه
اون مسیر ثبت میشه
بعد AFL++ ورودی رو تغییر میده و دوباره امتحانش میکنه
اگر ورودی جدید باعث بشه برنامه وارد Path 2 بشه
AFL++
متوجه میشه یه مسیر جدید پیدا شده
همین ورودی جدید ارزشمند میشه و نگهش میداره
Instrumentation
یعنی چی
اینجا میرسیم به بخش مهم
Instrumentation
یعنی اضافه کردن یه سری مکانیزم به برنامه تا بتونیم بفهمیم موقع اجرا چه اتفاقی داخلش افتاده
مثلا AFL++ میتونه با Instrumentation اطلاعاتی درباره مسیر اجرای برنامه جمع کنه
به زبون ساده:
قبل از Instrumentation
Input
│
▼
Program
│
▼
Result
بعد از Instrumentation
Input
│
▼
Program
│
▼
Path Tracking
│
▼
Result
یعنی برنامه همچنان کار خودش رو انجام میده ولی حالا یه نفر هم داره بررسی میکنه برنامه از چه مسیرهایی رد شده
اسم این کار رو گذاشتن Instrumentation تا قضیه یکم علمی تر به نظر بیاد
یه مثال ساده:
فرض کنید این برنامه رو داریم
C
#include <stdio.h>
#include <string.h>
int main(void)
{
char input[32];
if (!fgets(input, sizeof(input), stdin))
return 0;
if (strncmp(input, "HELLO", 5) == 0)
{
puts("First check passed");
if (input[5] == '!')
{
puts("Second check passed");
}
}
return 0;
}
AFL++
اینجا دنبال چیه
اول ممکنه ورودی های ساده رو امتحان کنه
AAAA
این ورودی فقط یه مسیر معمولی رو اجرا میکنه
بعد AFL++ شروع میکنه ورودی رو تغییر دادن
اگر به این برسه
HELLO
یه شاخه جدید اجرا میشه
پس AFL++ متوجه میشه این ورودی جالبه
بعد همین ورودی رو بیشتر تغییر میده
مثلا:
HELLO!
حالا شرط دوم هم رد شده
پس یه مسیر جدید دیگه پیدا شده
به صورت ساده میتونیم این روند رو اینطوری ببینیم
AAAA
│
▼
مسیر معمولی
│
▼
AFL++ تغییر میده
│
▼
HELLO
│
▼
مسیر جدید
│
▼
AFL++ دوباره تغییر میده
│
▼
HELLO!
│
▼
مسیر جدیدتر
Seed یا Corpus
یعنی چی
AFL++
معمولا با یه سری ورودی اولیه شروع میکنه به این ورودی های اولیه میگیم Seed
مثلا یه فایل ساده
project
├── input
│ └── seed1
└── output
داخل seed1 میتونه فقط این باشه
AAAA
بعد AFL++ همین ورودی رو بارها تغییر میده
حذف میکنه
اضافه میکنه
بایت ها رو تغییر میده
و بررسی میکنه کدوم تغییر باعث شده یه مسیر جدید پیدا بشه
ورودی هایی که ارزش داشته باشن میتونن در Corpus قرار بگیرن
Corpus
یعنی مجموعه ای از ورودی های جالب که Fuzzer میتونه از اونها برای ادامه Fuzzing استفاده کنه
پس میتونیم این روند رو اینطوری تصور کنیم
Seed
│
▼
Mutation
│
├── Input A ──► مسیر جدید نیست
│
├── Input B ──► مسیر جدید
│ │
│ ▼
│ Corpus
│
└── Input C ──► مسیر جدیدتر
│
▼
Corpus
یک مثال از ساختار فایل ها
فرض کنید این پوشه رو داریم
project
├── input
│ └── seed1
└── output
داخل seed1 میتونه فقط این باشه
AAAA
بعد AFL++ با همین ورودی شروع میکنه و به مرور ورودی های جدید تولید میکنه
یک نکته مهم
وقتی AFL++ یه Crash پیدا میکنه
کار تموم نشده تازه قسمت جذاب ماجرا شروع میشه
باید بفهمیم
چه ورودی باعث Crash شده
Crash
دقیقا کجا اتفاق افتاده
چه تابعی درگیر بوده
آیا مشکل واقعا یه Memory Bug هست یا نه
❤1
برای این مرحله معمولا میریم سراغ ابزارهایی مثل
پس مسیر کلی کار میتونه این شکلی باشه
Seed Input
│
▼
AFL++
│
▼
New Paths
│
▼
Interesting Input
│
▼
Crash
│
▼
GDB
│
▼
Root Cause Analysis
یعنی Fuzzer یه ورودی جالب پیدا میکنه
بعد ممکنه همین ورودی باعث Crash بشه
از اینجا به بعد ما وارد ماجرا میشیم و باید بفهمیم علت واقعی مشکل چی بوده
AFL++
با فرستادن ورودی های مختلف فقط دنبال Crash نیست مسیرهای جدید برنامه رو هم دنبال میکنه
Instrumentation
کمک میکنه بفهمیم یه ورودی چه بخش هایی از برنامه رو اجرا کرده
هر مسیر جدید میتونه یه راه جدید برای رسیدن به یه باگ پنهان باشه
و وقتی Crash پیدا شد
کار Fuzzer تموم میشه
و نوبت ما میرسه که علت واقعی مشکل رو پیدا کنیم
تمرین:
همین برنامه رو با یه شرط سوم تغییر بدید
مثلا بعد از !HELLO یه شرط جدید اضافه کنید بعد روی کاغذ فکر کنید AFL++ چه ورودی هایی ممکنه به ترتیب پیدا کنه تا هر سه مسیر برنامه رو پوشش بده
@reverseengine
GDB
Ghidra
IDA
ASan
پس مسیر کلی کار میتونه این شکلی باشه
Seed Input
│
▼
AFL++
│
▼
New Paths
│
▼
Interesting Input
│
▼
Crash
│
▼
GDB
│
▼
Root Cause Analysis
یعنی Fuzzer یه ورودی جالب پیدا میکنه
بعد ممکنه همین ورودی باعث Crash بشه
از اینجا به بعد ما وارد ماجرا میشیم و باید بفهمیم علت واقعی مشکل چی بوده
AFL++
با فرستادن ورودی های مختلف فقط دنبال Crash نیست مسیرهای جدید برنامه رو هم دنبال میکنه
Instrumentation
کمک میکنه بفهمیم یه ورودی چه بخش هایی از برنامه رو اجرا کرده
هر مسیر جدید میتونه یه راه جدید برای رسیدن به یه باگ پنهان باشه
و وقتی Crash پیدا شد
کار Fuzzer تموم میشه
و نوبت ما میرسه که علت واقعی مشکل رو پیدا کنیم
تمرین:
همین برنامه رو با یه شرط سوم تغییر بدید
مثلا بعد از !HELLO یه شرط جدید اضافه کنید بعد روی کاغذ فکر کنید AFL++ چه ورودی هایی ممکنه به ترتیب پیدا کنه تا هر سه مسیر برنامه رو پوشش بده
@reverseengine
ReverseEngineering
بخش بیست و هشتم بافر اورفلو AFL++ و Instrumentation چطور Fuzzer میفهمه داخل برنامه چه خبره توی بخش قبل با libFuzzer و Coverage Guided Fuzzing آشنا شدیم حالا میریم سراغ AFL++ AFL++ یکی از معروف ترین ابزارهای Fuzzing هست که مخصوصا برای تست برنامه های C…
Part 28 Buffer Overflow
AFL++ and Instrumentation
How does a Fuzzer understand what is going on inside a program
In the previous section, we learned about libFuzzer and Coverage Guided Fuzzing
Now let's move on to AFL++
AFL++
is one of the most famous fuzzing tools, which is especially used for testing C, C++, and binary programs. We want to understand how AFL++ understands that a program input has entered a new path
AFL++
does not just send random input
Suppose we have a program that has different paths
Input
│
▼
┌────────┐
│ Check A │
└────┬────┘
│
┌─────┴───┐
▼ ▼
Path 1 Path 2
│
▼
┌───────┐
│ Check B │
└──────┬───┘
│
┌──────┐
│ ┌──────┐
▼ ▼
Path 3 Path 4
If AFL++ sends an input and the program enters Path 1
That path is recorded
Then AFL++ changes the input and tries it again
If the new input causes the program to enter Path 2
AFL++
notices that a new path has been found
This new input becomes valuable and keeps it
What does Instrumentation mean
Here we come to the important part
Instrumentation
means adding a series of mechanisms to the program so that we can understand what happened inside it during execution
For example, AFL++ can collect information about the path of the program execution with Instrumentation
In simple terms:
Before Instrumentation
Input
│
▼
Program
│
▼
Result
After Instrumentation
Input
│
▼
Program
│
▼
Path Tracking
│
▼
Result
That means the program is still doing its job, but now someone is also checking which paths the program has taken
I named this task Instrumentation to make it seem a little more scientific
An example Simple:
Suppose we have this program
C
#include <stdio.h>
#include <string.h>
int main(void)
{
char input[32];
if (!fgets(input, sizeof(input), stdin))
return 0;
if (strncmp(input, "HELLO", 5) == 0)
{
puts("First check passed");
if (input[5] == '!')
{
puts("Second check passed");
}
}
return 0;
}
What is AFL++ looking for here?
First it might try simple inputs
AAAA
This input just executes a normal path
Then AFL++ starts modifying the input
If it reaches
HELLO
a new branch is executed
So AFL++ realizes that this input is interesting
Then it modifies this input further
For example:
HELLO!
Now the second condition is also met
So another new path has been found
Simply we can see this process like this
AAAA
│
▼
Normal path
│
▼
AFL++ changes
│
▼
HELLO
│
▼
New path
│
▼
AFL++ changes again
│
▼
HELLO!
│
▼
Newer path
Seed or Corpus
What does it mean
AFL++
Usually starts with a series of initial inputs, we call these initial inputs Seed
For example, a simple file
project
├── input
│ └── seed1
└── output
Inside seed1 can be just this
AAAA
Then AFL++ changes this input repeatedly
deletes
adds
changes bytes
and checks which change caused a new path to be found
Inputs that are worth it can be placed in Corpus
Corpus
means a set of interesting inputs that the Fuzzer can use to continue Fuzzing
So we can imagine this process like this
Seed
│
▼
Mutation
│
├── Input A ──► Not a new path
│
├── Input B ──► New path
│ │
│ ▼
│ Corpus
│
└── Input C ──► Path Newer
│
▼
Corpus
An example of file structure
Suppose we have this folder
project
├── input
│ └── seed1
└── output
Inside seed1 can be just this
AAAA
Then AFL++ starts with this input and gradually generates new inputs
An important point
When AFL++ finds a Crash
The work is not over yet, the interesting part begins
We need to find out
What input caused the Crash
Where exactly did the Crash happen
What function was involved
Is the problem really a Memory Bug or not
For this step, we usually go to tools like
So the general path of the work can be like this
Seed Input
│
▼
AFL++
│
▼
New Paths
│
▼
Interesting Input
│
▼
Crash
│
▼
GDB
│
▼
Root Cause Analysis
That is, the Fuzzer finds an interesting input
Then this input may cause a Crash
From here on, we get into the story and we need to find out what the real cause of the problem was
AFL++
By sending different inputs, it does not only look for Crash, it also follows new paths of the program
Instrumentation
helps us understand what parts of the program an input has executed
Each new path can be a new way to reach a hidden bug
And when a Crash is found
The Fuzzer's work is finished
And it's our turn to find the real cause of the problem
Exercise:
Change the same program with a third condition
For example, add a new condition after !HELLO On paper, think about what inputs AFL++ might find in order to cover all three paths of the program
@reverseengine
GDB
Ghidra
IDA
ASan
So the general path of the work can be like this
Seed Input
│
▼
AFL++
│
▼
New Paths
│
▼
Interesting Input
│
▼
Crash
│
▼
GDB
│
▼
Root Cause Analysis
That is, the Fuzzer finds an interesting input
Then this input may cause a Crash
From here on, we get into the story and we need to find out what the real cause of the problem was
AFL++
By sending different inputs, it does not only look for Crash, it also follows new paths of the program
Instrumentation
helps us understand what parts of the program an input has executed
Each new path can be a new way to reach a hidden bug
And when a Crash is found
The Fuzzer's work is finished
And it's our turn to find the real cause of the problem
Exercise:
Change the same program with a third condition
For example, add a new condition after !HELLO On paper, think about what inputs AFL++ might find in order to cover all three paths of the program
@reverseengine
From Chrome renderer code exec to kernel with MSG_OOB
https://projectzero.google/2025/08/from-chrome-renderer-code-exec-to-kernel.html
@reverseengine
https://projectzero.google/2025/08/from-chrome-renderer-code-exec-to-kernel.html
@reverseengine
projectzero.google
From Chrome renderer code exec to kernel with MSG_OOB
IntroductionIn early June, I was reviewing a new Linux kernel feature when I learned about the MS...
State divergence enables unauthorized access
https://blog.trailofbits.com/2026/08/25/state-divergence-enables-unauthorized-access
@reverseengine
https://blog.trailofbits.com/2026/08/25/state-divergence-enables-unauthorized-access
@reverseengine
The Trail of Bits Blog
State divergence enables unauthorized access
We found and reported a bug in Provenance Blockchain, a public proof-of-stake chain built on Cosmos SDK, that lets any user grant themselves admin control over marker accounts without holding a single token.
From P-Code to GNN: extract binary code semantics
https://blog.quarkslab.com/from-p-code-to-gnn-extract-binary-code-semantics.html
@reverseengine
https://blog.quarkslab.com/from-p-code-to-gnn-extract-binary-code-semantics.html
@reverseengine
Quarkslab
From P-Code to GNN: extract binary code semantics - Quarkslab's blog
pcode_graph is a Python library, published by Quarkslab, suitable to build semantic graphs from binary code. We present how to use it to detect function similarities in binaries.
Kernel Shield: Reversing the NSecKrnl (Malops.io) Rootkit Driver
https://medium.com/@sushant.m.mane/kernel-shield-reversing-the-nseckrnl-malops-io-rootkit-driver-fb4af6bcb19c
https://medium.com/@sushant.m.mane/kernel-shield-reversing-the-nseckrnl-malops-io-rootkit-driver-fb4af6bcb19c
Medium
Kernel Shield: Reversing the NSecKrnl (Malops.io) Rootkit Driver
Challenge URL: https://malops.io/challenges/kernel-shield
Reverse Engineering a 0day used Against EDRs
https://medium.com/@jehadbudagga/reverse-engineering-a-0day-used-against-crowdstrike-edr-a5ea1fbe3fd4
https://medium.com/@jehadbudagga/reverse-engineering-a-0day-used-against-crowdstrike-edr-a5ea1fbe3fd4
Medium
Reverse Engineering a 0day used Against EDRs
Hello again…
😁3
Forwarded from DarkBit
📌 مقاله جدید منتشر شد
⚔️ The Art of DLL Hijacking
در این مقاله به بررسی عمیق مکانیزمهای DLL Hijacking در ویندوز پرداختهام؛ از مبانی نحوه بارگذاری DLLها و DLL Search Order Hijacking تا تکنیکهای پیشرفتهتر مانند:
◾️ DLL Search Order Hijacking
◾️ DLL Substitution (Replacement)
◾️ DLL Side-Loading
◾️ DLL Proxying
◾️ Phantom DLL Hijacking
در این مقاله بررسی میکنیم که Windows Loader چگونه DLLها را resolve و بارگذاری میکند، چگونه شرایط مناسب برای DLL Hijacking شناسایی میشوند، و چرا برخی برنامههای معتبر میتوانند به بخشی از زنجیره بارگذاری تبدیل شوند.
همچنین یک رویکرد عملی برای تحلیل و شناسایی Phantom DLLها ارائه شده است.
مطالعه مقاله:
https://darkbitx.github.io/posts/the-art-of-dll-hijacking/
✍️ نویسنده: دارکبیت | DarkBit
💬 Forum
📣 DarkBit
#CyberSecurity #WindowsInternals #Persistence #Maldev #RedTeam
⚔️ The Art of DLL Hijacking
در این مقاله به بررسی عمیق مکانیزمهای DLL Hijacking در ویندوز پرداختهام؛ از مبانی نحوه بارگذاری DLLها و DLL Search Order Hijacking تا تکنیکهای پیشرفتهتر مانند:
◾️ DLL Search Order Hijacking
◾️ DLL Substitution (Replacement)
◾️ DLL Side-Loading
◾️ DLL Proxying
◾️ Phantom DLL Hijacking
در این مقاله بررسی میکنیم که Windows Loader چگونه DLLها را resolve و بارگذاری میکند، چگونه شرایط مناسب برای DLL Hijacking شناسایی میشوند، و چرا برخی برنامههای معتبر میتوانند به بخشی از زنجیره بارگذاری تبدیل شوند.
همچنین یک رویکرد عملی برای تحلیل و شناسایی Phantom DLLها ارائه شده است.
مطالعه مقاله:
https://darkbitx.github.io/posts/the-art-of-dll-hijacking/
✍️ نویسنده: دارکبیت | DarkBit
💬 Forum
📣 DarkBit
#CyberSecurity #WindowsInternals #Persistence #Maldev #RedTeam
DarkBit
The Art of DLL Hijacking
An in-depth exploration of DLL Hijacking and related techniques, covering how DLL loading works, how vulnerable applications can be identified, and the differences between classic DLL Hijacking, DLL Sideloading, DLL Proxying, and Phantom DLL Hijacking.
DarkBit
📌 مقاله جدید منتشر شد ⚔️ The Art of DLL Hijacking در این مقاله به بررسی عمیق مکانیزمهای DLL Hijacking در ویندوز پرداختهام؛ از مبانی نحوه بارگذاری DLLها و DLL Search Order Hijacking تا تکنیکهای پیشرفتهتر مانند: ◾️ DLL Search Order Hijacking ◾️ DLL…
📌 New article released
⚔️ The Art of DLL Hijacking
In this article, I have taken an in-depth look at the mechanisms of DLL Hijacking in Windows; from the basics of how DLLs are loaded and DLL Search Order Hijacking to more advanced techniques such as:
◾️ DLL Search Order Hijacking
◾️ DLL Substitution (Replacement)
◾️ DLL Side-Loading
◾️ DLL Proxying
◾️ Phantom DLL Hijacking
In this article, we will examine how the Windows Loader resolves and loads DLLs, how suitable conditions for DLL Hijacking are identified, and why some legitimate programs can become part of the loading chain.
A practical approach to analyzing and identifying Phantom DLLs is also presented.
Read the article:
https://darkbitx.github.io/posts/the-art-of-dll-hijacking
✍️ Author: Darkbit | DarkBit
💬 Forum
📣 DarkBit
#CyberSecurity #WindowsInternals #Persistence #Maldev #RedTeam
⚔️ The Art of DLL Hijacking
In this article, I have taken an in-depth look at the mechanisms of DLL Hijacking in Windows; from the basics of how DLLs are loaded and DLL Search Order Hijacking to more advanced techniques such as:
◾️ DLL Search Order Hijacking
◾️ DLL Substitution (Replacement)
◾️ DLL Side-Loading
◾️ DLL Proxying
◾️ Phantom DLL Hijacking
In this article, we will examine how the Windows Loader resolves and loads DLLs, how suitable conditions for DLL Hijacking are identified, and why some legitimate programs can become part of the loading chain.
A practical approach to analyzing and identifying Phantom DLLs is also presented.
Read the article:
https://darkbitx.github.io/posts/the-art-of-dll-hijacking
✍️ Author: Darkbit | DarkBit
💬 Forum
📣 DarkBit
#CyberSecurity #WindowsInternals #Persistence #Maldev #RedTeam
DarkBit
The Art of DLL Hijacking
An in-depth exploration of DLL Hijacking and related techniques, covering how DLL loading works, how vulnerable applications can be identified, and the differences between classic DLL Hijacking, DLL Sideloading, DLL Proxying, and Phantom DLL Hijacking.
❤3
exit
وقتی یک Process کارش تموم میشه چه اتفاقی میوفته
تا اینجا با fork و exec و wait و حتی Zombie Process آشنا شدیم
حالا فرض کنید یه Process داریم که کارش رو انجام داده و دیگه کاری برای انجام دادن نداره
مثلا یه برنامه خیلی ساده داریم:
وقتی main تموم میشه برنامه هم باید به سیستم عامل بفهمونه که
کار من تموم شده
اینجاست که مفهوم exit وارد داستان میشه
exit دقیقا چیکار میکنه
خیلی ساده بخوایم بگیم
exit
به سیستم عامل میگه این Process دیگه کاری نداره و میخواد اجرای خودش رو تموم کنه
مثلا:
اون عدد 0 معمولا یعنی برنامه با موفقیت تموم شده
یعنی
exit(0)
↓
اجرای موفق
ولی اگه مقدار غیر صفر بدیم معمولا یعنی یه وضعیت دیگه اتفاق افتاده
مثلا یه خطا
البته معنی دقیق اون عدد رو خود برنامه مشخص میکنه
پس قرار نیست Kernel بفهمه عدد 1 دقیقا یعنی چه خطایی
این عدد بیشتر یه اطلاعاتیه که Process به Parent میده
بعد از exit چی میشه
اینجا یه نکته مهم داریم
وقتی Process به exit میرسه دیگه قرار نیست دستورهای معمول برنامه رو ادامه بده
یعنی اجرای برنامه تموم شده
ولی Kernel هنوز باید چندتا کار انجام بده
مثلا منابعی که Process استفاده میکرد باید مدیریت بشن
مثل:
منابع مربوط به I O
و اطلاعات مربوط به خود Process
ولی یه نکته خیلی مهم
همه چیز همون لحظه کامل تموم نمیشه
این همون جاییه که Zombie Process دوباره وارد داستان میشه
فرض کنید این وضعیت رو داریم
Parent
│
└──── Child
│
▼
exit()
│
▼
اجرای Child تموم شد
│
▼
Zombie
Child دیگه اجرا نمیشه
CPU
هم دیگه بهش برای اجرای برنامه CPU Time نمیده
ولی یه مقدار اطلاعات مربوط به پایانش هنوز توسط Kernel نگه داشته میشه
چرا
چون Parent باید بتونه بفهمه Child چطوری تموم شده
مثلا Exit Status اون چی بوده
بعد Parent میاد و wait میکنه
Child
│
▼
exit()
│
▼
Zombie
│
▼
Parent calls wait()
│
▼
Exit Status دریافت میشه
│
▼
Reaping
│
▼
Zombie از بین میره
به این مرحله آخر میگیم
Reaping
یعنی Parent وضعیت پایان Child رو میگیره و Kernel دیگه اطلاعات باقی مونده مربوط به اون Child رو لازم نداره
پس یه نکته خیلی مهم رو یادتون باشه
Termination با Reaping یکی نیست
Termination
یعنی Process دیگه اجرا نمیشه
Reaping
یعنی Parent وضعیت پایان Process رو دریافت میکنه و اطلاعات باقی مونده مربوط به اون Process از ساختارهای مدیریت Process جمع میشه
این تفاوت دقیقاً دلیل وجود Zombie Process هست
Exit Status چیه
وقتی یه Process تموم میشه میتونه یه مقدار به عنوان وضعیت پایان خودش داشته باشه
مثلا
یعنی
یا
یعنی
توی Unix و Linux معمولا 0 یعنی همه چیز خوب تموم شده
و مقدارهای غیر صفر معمولا برای وضعیتهای دیگه استفاده میشن
Parent
میتونه این مقدار رو با wait یا waitpid دریافت کنه
حالا کل داستان رو کنار هم بذاریم
تا الان چندتا تکه مهم از چرخه Process رو یاد گرفتیم
Parent
│
fork()
│
▼
Child
│
exec()
│
▼
New Program
│
exit()
│
▼
Zombie
│
wait()
│
▼
Reaped
یعنی اول Parent یه Child میسازه
بعد Child میتونه با exec یه Program جدید رو اجرا کنه
Program کارش رو انجام میده
بعد exit میکنه
Process دیگه اجرا نمیشه
ممکنه برای مدت کوتاهی Zombie بشه
بعد Parent با wait وضعیتش رو میگیره
و در نهایت Child Reap میشه
حالا یه سوال مهم
آیا exit یعنی Process همون لحظه از همه جا پاک میشه؟
نه
اینجا یکی از اون جاهاییه که سیستم عامل یه کم از چیزی که در نگاه اول به نظر میاد پیچیده تره
وقتی Process terminate میشه
دیگه اجرا نمیشه
ولی Kernel ممکنه یه مقدار اطلاعات محدود از اون رو نگه داره
مهمترین دلیلش هم اینه که Parent باید بتونه وضعیت پایان Child رو بفهمه
پس این دوتا رو قاطی نکنید
Termination
↓
Process دیگه اجرا نمیشه
Reaping
↓
Parent
وضعیت Process رو میگیره
↓
اطلاعات باقی مونده جمع میشه
یه نکته دیگه درباره منابع
وقتی Process تموم میشه Kernel منابعی که دیگه لازم نیستن رو Cleanup میکنه
وقتی یک Process کارش تموم میشه چه اتفاقی میوفته
تا اینجا با fork و exec و wait و حتی Zombie Process آشنا شدیم
حالا فرض کنید یه Process داریم که کارش رو انجام داده و دیگه کاری برای انجام دادن نداره
مثلا یه برنامه خیلی ساده داریم:
int main() {
printf("Hello");
return 0;
}
وقتی main تموم میشه برنامه هم باید به سیستم عامل بفهمونه که
کار من تموم شده
اینجاست که مفهوم exit وارد داستان میشه
exit دقیقا چیکار میکنه
خیلی ساده بخوایم بگیم
exit
به سیستم عامل میگه این Process دیگه کاری نداره و میخواد اجرای خودش رو تموم کنه
مثلا:
exit(0);
اون عدد 0 معمولا یعنی برنامه با موفقیت تموم شده
یعنی
exit(0)
↓
اجرای موفق
ولی اگه مقدار غیر صفر بدیم معمولا یعنی یه وضعیت دیگه اتفاق افتاده
مثلا یه خطا
البته معنی دقیق اون عدد رو خود برنامه مشخص میکنه
پس قرار نیست Kernel بفهمه عدد 1 دقیقا یعنی چه خطایی
این عدد بیشتر یه اطلاعاتیه که Process به Parent میده
بعد از exit چی میشه
اینجا یه نکته مهم داریم
وقتی Process به exit میرسه دیگه قرار نیست دستورهای معمول برنامه رو ادامه بده
یعنی اجرای برنامه تموم شده
ولی Kernel هنوز باید چندتا کار انجام بده
مثلا منابعی که Process استفاده میکرد باید مدیریت بشن
مثل:
Memory
File Descriptor
منابع مربوط به I O
و اطلاعات مربوط به خود Process
ولی یه نکته خیلی مهم
همه چیز همون لحظه کامل تموم نمیشه
این همون جاییه که Zombie Process دوباره وارد داستان میشه
فرض کنید این وضعیت رو داریم
Parent
│
└──── Child
│
▼
exit()
│
▼
اجرای Child تموم شد
│
▼
Zombie
Child دیگه اجرا نمیشه
CPU
هم دیگه بهش برای اجرای برنامه CPU Time نمیده
ولی یه مقدار اطلاعات مربوط به پایانش هنوز توسط Kernel نگه داشته میشه
چرا
چون Parent باید بتونه بفهمه Child چطوری تموم شده
مثلا Exit Status اون چی بوده
بعد Parent میاد و wait میکنه
Child
│
▼
exit()
│
▼
Zombie
│
▼
Parent calls wait()
│
▼
Exit Status دریافت میشه
│
▼
Reaping
│
▼
Zombie از بین میره
به این مرحله آخر میگیم
Reaping
یعنی Parent وضعیت پایان Child رو میگیره و Kernel دیگه اطلاعات باقی مونده مربوط به اون Child رو لازم نداره
پس یه نکته خیلی مهم رو یادتون باشه
Termination با Reaping یکی نیست
Termination
یعنی Process دیگه اجرا نمیشه
Reaping
یعنی Parent وضعیت پایان Process رو دریافت میکنه و اطلاعات باقی مونده مربوط به اون Process از ساختارهای مدیریت Process جمع میشه
این تفاوت دقیقاً دلیل وجود Zombie Process هست
Exit Status چیه
وقتی یه Process تموم میشه میتونه یه مقدار به عنوان وضعیت پایان خودش داشته باشه
مثلا
exit(0);
یعنی
Exit Status = 0
یا
exit(1);
یعنی
Exit Status = 1
توی Unix و Linux معمولا 0 یعنی همه چیز خوب تموم شده
و مقدارهای غیر صفر معمولا برای وضعیتهای دیگه استفاده میشن
Parent
میتونه این مقدار رو با wait یا waitpid دریافت کنه
حالا کل داستان رو کنار هم بذاریم
تا الان چندتا تکه مهم از چرخه Process رو یاد گرفتیم
Parent
│
fork()
│
▼
Child
│
exec()
│
▼
New Program
│
exit()
│
▼
Zombie
│
wait()
│
▼
Reaped
یعنی اول Parent یه Child میسازه
بعد Child میتونه با exec یه Program جدید رو اجرا کنه
Program کارش رو انجام میده
بعد exit میکنه
Process دیگه اجرا نمیشه
ممکنه برای مدت کوتاهی Zombie بشه
بعد Parent با wait وضعیتش رو میگیره
و در نهایت Child Reap میشه
حالا یه سوال مهم
آیا exit یعنی Process همون لحظه از همه جا پاک میشه؟
نه
اینجا یکی از اون جاهاییه که سیستم عامل یه کم از چیزی که در نگاه اول به نظر میاد پیچیده تره
وقتی Process terminate میشه
دیگه اجرا نمیشه
ولی Kernel ممکنه یه مقدار اطلاعات محدود از اون رو نگه داره
مهمترین دلیلش هم اینه که Parent باید بتونه وضعیت پایان Child رو بفهمه
پس این دوتا رو قاطی نکنید
Termination
↓
Process دیگه اجرا نمیشه
Reaping
↓
Parent
وضعیت Process رو میگیره
↓
اطلاعات باقی مونده جمع میشه
یه نکته دیگه درباره منابع
وقتی Process تموم میشه Kernel منابعی که دیگه لازم نیستن رو Cleanup میکنه