Restore accidentally deleted/replaced binary of a running process


I compiled Turbo Bob (a build system) with a bug that prevented entering a build container, and since Turbo Bob is built with itself, I could not build a working binary again - chicken-egg problem.

(Of course I could download an old release, but I had some unreleased work I was benefiting from that the old binary included, and while Bob builds fine with just Go compiler, I didn’t want to install Go tools on my host system only to fix this minor issue. And besides, this was a fun challenge.)

My problem

I wanted to get the old and working binary back again.

Solution

Luckily I had the old working version of Turbo Bob’s binary processes still running for other projects, and I knew I can dump running process’ binary from /proc filesystem.

First, identify PIDs for processes we’re interested in:

$ ps aux | grep bob
vagrant   8141  0.0  0.0 711956  7708 pts/2    Sl+  Jul30   0:00 bob dev
vagrant  14780  0.0  0.0 711956  7056 pts/3    Sl+  Jul31   0:00 bob dev

I chose 14780. The Linux proc filesystem contains process’s binary in /proc/14780/exe. I thought I could just $ cp /proc/14780/exe /usr/local/bin/bob and call it a day, but apparently the exe entry is a symlink:

$ file /proc/14780/exe
/proc/14780/exe: broken symbolic link to /usr/local/bin/bob (deleted)

I knew that since the binary is running, it has to keep handles somewhere for the old binary, because the process’ binary file is not physically removed from the filesystem until all references to it are closed.

Next I inspected the proc filesystem’s maps (“A file containing the currently mapped memory regions and their access permissions.”):

$ cat /proc/14780/maps
00400000-00894000 r-xp 00000000 fd:00 657514                             /usr/local/bin/bob (deleted)
00894000-00d95000 r--p 00494000 fd:00 657514                             /usr/local/bin/bob (deleted)
00d95000-00ddd000 rw-p 00995000 fd:00 657514                             /usr/local/bin/bob (deleted)
00ddd000-00e0e000 rw-p 00000000 00:00 0
c000000000-c004000000 rw-p 00000000 00:00 0
7fe96e6a8000-7fe970a59000 rw-p 00000000 00:00 0

The 00400000-00894000 looks promising (notice it has an executable bit).

I could dump it out with sudo:

$ sudo cat /proc/14780/map_files/400000-894000 > maybe_bob
$ file maybe_bob
maybe_bob: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

Success!

Additional thoughts

Why did I use maps instead of fd (file descriptors)? While any open files by the process should be found from /proc/<id>/fd/..., I suspect the process’ binary is special because technically it’s not a file opened by the process, since the binary must exist before there even is a process that could open any files.

Related Posts

Quantified self: overview & quick links

Displaying RSS feed with Home Assistant

Tracking down inefficiencies in parallelization

Preventing outdated docs links in software projects

Quick primer on SVG paths

Linux kernel probing

COVID-19 and the world´s refusal to blame China

Go is not a simple language

Modem drops internet every now and then? I made a modem rebooter util

Serious security vulnerability in GunDB (and new ones)