Having an overview of the running processes on the operating system is something we usually take for granted. We can’t think of working without fundamental features like that.
But how does the kernel keep track of the processes, which are currently running ? Today, we take a look at the corresponding structures of the Windows and the Linux system, which are responsible for holding track of the running processes.
Linux – Task structures
If you ever used Linux before, you are probably familiar with the ps
command, which allows you to print the list of all processes currently running on the system. We will dive into how the Linux kernel keeps track of these processes internally.
The kernel stores a list of processes in a doubly linked list, called the task list
. Each node in this list is a process descriptor of the type task_struct
. The definition of this task struct
can be found in linux/sched.h
[1] of Linus Torvald’s git repository.
If you checked out the code, you will realise that this structure is pretty extensive and we will not dive into every member of this structure. Our focus lies on understanding how the kernel handles this task list. As I’ve already explained, the kernel keeps track of all processes by a doubly linked list. Each task structure holds a member tasks
of type list_head
.
struct list_head {
struct list_head *next, *prev;
};
As you’ve probably already guessed, the next
pointer holds a reference, which allows us to retrieve the next task_struct
and the prev
field allows us to take a step back. We can write a simple to linux kernel module to iterate through the task list and print out all process names and process ids on the current system:
Iterating through the linked list
Task structures lie in kernel space, so accessing these is not possible without writing a kernel module. The code is pretty straight forward. We just use the init_task
as an initial entry point, which is the idle task running on the linux system. Iterating through the linked list is possible via the next_task
macro. Then we use the printk
function to log the comm
(process executable) member and the process id.
#include <linux/sched/task.h>
#include <linux/sched/signal.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Andreas Klopsch");
MODULE_DESCRIPTION("Simple module for printing task structure members");
MODULE_VERSION("0.1");
// get the top element in the task doubly linked list
extern struct task_struct init_task;
static int __init action_init(void){
struct task_struct task;
printk(KERN_INFO "Init task = %s", init_task.comm);
printk(KERN_INFO "Getting next task");
task = *(next_task(&init_task)); // deference pointer for convencience reasons
while(task.pid != init_task.pid) {
printk(KERN_INFO "Comm = %s pid = %d", task.comm, task.pid);
task = *(next_task(&task)); // dereference again, use macro to not iterate through list_head
}
return 0;
}
static void __exit action_exit(void){
printk(KERN_INFO "Stopping task iterator");
}
module_init(action_init);
module_exit(action_exit);
Windows – EPROCESS
On Windows, there are similarities with Linux. Each process on Windows is represented by an EPROCESS
structure, which is actually the representation of a process object. The EPROCESS
structure also contains a KPROCESS
structure, which holds information for the kernel.
As with Linux, this block contains various information relating to the corresponding process, like:
- Virtual Address Descriptors, holding the map of the process virtual memory
- Process ID
- Image base name
Another similarity with the Linux system, is the way the processes are linked with each other. EPROCESS
structures are connected to each other via a doubly linked list, called ActiveProcessLinks
. The next process in the list is referenced by FLink
and the previous process object is referenced by the BLink
pointer. One way of how this could be implemented, is iterating through the ActiveProcessLinks
structure again.
References
- Windows Internals, Part 1: System Architecture, Processes, Threads, Memory Management, and More
- Mastering Malware Analysis: The complete malware analyst’s guide to combating malicious software, APT, cybercrime, and IoT attacks