Last active
May 30, 2024 17:15
-
-
Save CMCDragonkai/3f4032ace09772dc46f8 to your computer and use it in GitHub Desktop.
Inspecting the initrd of NixOS #nixos #cli
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
# The initrd is a binary concatenated file, it contains cpio archives, but also | |
# gzipped cpio archives and potentially ZIP files too. Basically by itself it's | |
# not just the initramfs directory, it could contain other things. In order to | |
# actually extract what we need, we need to use binwalk to find out what's inside | |
# the initrd file. | |
# In this particular situation, NixOS uses initramfs format | |
# The default initrd locations are based on gummiboot locations | |
# The locations may be different and even the formats may be different if using | |
# grub | |
set -o errexit | |
set -o pipefail | |
import_exec () { | |
for e in $@; do | |
type -P "$e" >/dev/null || { echo >&2 "Error: The $e executable could not be found on the PATH"; exit 1; } | |
done | |
} | |
import_exec binwalk gunzip cpio | |
while [[ $# > 0 ]]; do | |
key="$1" | |
case $key in | |
-d|--initrd-location) | |
initrd_location="$2" | |
shift | |
;; | |
esac | |
shift | |
done | |
initrd_location="${initrd_location:-/boot/EFI/nixos}" | |
echo "Output will be saved to /tmp/initrd-extraction!" | |
# allow this to be user configurable in the future, however | |
# this can only be done once binwalk has an output directory option | |
# on binwalk 2.1.1 | |
rm -rf /tmp/initrd-extraction | |
mkdir --parents /tmp/initrd-extraction | |
pushd /tmp/initrd-extraction | |
cp --force "$initrd_location"/*-initrd.efi . | |
extracted=() | |
for f in *; do | |
binwalk \ | |
--verbose \ | |
--term \ | |
--include 'gzip compressed data' \ | |
--dd 'gzip compressed data:gz' \ | |
--rm \ | |
"$f" | |
extracted+="_$f.extracted" | |
done | |
echo "Extected all gzipped archives from binwalk" | |
for d in "${extracted[@]}"; do | |
pushd "$d" | |
mkdir --parents ./initrd | |
for gz in *.gz; do | |
gunzip --keep --force "$gz" | |
( | |
cd ./initrd &&\ | |
cpio \ | |
--make-directories \ | |
--no-absolute-filenames \ | |
--unconditional \ | |
--extract \ | |
< ../${gz%.*} | |
) | |
done | |
popd | |
done | |
popd |
The fix is at line 61:
extracted+=( "_$f.extracted" )
It was a very useful script. Thanks.
It appears that the initrd can also just be a stream of gzipped CPIO archives. Therefore you don't need to use binwalk at all. You can just straightforward gunzip the initrd, and then straightforward use cpio to inspect them. The fact that concatenation works and appending works is because both gzip and cpio formats are streams.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This has a bug at line 66/67. The directories aren't being properly separated. Pending bugfix.