Skip to content

Instantly share code, notes, and snippets.

@CMCDragonkai
Last active May 30, 2024 17:15
Show Gist options
  • Save CMCDragonkai/3f4032ace09772dc46f8 to your computer and use it in GitHub Desktop.
Save CMCDragonkai/3f4032ace09772dc46f8 to your computer and use it in GitHub Desktop.
Inspecting the initrd of NixOS #nixos #cli
#!/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
@CMCDragonkai
Copy link
Author

This has a bug at line 66/67. The directories aren't being properly separated. Pending bugfix.

@hhomar
Copy link

hhomar commented Jan 10, 2018

The fix is at line 61:

extracted+=( "_$f.extracted" )

It was a very useful script. Thanks.

@CMCDragonkai
Copy link
Author

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.

@CMCDragonkai
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment