Day 2 – Understanding Process Management in C
Objective
The main goal for today is to get hands-on experience with process creation and management in C. You’ll learn how to create new processes and how child processes inherit variables and behavior from their parent processes. We’ll cover how to do this in both Unix (using 'fork()') and Windows (using 'CreateProcess()'), followed by a microproject to put our new skills to use.
Understanding Processes in C
A process is an instance of a running program. Every program that is executing in your operating system runs within its own process. For malware development, understanding how to create and manage processes is crucial because many malicious actions occur within new or injected processes.
In both Unix-based and Windows systems, processes can create child processes. A child process runs independently but can inherit certain properties from its parent process, such as file descriptors or environment variables. Let’s explore this in more detail.
Image Source: Uppsala University |
Creating Processes in Unix Systems
In Unix-based systems (such as Linux and macOS), the function 'fork()' is used to create a new process. When a process calls 'fork()', it creates a child process that runs the same code as the parent. The child process gets a copy of the parent’s variables but has its own separate memory space.
How does 'fork()' work?
- When you call 'fork()', it creates a new process (child) that is a duplicate of the parent process.
- The function returns two values:
- In the parent process, it returns the Process ID (PID) of the child.
- In the child process, it returns `0`.
Example: Process Creation with 'fork()'
#include <stdio.h>
#include <unistd.h>
int main()
{
int pid = fork(); // create a new process
if (pid < 0){
printf("Failed to create a new process.\n");
}
if (pid == 0){ // this block runs in the child process
printf("This is the child process. My PID is %d\n", getpid());
execlp("bin/ls","ls",NULL); // Execute the 'ls' command to list files
}
else { // this block runs in the parent process
printf("This is the parent process. My Child's PID is %d.\n", pid);
return 0;
}
}
OutputExplanation:
- We call 'fork()', and based on its return value, we determine whether we are in the parent or child process.
- In the child process, we use 'execlp()' to replace the current process image with a new program (in this case, the 'ls' command).
- The parent process continues running independently.
Creating Processes in Windows Systems
On Windows, process creation is done using the 'CreateProcess()' function. Unlike 'fork()' in Unix, 'CreateProcess()' allows you to directly specify which program the child process will execute.
'CreateProcess()' Syntax:
BOOL CreateProcess(
LPCSTR lpApplicationName, // Name of the program to execute
LPSTR lpCommandLine, // Command-line arguments
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation
);
Example: Process Creation with 'CreateProcess()'
Here’s a simple example that creates a child process to list files using the 'cmd.exe' command on Windows.
#include
#include
int main() {
STARTUPINFO si;
PROCESS_INFORMATION pi;
// Initialize memory for the structures
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// Create a new process to run 'cmd.exe' and list files
if (CreateProcess(NULL, "cmd.exe /c dir", NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
// Successfully created the child process
printf("Child process created. PID: %d\n", pi.dwProcessId);
// Wait until child process exits
WaitForSingleObject(pi.hProcess, INFINITE);
// Close process and thread handles
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
} else {
printf("Failed to create child process.\n");
}
return 0;
}
OutputExplanation:
- We initialize 'STARTUPINFO' and 'PROCESS_INFORMATION' structures that are required by 'CreateProcess()'.
- Then we call 'CreateProcess()' with 'cmd.exe /c dir' to execute the command to list files in the current directory.
- The parent process waits for the child process to finish by calling 'WaitForSingleObject()'.
Investigating Inheritance Between Parent and Child Processes
In Unix, child processes inherit the environment variables, file descriptors, and some resources from their parent process. However, changes made to variables in the child process do not affect the parent, because the memory is copied when 'fork()' is called.
For example, if the parent process has an environment variable, the child process can access it, but any modifications the child makes won’t reflect in the parent.
Example: Inheriting Environment Variables in Unix
#include
#include
#include
int main() {
printf("Parent process environment variable: %s\n", getenv("HOME"));
int pid = fork();
if (pid == 0) {
// Child process
printf("Child process inherited environment variable: %s\n", getenv("HOME"));
}
return 0;
}
OutputIn Windows, 'CreateProcess()' has a parameter 'bInheritHandles' that determines whether the new process inherits handles from the parent process.
Microproject – Create a Child Process to List Files
Let’s put what we’ve learned into practice with a microproject. Your goal is to write a C program that creates a child process. The child process should execute a simple task, like listing the files in a directory.
Instructions:
Unix Approach
- Use 'fork()' to create a new child process.
- In the child process, use 'execlp()' to execute the 'ls' command and list the files in the current directory.
#include
#include
#include
#include
int main() {
// Create a new process using fork()
int pid = fork();
if (pid < 0) {
// If fork() returns a negative value, the process creation failed
printf("Failed to create a new process.\n");
return 1;
}
if (pid == 0) {
// This block is executed by the child process
printf("Child process (PID: %d) is running 'ls' to list files in the current directory...\n", getpid());
// Replace the current process image with a new program (ls command)
// execlp takes the program name and its arguments. NULL marks the end of the argument list.
execlp("/bin/ls", "ls", NULL);
// If execlp() fails, we print an error message (this line is reached only if execlp fails)
printf("Error: Failed to execute 'ls' command\n");
return 1;
} else {
// This block is executed by the parent process
printf("Parent process (PID: %d) is waiting for the child process (PID: %d) to complete...\n", getpid(), pid);
// Wait for the child process to finish
wait(NULL);
// After the child process completes, the parent resumes
printf("Child process completed. Parent process resuming execution.\n");
}
return 0;
}
Make sure to handle any errors (e.g., failed process creation) and test the program on your system.Conclusion
By the end of Day 2, you should have a solid understanding of how processes are created and managed in C, both in Unix and Windows environments. This knowledge is crucial when developing malware, as creating and controlling processes allows malicious code to execute tasks like running shell commands, injecting code, or even hiding its activity. Keep practicing, and tomorrow we’ll explore file manipulation and system calls, taking us further down the path of malware development!
Happy Hacking! ❤
No comments:
Post a Comment