A script that can unpack initramfs from a citadel bzImage
This commit is contained in:
parent
553ca22f41
commit
aed005c945
190
scripts/extract-initramfs.sh
Executable file
190
scripts/extract-initramfs.sh
Executable file
@ -0,0 +1,190 @@
|
||||
#!/bin/bash
|
||||
|
||||
#
|
||||
# extract-initramfs:
|
||||
#
|
||||
# Extracts and decompresses kernel binary (vmlinux) from a bzImage then searches
|
||||
# the extracted kernel for an initramfs cpio archive.
|
||||
#
|
||||
# Assumes kernel is compressed with LZ4 and that embedded cpio archive is not compressed.
|
||||
#
|
||||
|
||||
usage() {
|
||||
cat <<-EOF
|
||||
USAGE: extract-initramfs [options] [kernel bzImage]
|
||||
|
||||
OPTIONS
|
||||
|
||||
-d <directory-name> Choose a non-default directory for extracted initramfs (default ${PWD}/initramfs)
|
||||
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
info() {
|
||||
printf "[+] ${1}\n"
|
||||
}
|
||||
|
||||
error() {
|
||||
>&2 echo "[!] ${1}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
REQUIRED_UTILS="cpio lz4 readelf"
|
||||
|
||||
confirm_required_utils() {
|
||||
local required=${1}
|
||||
local missing=""
|
||||
|
||||
for util in ${required}; do
|
||||
which ${util} > /dev/null || missing="${missing}${util} "
|
||||
done
|
||||
|
||||
if [[ -n ${missing} ]]; then
|
||||
error "Required utilities are not installed: ${missing}"
|
||||
fi
|
||||
}
|
||||
|
||||
confirm_required_utils "${REQUIRED_UTILS}"
|
||||
|
||||
CPIO_HEADER='070701'
|
||||
LZ4_HEADER='\x02!L\x18'
|
||||
|
||||
img=""
|
||||
workdir=$(mktemp -d /tmp/initramfs-extract.XXXX)
|
||||
vmlinux=${workdir}/vmlinux
|
||||
outdir=${PWD}/initramfs
|
||||
trap "rm -rf $workdir" 0
|
||||
|
||||
#
|
||||
# Search for $pattern in $infile and output a list of offsets where the pattern is found
|
||||
#
|
||||
|
||||
binary_grep() {
|
||||
local pattern=$1 infile=$2
|
||||
grep --only-matching --byte-offset --text --perl-regexp ${pattern} ${infile} | cut -d: -f1
|
||||
}
|
||||
|
||||
#
|
||||
# Output $infile starting at byte position $offset
|
||||
#
|
||||
|
||||
binary_tail() {
|
||||
local offset=$1 infile=$2
|
||||
tail -c +$(($offset + 1)) ${infile}
|
||||
}
|
||||
|
||||
check_elf() {
|
||||
readelf -h ${1} > /dev/null 2>&1
|
||||
}
|
||||
|
||||
#
|
||||
# Search for $pattern in $img file and for each offset found, attempt to decompress
|
||||
# the file starting at that position by piping through $cmdline
|
||||
#
|
||||
|
||||
try_decompress() {
|
||||
local pattern=$1 cmdline=$2
|
||||
for pos in $(binary_grep ${pattern} ${img}); do
|
||||
printf "Testing offset %08x\n" $pos
|
||||
binary_tail $pos $img | ${cmdline} > ${vmlinux} 2> /dev/null
|
||||
check_elf ${vmlinux} && return 0
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
#
|
||||
# Add more patterns and command lines from extract-vmlinux script in linux kernel source tree
|
||||
# to support more compression formats
|
||||
#
|
||||
|
||||
decompress_kernel() {
|
||||
try_decompress $LZ4_HEADER 'lz4 -d' && return 0
|
||||
error "Failed to decompress kernel"
|
||||
}
|
||||
|
||||
#
|
||||
# CPIO format is a list of file entries where each entry begins with a header that contains only
|
||||
# ASCII hexadecimal digits. To avoid false positives matching only the magic value, check to make
|
||||
# sure that some of the the bytes following the magic field are ASCII hex digits.
|
||||
#
|
||||
# From https://www.systutorials.com/docs/linux/man/5-cpio/
|
||||
#
|
||||
# New ASCII Format
|
||||
#
|
||||
# The "new" ASCII format uses 8-byte hexadecimal fields for all numbers and separates device numbers
|
||||
# into separate fields for major and minor numbers.
|
||||
#
|
||||
# struct cpio_newc_header {
|
||||
# char c_magic[6];
|
||||
# char c_ino[8];
|
||||
# char c_mode[8];
|
||||
# char c_uid[8];
|
||||
# char c_gid[8];
|
||||
# char c_nlink[8];
|
||||
# char c_mtime[8];
|
||||
# char c_filesize[8];
|
||||
# char c_devmajor[8];
|
||||
# char c_devminor[8];
|
||||
# char c_rdevmajor[8];
|
||||
# char c_rdevminor[8];
|
||||
# char c_namesize[8];
|
||||
# char c_check[8];
|
||||
# };
|
||||
#
|
||||
|
||||
confirm_cpio() {
|
||||
# Check that there are 100 ascii hex digit characters at offset
|
||||
local offset=${1} checklen=100
|
||||
dd if=${vmlinux} bs=1 count=${checklen} skip=${offset} 2> /dev/null | grep -q "[[:xdigit:]]\{${checklen}\}"
|
||||
}
|
||||
|
||||
extract_cpio() {
|
||||
cpio --make-directories --extract --no-absolute-filenames -D ${outdir} 2> /dev/null
|
||||
}
|
||||
|
||||
extract_initramfs() {
|
||||
for pos in $(binary_grep ${CPIO_HEADER} ${vmlinux}); do
|
||||
printf "Found CPIO: %08x\n" $pos
|
||||
if confirm_cpio ${pos}; then
|
||||
mkdir ${outdir}
|
||||
binary_tail $pos ${vmlinux} | extract_cpio
|
||||
info "Initramfs files extracted to ${outdir}"
|
||||
exit 0
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
key=${1}
|
||||
case $key in
|
||||
-d)
|
||||
outdir="$(realpath ${2})"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
;;
|
||||
-*)
|
||||
printf "Unknown option ${key}\n"
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
img=${1}
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -z ${img} ]] && usage
|
||||
[[ -f ${img} ]] || error "kernel image file ${img} does not exist"
|
||||
|
||||
if [[ -d ${outdir} ]]; then
|
||||
error "Output directory ${outdir} already exists. Remove it or choose a different directory with -d option."
|
||||
fi
|
||||
|
||||
decompress_kernel
|
||||
extract_initramfs
|
||||
|
Loading…
Reference in New Issue
Block a user