LINUX · LESSON 02

Processes & Syscalls

20 min

Learning Objectives

  • Explain the fork/exec model for creating Linux processes
  • Explain how a syscall transitions from userspace to the kernel (the syscall instruction)
  • Read /proc/<pid>/maps to understand a process's memory layout
  • Identify common syscalls (execve, ptrace, mmap) relevant to reverse engineering

Every Linux Process Is Born Twice

Windows has CreateProcess, one call that does everything. Linux splits process creation into two distinct steps, and that split shapes how you reason about every process tree you’ll ever inspect.

fork() Then exec()

fork()   duplicate the calling process — new PID, copy-on-write memory,
         same code, same open file descriptors, execution resumes
         in BOTH parent and child right after the call

exec()   replace the CURRENT process image with a new program —
         same PID, but new code/data/stack; nothing survives except
         open file descriptors (unless FD_CLOEXEC is set)

A shell running ls does fork() to get a child process, then that child calls execve("/bin/ls", ...) to become ls without spawning a third process. This is why ps shows parent/child relationships that make sense: every process except PID 1 was forked from something, and most immediately exec’d into something else. vfork and clone are optimized/parameterized variants of the same idea — clone in particular is what Linux threads are built from, sharing the address space instead of copying it.

The syscall Instruction

A syscall is how userspace asks the kernel to do something it isn’t allowed to do directly: open a file, allocate memory, send a signal. On x86-64 Linux, the mechanism is the syscall instruction:

mov rax, 59       ; syscall number for execve
mov rdi, path     ; arg1
mov rsi, argv     ; arg2
mov rdx, envp     ; arg3
syscall           ; trap into kernel mode

rax holds the syscall number, arguments go in rdi, rsi, rdx, r10, r8, r9 (note: r10 instead of rcx — the C calling convention’s rcx slot is clobbered by the syscall instruction itself), and the return value comes back in rax. The CPU switches privilege rings, the kernel’s syscall handler dispatches on the number in rax, does the work, and sysret hands control back. This is the one, narrow gate between userspace and kernel — every anti-debug check, every file read, every network call eventually funnels through it, which is exactly why tracing syscalls is such a powerful analysis technique (more in the Anti-Debugging and tooling lessons).

Reading /proc//maps

Every running process exposes its own memory layout as a pseudo-file:

555555554000-555555555000 r-xp 00000000 08:01 1234  /usr/bin/sample
555555754000-555555755000 rw-p 00000000 08:01 1234  /usr/bin/sample
7ffff7dc0000-7ffff7de5000 r-xp 00000000 08:01 5678  /lib/x86_64-linux-gnu/libc.so.6
7ffff7fc0000-7ffff7fc4000 rw-p 00000000 00:00 0                        [heap]
7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0                        [stack]

Each line is a mapped region: address range, permissions (rwxp/rwxs), file offset, device, inode, and backing file (or [heap], [stack], or blank for anonymous memory). This is the direct, ground-truth answer to “where does each PT_LOAD segment from the ELF header end up, and where did the dynamic linker put libc?” It’s also where you look for a suspiciously rwx region, a library mapped without a backing file, or ASLR-shifted base addresses in a live process.

Syscalls Worth Knowing for RE

SyscallPurposeWhy it matters
execvereplace process imagehow every program actually starts running
fork / clonecreate process/threadprocess tree reconstruction
ptracedebug/trace another processboth how debuggers work and a self-debugging anti-debug trick
mmapmap memory (files or anonymous)how segments, shared libraries, and packers’ unpacked payloads get memory
mprotectchange page permissionsa RW → RX transition is a strong unpacking/self-modification signal
ptrace(PTRACE_TRACEME)mark self as tracedclassic Linux anti-debug — see the next lesson

Takeaway

Every Linux process exists because something called fork, then exec; every privileged action it takes funnels through one instruction, syscall — trace that one gate and you see everything the process does.