File Descriptors
are also closed during the termination of the Process
But this does not mean that the Resource itself is necessarily destroyed at that moment
For example, a File may be open by another Process
So the File Descriptor is not the same as the Resource itself
We will see this issue much more later in Linux Internals
An interesting point about return and exit
For example, if we write:
int main() {
return 0;
}
When main ends, the program ends normally
But if we write
exit(0);
Here, the program termination request is given directly
That's why in simple programs, the result of both may seem almost the same
But in terms of the program execution path, they are not exactly the same
We also have another interesting difference
exit
is not the same as exit_
In C, the exit function can perform some User Space cleanups before the program ends
For example, it can execute Handlers registered with atexit
Or flush the stdio buffers
But exit_ does not perform these User Space cleanups and directly terminates the Process
This difference becomes very important when we get into the discussion of fork, buffering, and system calls
Another point is that a Process does not end only with exit
A Process may also terminate due to a Signal
So the overall story could be like this
Process
│
┌───────────────┐
│ │
exit() Signal
│ │
└─�
▼
Termination
│
▼
Exit Information
│
▼
Zombie
│
wait()
│
▼
Reaped
Now let's move on to Reverse Engineering
Suppose you are examining a Binary
In the Trace you see something like this happen
fork()
↓
exec()
↓
Program Execution
↓
exit()
Now you can have a mental picture of what happened
New Process created
↓
Child started executing
↓
New Program executed
↓
Program did its job
↓
Process terminated
If you then see Parent waiting
Child
│
▼
exit()
│
▼
Zombie
│
▼
Parent
│
wait()
│
▼
Exit Status
│
▼
Reaping
You can understand that Parent was probably waiting for the result of Child execution
This is where the things we have learned so far are starting to come in handy for Reverse Engineering
Now a picture Have more complete than what we have learned so far
Parent Process
│
fork()
│
▼
Child Process
│
exec()
│
▼
Program
│
▼
Execution
│
exit()
│
▼
Termination
│
▼
Zombie
│
wait()
│
▼
Reaping
exit
Very simply,
Process
says I am done
After that
Process
↓
Termination
↓
Exit Status
↓
Parent gets the status with wait
↓
Reaping
↓
Zombie dies
So far we have four very important concepts of Process API
fork()
↓
Creating Child Process
exec()
↓
Executing a new program inside a Process
wait()
↓
Waiting or getting the status of Child
exit()
↓
Completing the execution of Process
Learn these four concepts well
Because when you later enter Linux Internals and System Calls And Reverse Engineering ELF binaries, we will come across the same concepts again and again
Just one thing to always remember
We are following the Unix and Linux model here
The internal details of Process, PCB and Process Descriptor may differ in different operating systems
For now, we will make this model our main model
@reverseengine