4

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 dding 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:

  1. 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).

  2. I extract vmlinux from the middle part using gunzip.

  3. 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).

  4. 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.

  5. I embed modified initramfs into a new cpio archive and gzip it.

  6. I concatenate VMLINUX_HEADER, new gzipped cpio archive and VMLINUX_TRAILER to recreate vmlinux.

  7. I gzip new vmlinux.

  8. I concatenate KERNEL_HEADER, new gzipped vmlinux and KERNEL_TRAILER into a new kernel image which, unfortunately, fails to boot.

Side notes:

  1. 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.

  2. 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.

11
  • At which stage does the original working kernel show output? During kernel boot, initramfs stage or later? Does it work with qemu? For which system is this kernel normally used? The output of binwalk shows a certificate section. Binwalk does not always get things 100% right but maybe it is a signed kernel image (UKI). So there might be some verification involved and changing contents will render the kernel unbootable. In general I would recommend the kernel-newbies irc. Maybe there is already a tool/script in the kernel src for extracting/reassembling such images.
    – secfren
    Mar 8, 2023 at 12:25
  • Slightly older related thread. Not sure if helpful. reverseengineering.stackexchange.com/questions/23547/…
    – secfren
    Mar 8, 2023 at 12:27
  • @secfren With the original working kernel the startup sequence definitely reaches initramfs, I've verified that both with qemu and a real machine. The kernel is part of a full bootable image consisting of several partitions, some of which are encrypted, but that's irrelevant at the moment. Currently the boot process does not go beyond initramfs, but that's not a problem, either - I'm able to access init script in initramfs and have an idea how to modify it in order to proceed. The whole point is that I can't assemble the fragmented kernel back to a state where it's bootable.
    – Peter
    Mar 8, 2023 at 18:43
  • @secfren As for the kernel itself: fragments of text data found in it suggest it might be based on 3.13 x86 64-bit Linux kernel. I don't have detailed info about the system as a whole, but it could be some custom version of Ubuntu (definitely not a regular one, as standard ones come with separate kernel and initramfs).
    – Peter
    Mar 8, 2023 at 19:07
  • When does the original kernel show output during boot? Already at kernel boot? Depending on the kernel there is not output until initramfs stage. So you might not know where your new kernel stops. If it is quiet by default it could just be due to the initramfs failing. Under which circumstances does boot freeze and when does it reboot? If the original kernel is working shouldn't it be possible to get more info on the kernel version from within initramfs?
    – secfren
    Mar 8, 2023 at 21:01

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.