Playing with /proc
Credits for image: deepai.org
/proc
is a virtual file system that contains runtime system information like system/process memory, mounted devices, hardware information etc. The kernel saves the state to to a specific set of files which can be utilized by the kernel itself or other programs interacting with the system. These files contain information such as the range of memory, state, file descriptors used by a process, the current CPU’s make, model, type, the type of filesystems supported by the kernel etc.
There is a plethora of information that can be derived from the /proc/
fs. In this article, we’ll be focusing on few major components like processes and system state information. The rest of ‘em, I’ll leave them to your homework.
Sidenote: I’m writing this article because my project Kunai extensively uses the procfs to manipulate values of running processes. Thought I would share my knowledge to the world as well :)
Getting system information
Just for the sakes of it, try listing the files present in /proc
. What do you find? Some weird gibberish like this?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
~ ls -F /proc
1/ 122692/ 204/ 249/ 433145/ 483834/ 488875/ 779/ interrupts
100/ 122717/ 21/ 25/ 434833/ 484146/ 488885/ 78/ iomem
101/ 122769/ 210/ 250/ 435738/ 484676/ 489282/ 787/ ioports
102/ 124/ 211/ 251/ 437394/ 484840/ 489309/ 79/ irq/
104/ 1240/ 212/ 252/ 437396/ 484907/ 489310/ 796/ kallsyms
105/ 1242/ 213/ 253/ 439/ 484908/ 489338/ 797/ kcore
106/ 1246/ 214/ 254/ 439388/ 484913/ 489383/ 798/ keys
106048/ 125/ 215/ 255/ 440172/ 485263/ 49/ 799/ key-users
106059/ 1251/ 216/ 256/ 440388/ 485500/ 5/ 800/ kmsg
106060/ 1255/ 217/ 28/ 440954/ 485965/ 52/ 801/ kpagecgroup
106062/ 1257/ 218/ 29/ 441521/ 485967/ 53/ 803/ kpagecount
106064/ 1259/ 219/ 3/ 441929/ 486259/ 530/ 82/ kpageflags
106070/ 126/ 22/ 30/ 442536/ 486321/ 54/ 83/ latency_stats
106071/ 127/ 220/ 300/ 442967/ 486439/ 542/ 84/ loadavg
106072/ 128/ 221/ 301/ 444234/ 486515/ 546/ 844/ locks
106075/ 1287/ 222/ 31/ 445444/ 486796/ 547/ 847/ meminfo
106086/ 13/ 223/ 328693/ 451067/ 486801/ 548/ 85/ misc
106116/ 130/ 224/ 328797/ 452888/ 486805/ 549/ 88/ modules
106120/ 1306/ 225/ 338/ 453771/ 486870/ 55/ 89/ mounts@
106130/ 1310/ 226/ 34/ 46/ 486871/ 550/ 90/ mtrr
1068/ 1317/ 227/ 344/ 461173/ 486890/ 551/ 92/ net@
107/ 132/ 228/ 345/ 461205/ 486891/ 552/ 93/ pagetypeinfo
1070/ 1338/ 229/ 35/ 463094/ 487050/ 553/ 95/ partitions
1071/ 1349/ 23/ 36/ 463134/ 487079/ 554/ 96/ pressure/
1078/ 1369/ 230/ 37/ 463240/ 487200/ 555/ 97/ schedstat
1079/ 1382/ 231/ 374123/ 467135/ 487459/ 556/ 99/ scsi/
108/ 139657/ 232/ 374147/ 467314/ 487565/ 557/ acpi/ self@
1080/ 14/ 233/ 381/ 467349/ 487569/ 558/ asound/ slabinfo
1081/ 140/ 234/ 382/ 467951/ 487681/ 559/ bootconfig softirqs
1083/ 1406/ 235/ 4/ 467961/ 487859/ 58/ buddyinfo stat
1093/ 141/ 236/ 40/ 468046/ 487860/ 59/ bus/ swaps
11/ 1411/ 237/ 402072/ 468064/ 488038/ 6/ cgroups sys/
110/ 143/ 238/ 41/ 468105/ 488253/ 60/ cmdline sysrq-trigger
1100/ 145297/ 239/ 410348/ 468185/ 488255/ 61/ config.gz sysvipc/
111/ 149760/ 24/ 414236/ 468786/ 488322/ 64/ consoles thread-self@
112/ 15/ 240/ 414278/ 469102/ 488468/ 65/ cpuinfo timer_list
113/ 16/ 241/ 42/ 469240/ 488469/ 66/ crypto tty/
1172/ 17/ 242/ 425388/ 469307/ 488480/ 67/ devices uptime
1200/ 18/ 243/ 425390/ 47/ 488500/ 70/ diskstats version
122/ 19/ 244/ 425478/ 470449/ 488572/ 7032/ dma vmallocinfo
122638/ 2/ 245/ 425523/ 471494/ 488587/ 71/ driver/ vmstat
122640/ 20/ 246/ 43/ 472196/ 488588/ 72/ dynamic_debug/ zoneinfo
122651/ 2007/ 247/ 430896/ 479324/ 488610/ 73/ execdomains
122652/ 201/ 248/ 431463/ 48/ 488677/ 76/ fb
122654/ 202/ 248562/ 432470/ 481539/ 488751/ 77/ filesystems
122688/ 203/ 248583/ 432490/ 483654/ 488823/ 778/ fs/
Seems to have a lot of folders! But, what are these weird folders with numbers?
If that’s your first question, you are on the right track. All of the folders with numbers on them represents the process information related to that process id (PID) stored by the kernel.
There are few other interesting files such as cpuinfo, meminfo, modules, mounts, uptime, version, swaps. There are also few folders that I find interesting, such as self/, sys/. The above links are all the stuff that I’m going to cover in this article. Feel free to go to the References to learn more about procfs :)
Process info
Each and every folder with a number represents a process in your system. Specifically, this is the PID
(Process id) of the process. Every process has an ID associated with it, and all of the process state required by the kernel is stored in /proc/$PID/
folder.
If you check your system processes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
~ pstree -p
systemd(1)─┬─NetworkManager(803)─┬─{NetworkManager}(827)
│ ├─{NetworkManager}(829)
│ └─{NetworkManager}(830)
├─Xwayland(106086)─┬─{Xwayland}(106095)
│ ├─{Xwayland}(106096)
│ ├─{Xwayland}(106097)
│ ├─{Xwayland}(106098)
│ ├─{Xwayland}(106099)
│ ├─{Xwayland}(106100)
│ ├─{Xwayland}(106101)
│ ├─{Xwayland}(132948)
│ ├─{Xwayland}(132949)
│ └─{Xwayland}(132950)
├─alacritty(414236)─┬─zsh(414278)───nvim(425388)─┬─nvim(425390)─┬─zsh(425478)───bundle(425523)─┬─{bundle}(425559)
│ │ │ │ ├─{bundle}(425560)
│ │ │ │ ├─{bundle}(425561)
│ │ │ │ └─{bundle}(486973)
│ │ │ ├─{nvim}(425392)
│ │ │ ├─{nvim}(487774)
│ │ │ ├─{nvim}(487775)
│ │ │ ├─{nvim}(487776)
│ │ │ ├─{nvim}(487777)
│ │ │ ├─{nvim}(487784)
│ │ │ ├─{nvim}(488323)
│ │ │ └─{nvim}(492949)
│ │ ├─{nvim}(425389)
│ │ └─{nvim}(425391)
│ ├─{alacritty}(414238)
│ ├─{alacritty}(414239)
│ ├─{alacritty}(414240)
│ ├─{alacritty}(414241)
│ ├─{alacritty}(414265)
│ ├─{alacritty}(414266)
│ ├─{alacritty}(414267)
│ ├─{alacritty}(414268)
I’ve used pstree
for pretty presentation, you can use a normal ps
or top
. But the most important thing that you’ll have to notice here is the PID
of the process.
For example, if you take systemd(1)
, it’s PID
is 1. It is the init process which initializes other processes and daemons in the system. Let’s have a look at the process 1
/proc/1
Listing the files in /proc/1
gives us a output similar to this
1
2
3
4
5
6
7
/proc/1
❯ ls
arch_status clear_refs cpuset fdinfo latency mem ns pagemap schedstat stack task wchan
attr cmdline cwd gid_map limits mountinfo numa_maps personality sessionid stat timens_offsets
autogroup comm environ io loginuid mounts oom_adj projid_map setgroups statm timers
auxv coredump_filter exe ksm_merging_pages map_files mountstats oom_score root smaps status timerslack_ns
cgroup cpu_resctrl_groups fd ksm_stat maps net oom_score_adj sched smaps_rollup syscall uid_map
Here, we are looking at the process called systemd
whose PID is 1. The first thing I love look in this folder is the cmdline
file.
/proc/$PID/cmdline
1
2
3
/proc/1
❯ cat -A cmdline
/sbin/init^@splash^@%
The command line shown in this file is cmd that had been used to invoke the executable. It also provides the arguments that had been used for the command.
The
^@
character is aNUL
in caret notation. It’s shown in such a representation, because the C program invoking themain(int argc, char** argv)
function expects theargv
in NUL-terminated C-string array.
/proc/$PID/environ
The environ
file describes the shell environment of the process. Here you’ll find env variables that were set when the process was run. It could range from simple $TERM
to sophisticated variables like $PATH
etc.
1
2
3
/proc/1
❯ cat environ
TERM=linux%
Process memory
There are few other interesting files, but I’d like to focus more the memory of the process, as this is the crucial part that I’ve utilized extensively for building my project Kunai.
/proc/$PID/maps
This is the file that stores the memory mapping of the process. Let’s have a look at it first.
1
2
3
4
5
6
7
8
9
10
11
12
13
/proc/1
❯ sudo cat maps
55a4fd849000-55a4fd84f000 r--p 00000000 103:0a 6316636 /usr/lib/systemd/systemd
55a4fd860000-55a4fd861000 rw-p 00017000 103:0a 6316636 /usr/lib/systemd/systemd
55a4fdbba000-55a4fde74000 rw-p 00000000 00:00 0 [heap]
7f5369074000-7f5369078000 r--p 00000000 103:0a 6316188 /usr/lib/libelf-0.189.so
7f5369078000-7f536908a000 r-xp 00004000 103:0a 6316188 /usr/lib/libelf-0.189.so
7f536908a000-7f536908e000 r--p 00016000 103:0a 6316188 /usr/lib/libelf-0.189.so
...
7ffe66c98000-7ffe66d9a000 rw-p 00000000 00:00 0 [stack]
7ffe66dc9000-7ffe66dcd000 r--p 00000000 00:00 0 [vvar]
7ffe66dcd000-7ffe66dcf000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]
The two lines I want you to focus here are
1
55a4fdbba000-55a4fde74000 rw-p 00000000 00:00 0 [heap]
1
7ffe66c98000-7ffe66d9a000 rw-p 00000000 00:00 0 [stack]
Syntax:
<address start>-<address end> <mode> <offset> <major id:minor id> <inode id> <file path>
These two lines contain the curx required to scan, edit, manipulate the process of a memory. Inherently, a process definitely has two kinds of memory, one is the [stack] and the other is the [heap1]. Now these two memory ranges can be read and manipulated, given that the user/process accessing the memory region has enough privileges. Kunai, in fact, utilizes these permissions to edit a processes’ memory during it’s runtime. It’s like a CheatEngine for linux!
The other lines present in the file are libraries or external entities that are managed by the same process but has dynamically loaded them outside the application point of view.
After getting the processes memory region from the maps
file, it opens the mem
file to edit or read the memory of that process.
/proc/$PID/mem
This the magic file! It contains the memory held by the process. You can access the memory, by seeking it to the location specified in the maps
file.
For example, you can seek and edit memory as shown in the code below.
1
2
3
4
5
6
7
8
9
10
11
12
def edit_mem():
# ... A bit of preprocessing
idx = mem.find(bytes(string_to_find, "ASCII")) # Finding the location of the string
if idx == -1:
eprint("[!] String not found in", mem.name)
return
print("[*] String found at:", idx, "in", mem.name)
mem.seek(start + idx) # Seeking to the location of string
mem.write(bytes(changed_string, "ASCII")) # Editing the string
print(f"[*] Changed {original} to {changed} in {mem.name}")
You can refer to the file at github
Running a simple C program that allocates a string on the heap, some what similar to this code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main(void) {
// String on the stack
char s_stack[] = {'S', 'o', 'm', 'e', 'S', 't', 'r', 'i', 'n', 'g', '\0'};
// String on the heap
char *s_heap = strdup("HeapString");
printf("Heap : %p\n", s_heap); // Printing the location of string on stack
printf("Stack: %p\n", s_stack); // Printing the location of string on heap
while (1) {
sleep(5);
printf("Stack: %s\n", s_stack);
printf("Heap: %s\n", s_heap);
}
return 0;
}
When I run the C file and run the python file to edit the memory, you seen the end result as follows
1
2
3
4
5
6
7
8
9
10
11
12
13
❯ ./a.out
Heap : 0x55edc241d2a0
Stack: 0x7fffdc1daecd
Stack: SomeString
Heap: HeapString
Stack: SomeString
Heap: HeapString
Stack: SomeString
...
Heap: HeapString
Stack: SomeString
Heap: SheepString ===> The string changed!
...
1
2
3
4
❯ sudo python mem_editor.py 585933 HeapString
String found at: 672
Change to: SheepString
Changed HeapString to SheepString
The “HeapString” is changed to “SheepString”, as I’ve mentioned in the python script input. You can find the mem_editor.py in github
There are a few other files present in /proc/$PID
, but I’ll leave that to your homework. Moving forward, I’ll focus on more file inside the /proc
directory.
/proc/cpuinfo
This file contains information about the processor, such as its type, make, model, and performance.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/proc
❯ cat cpuinfo
processor : 0 => Processor ID
vendor_id : AuthenticAMD => Manufacturer
cpu family : 23
model : 96
model name : AMD Ryzen 5 4600H with Radeon Graphics
stepping : 1
microcode : 0x8600104
cpu MHz : 3955.064
cache size : 512 KB
physical id : 0
siblings : 12 => Total number of processors present along side this processor
core id : 0
cpu cores : 6
apicid : 0
initial apicid : 0
fpu : yes
fpu_exception : yes
cpuid level : 16
wp : yes
...
/proc/meminfo
This contains information about memory usage, both physical and swap. Concatenating this file produces similar results to using ‘free’ or the first few lines of ‘top’.
1
2
3
4
5
6
7
8
9
10
/proc
❯ cat meminfo
MemTotal: 15754988 kB
MemFree: 6072304 kB
MemAvailable: 6294144 kB
Buffers: 23472 kB
Cached: 505044 kB
SwapCached: 967288 kB
Active: 3207352 kB
...
/proc/modules
Kernel modules currently loaded. Typically its output is the same as that given by the lsmod
command.
1
2
3
4
5
6
7
8
9
10
11
❯ lsmod
Module Size Used by
uvcvideo 176128 0
videobuf2_vmalloc 20480 1 uvcvideo
uvc 12288 1 uvcvideo
videobuf2_memops 16384 1 videobuf2_vmalloc
videobuf2_v4l2 40960 1 uvcvideo
videodev 389120 2 videobuf2_v4l2,uvcvideo
videobuf2_common 94208 4 videobuf2_vmalloc,videobuf2_v4l2,uvcvideo,videobuf2_memops
mc 90112 4 videodev,videobuf2_v4l2,uvcvideo,videobuf2_common
...
1
2
3
4
5
6
7
8
9
❯ cat /proc/modules
uvcvideo 176128 0 - Live 0x0000000000000000
videobuf2_vmalloc 20480 1 uvcvideo, Live 0x0000000000000000
uvc 12288 1 uvcvideo, Live 0x0000000000000000
videobuf2_memops 16384 1 videobuf2_vmalloc, Live 0x0000000000000000
videobuf2_v4l2 40960 1 uvcvideo, Live 0x0000000000000000
videodev 389120 2 uvcvideo,videobuf2_v4l2, Live 0x0000000000000000
videobuf2_common 94208 4 uvcvideo,videobuf2_vmalloc,videobuf2_memops,videobuf2_v4l2, Live 0x0000000000000000
...
/proc/mounts
Lists all the mounted file systems and their parameters. This is similar to the file in /etc/mtab
1
2
3
4
5
6
7
❯ cat /etc/mtab
proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
sys /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0
dev /dev devtmpfs rw,nosuid,relatime,size=7856308k,nr_inodes=1964077,mode=755,inode64 0 0
run /run tmpfs rw,nosuid,nodev,relatime,mode=755,inode64 0 0
efivarfs /sys/firmware/efi/efivars efivarfs rw,nosuid,nodev,noexec,relatime 0 0
...
1
2
3
4
5
6
7
❯ cat /proc/mounts
proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0
sys /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0
dev /dev devtmpfs rw,nosuid,relatime,size=7856308k,nr_inodes=1964077,mode=755,inode64 0 0
run /run tmpfs rw,nosuid,nodev,relatime,mode=755,inode64 0 0
efivarfs /sys/firmware/efi/efivars efivarfs rw,nosuid,nodev,noexec,relatime 0 0
...
/proc/uptime
The output of the command uptime
is derived from this file. You can verify it from this line of coreutils library
1
2
3
4
5
6
7
/proc
❯ cat uptime
2711524.13 590084.11
/proc
❯ uptime
23:06:08 up 31 days, 9:12, 1 user, load average: 2.42, 2.61, 2.47
/proc/version
The contents of this file are similar to the output of the command uname
1
2
3
4
5
6
7
/proc
❯ cat version
Linux version 6.5.8-arch1-1 (linux@archlinux) (gcc (GCC) 13.2.1 20230801, GNU ld (GNU Binutils) 2.41.0) #1 SMP PREEMPT_DYNAMIC Thu, 19 Oct 2023 22:52:14 +0000
/proc
❯ uname -a
Linux yoru 6.5.8-arch1-1 #1 SMP PREEMPT_DYNAMIC Thu, 19 Oct 2023 22:52:14 +0000 x86_64 GNU/Linux
/proc/swaps
Similar to swapon -s
command
1
2
3
4
5
6
7
8
9
/proc
❯ cat swaps
Filename Type Size Used Priority
/dev/nvme0n1p3 partition 16777212 3821596 -2
/proc
❯ swapon -s
Filename Type Size Used Priority
/dev/nvme0n1p3 partition 16777212 3800604 -2
/proc/sys/
Taken from tldp
This is not only a source of information, it also allows you to change parameters within the kernel without the need for recompilation or even a system reboot. Take care when attempting this as it can both optimize your system and also crash it. To change a value, simply echo the new value into the file.
An example is given below in the section on the file system data. You can create your own boot script to perform this every time your system boots.
For example, to enable coredumps for processes in linux, you can do the following.
1
2
ulimit -c unlimited
echo 1 > /proc/sys/kernel/core_uses_pid
Refer this link for more information.
/proc/self/
This is one of the most interesting directory in procfs. Why? The answer is quite simple. It represents your current running process.
1
2
❯ ls -l /proc | grep 'self'
lrwxrwxrwx - root 5 Dec 2024 self -> 615669
As you can see, the self directory is mapped to another PID directory in the procfs. It’s a soft link that is different for every process and is assigned by the kernel.
References
Footnotes
There are few processes that don’t allocate heap space. But for this example, let’s just consider they do exist. ↩︎