After my previous question from nearly a year ago directing me to the YAA format to make sense of the chunks that could be decompressed, this time we're dealing with the chunks that made pbzx tell us that we're dealing with non-xz chunks.

I've tried to slowly rewrite Jonathan Levin's first pbzx extractor in Rust, the code I've written so far just reads out the flag of the file and each chunk's flag, length and signature (which for the most part should be the XZ header magic).

use std::io::{self, BufReader, Read, Seek, SeekFrom};
use std::fs::File;
use std::path::Path;
use binread::{BinRead, BinReaderExt};

#[derive(Debug, BinRead)]
pub struct block {
    flg: u64,
    len: i64,
    sig: u64

#[derive(Debug, BinRead)]
#[br(magic = b"pbzx")]
pub struct pbzx  {
    flag0: u64,
    chunk: block

fn main() -> io::Result<()>  {
    // Read file
    let file = File::open("payload.008")?;
    let mut reader = BufReader::new(file);
    // Apply it
    let header: pbzx = reader.read_be().unwrap();
    println!(" {:#04x}", header.flag0);
    // Zeroth block
    println!(" {:#04x}, {}, {:#04x}", header.chunk.flg, header.chunk.len, header.chunk.sig);
    reader.seek(SeekFrom::Current(header.chunk.len-8)).expect("Could not get current position!");
    // All the other blocks
    while (true) {  //
                    // I should actually be using "chunk.flg & ((1 << 24) | (1 << 23)) != 0" but I don't know
                    // where that's come from
                    // How does someone exactly calculate these bitshifts?
        let chunk: block = reader.read_be().unwrap();
        println!(" {:#04x}, {}, {:#04x}", chunk.flg, chunk.len, chunk.sig);
        reader.seek(SeekFrom::Current(chunk.len-8)).expect("Could not get current position!");


Running it makes it behave as expected, showing us a majority of chunks being XZ but some others, especially at the beginning, show non-repeating values that don't seem to make much sense.

kitty@kitty ~/Projects/pbzx                      
> $ ./target/debug/pbzx             
 0x800000, 8386152, 0xfd377a585a000000
 0x800000, 8388608, 0xf5278dda2c95573c
 0x800000, 8387764, 0xfd377a585a000000
 0x800000, 8388608, 0x7ec3ca75b21ecca8
 0x800000, 8377552, 0xfd377a585a000000
 0x800000, 8387576, 0xfd377a585a000000
 0x800000, 8388608, 0x2cb02061c42ec23b
 0x800000, 8388608, 0x94f8a22d86b5b145
 0x800000, 8388608, 0x7cf626aff7e56be6
 0x800000, 8388608, 0xd053c8e78ee64674
 0x800000, 8388608, 0xafac0d53e933e247
 0x800000, 8388608, 0xcc6c78f45ab958a5
 0x800000, 5815664, 0xfd377a585a000000
 0x800000, 3505580, 0xfd377a585a000000 
 0x800000, 4876720, 0xfd377a585a000000
 0x800000, 4761144, 0xfd377a585a000000
 0x800000, 4905372, 0xfd377a585a000000
 0x51e0b5, 4568784, 0xfd377a585a000000
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Io(Custom { kind: UnexpectedEof, error: "Out of bytes in reader" })', src/main.rs:35:45
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Searching for them in Hex Fiend, they are confirmed to be non-repeating, adjacent to a valid chunk (see YZ end signature) and don't match any known signatures. I tried converting between endians and got nothing, just in case.



I can go out on a limb and say they're encrypted but that begs the question but then that begs the question, how do I confirm that it is encrypted before exploring that path?

Pudquick's version, modified with the new flag, goes ahead and treats it like a decompressed cpio chunk but gives me some valid chunks and some chunks containing unknown or no data, Levin's updated version, which I used about a year ago skipped those chunks altogether

-rw-r--r--  1 kitty  staff  388745580 May 12 19:10 payload.008
-rw-r--r--  1 kitty  staff    8386152 Jun 21 02:30 payload.008.part00.cpio.xz
-rw-r--r--  1 kitty  staff    8388608 Jun 21 02:30 payload.008.part01.cpio
-rw-r--r--  1 kitty  staff    8387764 Jun 21 02:30 payload.008.part02.cpio.xz
-rw-r--r--  1 kitty  staff    8388608 Jun 21 02:30 payload.008.part03.cpio
-rw-r--r--  1 kitty  staff   16765128 Jun 21 02:30 payload.008.part04.cpio.xz
-rw-r--r--  1 kitty  staff    8388608 Jun 21 02:30 payload.008.part05.cpio
-rw-r--r--  1 kitty  staff          0 Jun 21 02:30 payload.008.part06.cpio.xz
-rw-r--r--  1 kitty  staff    8388608 Jun 21 02:30 payload.008.part07.cpio
-rw-r--r--  1 kitty  staff          0 Jun 21 02:30 payload.008.part08.cpio.xz
-rw-r--r--  1 kitty  staff    8388608 Jun 21 02:30 payload.008.part09.cpio
-rw-r--r--  1 kitty  staff          0 Jun 21 02:30 payload.008.part10.cpio.xz
-rw-r--r--  1 kitty  staff    8388608 Jun 21 02:30 payload.008.part11.cpio
-rw-r--r--  1 kitty  staff          0 Jun 21 02:30 payload.008.part12.cpio.xz
-rw-r--r--  1 kitty  staff    8388608 Jun 21 02:30 payload.008.part13.cpio
-rw-r--r--  1 kitty  staff          0 Jun 21 02:30 payload.008.part14.cpio.xz
-rw-r--r--  1 kitty  staff    8388608 Jun 21 02:30 payload.008.part15.cpio
-rw-r--r--  1 kitty  staff  288096620 Jun 21 02:30 payload.008.part16.cpio.xz
  • How do you tell that a chunk of data is encrypted?

  • How did the original PBZX decompression tools figure out the binary operations to be performed on a flag to signal the archive's end? I couldn't figure out the relationship between 0x51e0b5 and 0x800000

    If it was just a matter of comparing the flag, then wouldn't a while (flag == 0x800000) be enough, why bother with those values in the first place?

  • What would be a good next step in understanding what those magic values mean?

Your Answer

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

Browse other questions tagged or ask your own question.