|=-----------------=[ Walking though processes under linux ]=-----------------=| |=----------------------------------------------------------------------------=| |=-----------------------=[ pouik ]=----------------------=| |=----------------------------------------------------------------------------=| |=----------------=[ Translated by N-0-X ]=----------------=| --[ Summary 1 - Introduction 2 - Retrieving init_task 2.1 - proc_root 2.2 - proc_root_operations 2.3 - proc_root_readdir 2.4 - proc_pid_readdir 2.5 - get_tgid_list 2.6 - init_task 3 - Let's rock 3.1 - name 3.2 - following address 3.3 - pid 3.4 - uid 4 - Conclusion --[ 1 Introduction In this article, we will quickly see how the 2.6 linux kernel manages the processes. It is easy to check the tasks with a lkm thanks to the for_each_process() function. In userland, we will use /dev/kmem and /dev/mem. We can also use kernel headers to get the task_struct structure even if there are often compilation problems and sources aren't always available. The main difficulty is looking for the addresses and the offsets of the structure fields. All the following tests were done on a i386 architecture with a 2.6.16 Linux kernel. The code corresponding to each point is available at [1]. We will use the term of kernel memory to talk about /dev/kmem and /dev/mem (even if we aren't supposed to ... ). --[ 2 Retrieving init_task #define for_each_process(p) \ for (p = &init_task ; (p = next_task(p)) != &init_task ; ) init_task is the very first process created by the kernel. Here are three methods to get it: 1 - System.map 2 - lkm 3 - kernel memory The two first ones don't have any interest at all, so we'll let the readers have fun with them all by themselves. :p The easiest way to think about it is to say that the process are shown in /proc, therefore the kernel must have used init_task. pouik@atlantis /usr/src/linux/fs/proc $ grep -R init_task *.c base.c: p = next_task(&init_task); base.c: for ( ; p != &init_task; p = next_task(p)) { proc_misc.c: cputime_t idletime = cputime_add(init_task.utime, init_task.stime); init_task is used twice in get_tgid_list. We will then have to find that function's address. pouik@atlantis /usr/src/linux/fs/proc $ grep get_tgid_list *.c base.c:static int get_tgid_list(int index, unsigned long version, unsigned int *tgids) base.c: nr_tgids = get_tgid_list(nr, next_tgid, tgid_array); This function is called by proc_pid_readdir, so we'll find that function's address. pouik@atlantis /usr/src/linux/fs/proc $ grep proc_pid_readdir *.c base.c:int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir) root.c: ret = proc_pid_readdir(filp, dirent, filldir); proc_root_readdir calls this function too, so let's do the same thing once more: pouik@atlantis /usr/src/linux/fs/proc $ grep proc_root_readdir *.c root.c:static int proc_root_readdir(struct file * filp, root.c: .readdir = proc_root_readdir, proc_root_readdir is the readdir field of the proc_root_operations structure. Its type is file_operations. pouik@atlantis /usr/src/linux/fs/proc $ grep proc_root_operations *.c root.c:static struct file_operations proc_root_operations = { root.c: .proc_fops = &proc_root_operations, As you can see, proc_root_operations is also a structure field of proc_root and its type is proc_dir_entry. In my article [2], c0de shows a method to find proc_root's address. Therefore, here is the correct procedure to retrieve init_task: => proc_root => proc_root_operation => proc_root_readdir => proc_pid_readdir => get_tgid_list => init_task --[ 2.1 proc_root C.f. [2] --[ 2.2 proc_root_operations struct proc_dir_entry proc_root = { .low_ino = PROC_ROOT_INO, .namelen = 5, .name = "/proc", .mode = S_IFDIR | S_IRUGO | S_IXUGO, .nlink = 2, .proc_iops = &proc_root_inode_operations, .proc_fops = &proc_root_operations, .parent = &proc_root, }; struct proc_dir_entry { unsigned int low_ino; unsigned short namelen; const char *name; mode_t mode; nlink_t nlink; uid_t uid; gid_t gid; unsigned long size; struct inode_operations * proc_iops; struct file_operations * proc_fops; get_info_t *get_info; struct module *owner; struct proc_dir_entry *next, *parent, *subdir; void *data; read_proc_t *read_proc; write_proc_t *write_proc; atomic_t count; /* use count */ int deleted; /* delete flag */ void *set; }; int + unsigned short + 6 + int + int + int + int + unsigned long + struct inode_operations * = 4 + 2 + 6 + 4 + 4 + 4 + 4 + 4 = 32 proc_root_operations can therefore be found by reading the memory at proc_root+32 (because of its position in the structure). --[ 2.3 proc_root_readdir static struct file_operations proc_root_operations = { .read = generic_read_dir, .readdir = proc_root_readdir, }; struct file_operations { struct module *owner; loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*aio_read) (struct kiocb *, char __user *, size_t, loff_t); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); ssize_t (*aio_write) (struct kiocb *, const char __user *, size_t, loff_t); int (*readdir) (struct file *, void *, filldir_t); [...] }; As you can see, proc_root_readir's address is proc_root_operations + 6 * 4 or proc_root_operations+24. --[ 2.4 proc_pid_readdir (gdb) disassemble proc_root_readdir Dump of assembler code for function proc_root_readdir: 0xc0180bf9 : push %ebp 0xc0180bfa : push %edi 0xc0180bfb : push %esi 0xc0180bfc : push %ebx [...] 0xc0180c4d : jmp 0xc0182d3f [...] All we have to do is find the first JMP in proc_root_readdir to find proc_pid_readdir's address. --[ 2.5 get_tgid_list (gdb) disassemble proc_pid_readdir Dump of assembler code for function proc_pid_readdir: 0xc0182d3f : push %ebp 0xc0182d40 : push %edi 0xc0182d41 : push %esi 0xc0182d42 : push %ebx [...] 0xc0182db3 : call 0xc0182c46 [...] To retrieve the address of get_tgid_list in proc_pid_readdir we also have to find the first call. --[ 2.6 init_task (gdb) disassemble get_tgid_list Dump of assembler code for function get_tgid_list: 0xc0182c46 : push %ebp 0xc0182c47 : mov $0xc0446c00,%eax 0xc0182c4c : push %edi 0xc0182c4d : push %esi [...] 0xc0182c97 : cmp $0xc03dbb60,%edx [...] Here, we have to find the first type of CMP to retrieve the address of init_task. In our example, it is 0xc03dbb60. --[ 3 Let's rock Now that we have the address of the first process (init_task), we have to define a couple offsets in the structure. For Zeppoo, we will only need four fields. - name - pid - uid/gid - address of the following process struct task_struct { [...] struct list_head tasks; struct list_head ptrace_children; struct list_head ptrace_list; [...] pid_t pid; pid_t tgid; [...] uid_t uid,euid,suid,fsuid; gid_t gid,egid,sgid,fsgid; [...] char comm[TASK_COMM_LEN]; [...] }; --[ 3.1 name struct task_struct { [...] char comm[TASK_COMM_LEN]; [...] }; (gdb) print init_task.comm $1 = "swapper\000\000\000\000\000\000\000\000" (gdb) x/4x init_task.comm 0xc0358ca4 : 0x70617773 0x00726570 0x00000000 0x00000000 To find the process name offset, we will use the name of the first process. That process is called "swapper", so by finding the string "\x73\x77\x61\x70\x70\x65\x72", we can find the offset by a simple subtraction. --[ 3.2 address of the following process struct task_struct { [...] struct list_head tasks; struct list_head ptrace_children; struct list_head ptrace_list; [...] }; struct list_head { struct list_head *next, *prev; }; (gdb) print &init_task $1 = (struct task_struct *) 0xc0358b00 (gdb) print init_task.tasks $2 = {next = 0xcbec9ab0, prev = 0xcbbc2600} (gdb) x/32x 0xc0358b00+40 0xc0358b28 : 0x00000000 0x00000000 0x00000000 0x5108a200 0xc0358b38 : 0x003d0dd7 0x5108a200 0x003d0dd7 0x30805400 0xc0358b48 : 0x003d0ddd 0x00000000 0x00000000 0x00000001 0xc0358b58 : 0x0000007d 0x00000000 0xcbec9ab0 0xcbbc2600 0xc0358b68 : 0xc0358b68 0xc0358b68 0xc0358b70 0xc0358b70 0xc0358b78 : 0x00000000 0xc12b56c0 0x00000000 0x00000000 0xc0358b88 : 0x00000000 0x00000000 0x00000000 0x00000000 0xc0358b98 : 0x00000000 0x00000000 0x00000000 0xc0358b00 To find the position of the task structure, we can use the addresses of ptrace_children and ptrace_list which both have the same next and prev address. In our example, they respectively are 0xc0358b68 and 0xc0358b70. (gdb) x/32x 0xcbec9ab0 0xcbec9ab0: 0xcbec95a0 0xc0358b60 0xcbec9ab8 0xcbec9ab8 0xcbec9ac0: 0xcbec9ac0 0xcbec9ac0 0xc12b56c0 0xc12b56c0 0xcbec9ad0: 0xc0360a10 0x00000000 0x00000000 0x00000000 0xcbec9ae0: 0x00000000 0x00000000 0x00000001 0x00000001 0xcbec9af0: 0x00000001 0xc0358b00 0xc0358b00 0xcbec95f4 0xcbec9b00: 0xcbb4a124 0xc0358bac 0xc0358bac 0xcbec9a50 0xcbec9b10: 0x00000001 0x00000000 0xc117f9e0 0xcbec9b1c 0xcbec9b20: 0xcbec9b1c 0x00000001 0x00000000 0xc11809e0 The init process address is 0xcbec9a50 (it can be found with a lkm). As you can see, two addresses (0xc0358b00 and 0xc0358bac) are seen twice and are pretty close to each-other. This will help us a lot to find the offset. --[ 3.3 pid struct task_struct { [...] pid_t pid; pid_t tgid; [...] }; From the process init: (gdb) x/32x 0xcbec9a50+96 0xcbec9ab0: 0xcbec95a0 0xc0358b60 0xcbec9ab8 0xcbec9ab8 0xcbec9ac0: 0xcbec9ac0 0xcbec9ac0 0xc12b56c0 0xc12b56c0 0xcbec9ad0: 0xc0360a10 0x00000000 0x00000000 0x00000000 0xcbec9ae0: 0x00000000 0x00000000 0x00000001 0x00000001 0xcbec9af0: 0x00000001 0xc0358b00 0xc0358b00 0xcbec95f4 0xcbec9b00: 0xcbb4a124 0xc0358bac 0xc0358bac 0xcbec9a50 0xcbec9b10: 0x00000001 0x00000000 0xc117f9e0 0xcbec9b1c 0xcbec9b20: 0xcbec9b1c 0x00000001 0x00000000 0xc11809e0 One method is to look for two consecutive 0x00000001 starting from the address of init + the task offset. --[ 3.4 uid struct task_struct { [...] uid_t uid,euid,suid,fsuid; gid_t gid,egid,sgid,fsgid; [...] }; (gdb) x/100x 0xcbec9a50+156 0xcbec9aec: 0x00000001 0x00000001 0xc0358b00 0xc0358b00 0xcbec9afc: 0xcbec95f4 0xcbb4a124 0xc0358bac 0xc0358bac 0xcbec9b0c: 0xcbec9a50 0x00000001 0x00000000 0xc117f9e0 0xcbec9b1c: 0xcbec9b1c 0xcbec9b1c 0x00000001 0x00000000 0xcbec9b2c: 0xc11809e0 0xcbec9b30 0xcbec9b30 0x00000000 0xcbec9b3c: 0x00000000 0x00000000 0xcbec9634 0xc0358bf4 0xcbec9b4c: 0x00000000 0x00000000 0x00000000 0xcbec9648 0xcbec9b5c: 0xc0358c08 0x00000000 0x00000000 0x00000000 0xcbec9b6c: 0x00000000 0x00000001 0x000000f1 0x00000abb 0xcbec9b7c: 0x0000038e 0x00000000 0x1f72b478 0x000000e0 0xcbec9b8c: 0x0000000a 0x00000000 0x00000000 0x00000000 0xcbec9b9c: 0x00000000 0xcbec9ba0 0xcbec9ba0 0xcbec9ba8 0xcbec9bac: 0xcbec9ba8 0xcbec9bb0 0xcbec9bb0 0x00000000 0xcbec9bbc: 0x00000000 0x00000000 0x00000000 0x00000000 0xcbec9bcc: 0x00000000 0x00000000 0x00000000 0xc035e480 0xcbec9bdc: 0xfffffeff 0xfffffeff 0xffffffff 0x00000000 0xcbec9bec: 0xc035e3e0 0x00000000 0x74696e69 0x00726500 0xcbec9bfc: 0x00000000 0x00000000 0x00000000 0x00000000 0xcbec9c0c: 0x00000000 0x3aa0ffff 0xb7dff2e4 0x00000000 0xcbec9c1c: 0x00000000 0x00000000 0x00000000 0xcbecfff8 0xcbec9c2c: 0x00000060 0xc02f39ec 0xcbecfe7c 0x00000000 0xcbec9c3c: 0x00000033 0x00000000 0x00000000 0x00000000 0xcbec9c4c: 0x00000000 0x00000000 0x00000000 0x00000000 0xcbec9c5c: 0x00000000 0x00000000 0x00000000 0x00000000 0xcbec9c6c: 0x00000000 0xffff037f 0xffff0000 0xffffffff For the init process, the fields uid,euid,suid,fsuid,gid,egid,sgid,fsgid have a value of 8 zeros, thus it is a quite simple search starting at 8 zeros + pid offset. In our example, the address is 0xcbec9bb8. --[ 4 Conclusion To find the offset of a field in a structure isn't complicated but it can vary depending on the kernel version. Regarding other offsets, the method is the same. If you use kgdb (which is what we used here), research is much easier. References : [1] http://www.zeppoo.net [2] http://www.ouah.org/p61_BONUS_BONUS.txt Thanks to: Yperite, bl, n0name, et tous contre DADVSI !