I have a Linux kernel file and I need to tweak the contents of its corresponding initramfs. I did it in the past for standard distros where kernel and initramfs were separate. However, this specific kernel comes with an embedded initramfs which makes it all a lot more complicated. Worse still, all I have is a binary image, so recompiling is not possible. Binwalk describes the kernel structure as follows:
Microsoft executable, portable (PE)
gzip compressed data, maximum compression, from Unix, last modified: 1970-01-01 00:00:00 (null date)
Object signature in DER format (PKCS header length: 4, sequence length: 1509
Certificate in DER format (x509 v3), header length: 4, sequence length: 769
Extracting and modifying the embedded initramfs is pretty straightforward - the second section of the kernel (as per binwalk output above) is a gzipped vmlinux which I can extract either using extract-vmlinux
or by manually dd
ing the .gz archive out of the kernel image and then extracting it with gunzip. The last section of resulting vmlinux is a gzipped cpio archive with initramfs I intend to modify. Unfortunately, sticking it all back together (with a great care taken to keep the unmodified fragments of the original kernel image intact) renders the kernel unbootable. Here's a more detailed description of my approach:
with dd, I separate the original kernel image into 3 parts: stuff before the gzipped vmlinux (call it a KERNEL_HEADER), the gzipped vmlinux, and stuff after it (call it a KERNEL_TRAILER).
I extract vmlinux from the middle part using gunzip.
like in step 1 above, I use dd to separate vmlinux into 3 parts: stuff before the gzipped cpio archive (call it a VMLINUX_HEADER), the gzipped cpio archive with initramfs, and stuff after it (call it a VMLINUX_TRAILER).
I extract the cpio archive from the middle part using gunzip, then extract initramfs from it using cpio and modify it according to my needs.
I embed modified initramfs into a new cpio archive and gzip it.
I concatenate VMLINUX_HEADER, new gzipped cpio archive and VMLINUX_TRAILER to recreate vmlinux.
I gzip new vmlinux.
I concatenate KERNEL_HEADER, new gzipped vmlinux and KERNEL_TRAILER into a new kernel image which, unfortunately, fails to boot.
Side notes:
I'm aware of the fact that .gz archive can have trailing bytes appended to it and still get extracted correctly to produce the desired result. Since such bytes would be discarded by gunzip as "trailing garbage", I took extra care to separate both .gz archives from surrounding bytes in such a way as to avoid any redundancy - after all, those "trailing garbage" bytes are part of the kernel and can't simply be discarded. I did that by searching for ISIZE members at the end of each archive. I naively assumed that if I'd modify only the .gz archives and then prepend and append exactly the same data blocks they were originally attached to to them, I'd end up with a new working kernel. Unfortunately, it doesn't seem that simple.
while experimenting I noticed that mere gunzipping a gzipped vmlinux and gzipping it back again is enough to destroy the kernel image - unless the new .gz archive has exactly the same contents (or at least the same length) as the original one, prepending and appending the aforementioned KERNEL_HEADER and KERNEL_TRAILER to it, respectively, results in a new, unbootable kernel. This holds true even if old and new .gz archives have identical payload bytes and differ only by metadata. Depending on exact difference between old and new .gz archives, the following invocation:
qemu-system-x86_64 -kernel <NEW KERNEL IMAGE FILE>
results either in a freeze or an infinite boot loop with a "Booting from ROM..." message displayed in both cases.
My wild guess is that even a slight change in gzipped vmlinux data layout confuses the kernel decompression routine located earlier in the image. Is there a simple way to fix it by means of a raw binary edit? I don't have a detailed knowledge of Linux kernel internal structure (I do know about piggy.o and other object files a kernel image consists of, but a serious research is still ahead of me). I'm ready to learn, but I hope someone more experienced will at least point me in the right direction. My question boils down to: how to safely embed a (possibly modified) vmlinux back into its corresponding kernel image? Any help will be appreciated.