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!");
}
Ok(())
}
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
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
_____________repeating_______________
0x800000, 4876720, 0xfd377a585a000000
0x800000, 4761144, 0xfd377a585a000000
0x800000, 4905372, 0xfd377a585a000000
_____________repeating_______________
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
and0x800000
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?