1

I'd like to modify some of the init scripts in an initramfs that is embedded in a kernel bzImage. I know I can modify them by not changing the size of any files (e.g. by commenting out lines, and deleting letters or adding comments if necessary to even things out.)

Ideally, I'd like to DD out the xz file from the zImage. Decompress it. Modify the cpio, then recompress it, and DD back to the zImage. My hope is that by not changing the size of any files, this is going to work. Although I appreciate it may not be that easy. So any help with that would be much appreciated.

In addition, I've run binwalk on the kernel image, and it looks like this:


DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             Microsoft executable, portable (PE)
16817         0x41B1          xz compressed data
7297600       0x6F5A40        xz compressed data
7299230       0x6F609E        Unix path: /x86/boot/compressed/kaslr_64.c!?
7483729       0x723151        Certificate in DER format (x509 v3), header length: 4, sequence length: 762

Now. I've also run Binwalk -e on the file, and it is able to successfully extract the xz archives, and decompress at least one of them (0x41B1) into a .cpio file, which I've verified contains the initramfs. But a couple issues I've run into don't make sense to me:

  1. The "xz" file extracted by binwalk is 7468247 in size. Yet, if you subtract the offsets above between the two xz files you get 7280783. Why is there a difference?

  2. When I try to run xz -d on the .xz file extracted by binwalk I get a message "Compressed data is corrupt." And if I do a hexdump of the XZ file extracted by binwalk it doesn't appear to have the magic bytes associated with an XZ file up top. So what is binwalk doing differently to properly extract the XZ file? And what exactly is binwalk extracting since it doesn't appear to be a valid XZ file (at least according to the XZ utility)?

  3. When I run the xz_wrap script (included in the linux kernel) (or any other options of XZ that I have tried) on the extracted CPIO file by Binwalk--no matter what I do--I can't get an xz file to generate that is exactly the same as the original .xz file that binwalk extracted from the zImage. Can anyone explain this or what I can do to generate the exact same XZ archive?

I know with certainty the Kernel is using "CONFIG_HAVE_KERNEL_XZ=y" since I've been able to extract that much. Although I'll note that after I enabled some debugging in Binwalk, I can see it is using its lzma decompressor to decompress the XZ file extracted from the kernel image.

Any suggestions here? I'm certainly not trying to reinvent the wheel. So if someone else has come up with a way to repack an initramfs into a x86 Kernel, I'd be interested in looking at that. I'm also open to any other suggestions here.

  • Would you mind sharing that kernel image? – malikcjm Mar 28 '20 at 11:57
  • Hi and welcome to RE.SE! Have you looked at how the Linux kernel source tree handles creation of the embedded initrd? In any case I'd expect some sort of integrity checking to be present, be it a CRC, Adler or even a proper hash. Perhaps these integrity checks are even interspersed with the data, so that the checking can be done without having to read the initrd completely first?! – 0xC0000022L Mar 28 '20 at 12:55
0

A cursory check of the matter based on the 5.5.13 stable kernel sources revealed within a matter of minutes that a special program called xzkern is used for the purpose. Excerpt from scripts/Makefile.lib:

# XZ
# ---------------------------------------------------------------------------
# Use xzkern to compress the kernel image and xzmisc to compress other things.
#
# xzkern uses a big LZMA2 dictionary since it doesn't increase memory usage
# of the kernel decompressor. A BCJ filter is used if it is available for
# the target architecture. xzkern also appends uncompressed size of the data
# using size_append. The .xz format has the size information available at
# the end of the file too, but it's in more complex format and it's good to
# avoid changing the part of the boot code that reads the uncompressed size.
# Note that the bytes added by size_append will make the xz tool think that
# the file is corrupt. This is expected.
#
# xzmisc doesn't use size_append, so it can be used to create normal .xz
# files. xzmisc uses smaller LZMA2 dictionary than xzkern, because a very
# big dictionary would increase the memory usage too much in the multi-call
# decompression mode. A BCJ filter isn't used either.

I am posting this as community wiki as it's not a proper answer, but too big for a mere comment.

Additionally you can find details in Documentation/xz.txt within the kernel source tree.

Either way it looks as if you're looking for xzmisc or perhaps (but less likely) xzkern.

Also, given that binwalk reports the PE file format I am wondering if this is some UEFI-related image. More details would help to give a definitive answer.

But this should help with some of the known puzzle pieces.

0

Thanks for the pointers. At least the xz.txt answers one of my questions, which is why the XZ util won't work to decompress. Although I still don't think what binwalk extracts is the exact XZ file generated by kernel make process. More on that below.

The kernel is a UEFI enabled kernel, so I'm guessing the first PE image is the EFI stub.

The specific version I'm dealing with is 4.19.36.

I looked at the Makefile.lib. And if I understand it correctly, this is the command that is run for XZKERN:


cmd_xzkern = (cat $(filter-out FORCE,$^) | \
    sh $(srctree)/scripts/xz_wrap.sh && \
    $(call size_append, $(filter-out FORCE,$^))) > $@ || \
    (rm -f $@ ; false)

In other words:

 cat CPIOFile | xz_wrap.sh && size_append(CPIOFile) > output.xz

If we look at xz_wrap.sh, it appears to be doing this for X86 platforms


xz --check=crc32 --x86 --lzma2=,dict=32MiB

So it would strike me that in my scenario, if I ran the following command I should be able to generate an XZ file very close (except for the size length appended) to that extracted via Binwalk:

cat 41B1 | xz --check=crc32 --x86 --lzma2=,dict=32MiB > 41B1.xz

But the file generated in this scenario is 20KB smaller than the original file.

Now what's interesting is that when I do a hexdump on the original file, I can see there is a ton of 0x00 padding and even some strings embedded in there (which of course is weird since its an XZ file). This makes me think that what binwalk extracts isn't exactly the XZ file generated in the make process.

Also, here is a link to the Kernel image, if anyone else wants to take a crack:

https://ufile.io/pjkx79e4

Edit: I also add that I'm not sure xzmisc is the right command to run. But in any event, it generates an even larger XZ file than the original.

  • 1
    This one describes process of starting up and decompressing linux kernel. 0xax.gitbooks.io/linux-insides/content/Booting/… Maybe it will help. – malikcjm Mar 28 '20 at 18:08
  • I started that attached file in qemu-system-x86_64. During early startup it shows size of compress kernel image: 0x6e8bd4, and original size: 0x1f86e6c – malikcjm Mar 28 '20 at 19:51
  • In the linux kernel source code I found something that might be describing internal structure of attached file: github.com/torvalds/linux/blob/v4.19/arch/x86/boot/compressed/… – malikcjm Mar 28 '20 at 20:05
  • Apparently that 7244756 (0x6e8bd4) is size of the xz archive + 4 additional byte appended. Those 4 bytes it the actual size of the xz archive. – malikcjm Mar 28 '20 at 20:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.