Added TLZMA support

This commit is contained in:
xavo95 2025-01-03 00:10:22 +01:00
parent 8565eba0f3
commit 187c271657
Signed by: xavo95
GPG key ID: CBF8ADED6DEBB783
4 changed files with 147 additions and 1 deletions

39
Cargo.lock generated
View file

@ -10,6 +10,27 @@ dependencies = [
"offset-finder", "offset-finder",
] ]
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "crc"
version = "3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636"
dependencies = [
"crc-catalog",
]
[[package]]
name = "crc-catalog"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]] [[package]]
name = "crossbeam-deque" name = "crossbeam-deque"
version = "0.8.6" version = "0.8.6"
@ -66,6 +87,16 @@ version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]]
name = "lzma-rs"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e"
dependencies = [
"byteorder",
"crc",
]
[[package]] [[package]]
name = "offset-finder" name = "offset-finder"
version = "0.1.0" version = "0.1.0"
@ -241,6 +272,14 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "tlzma"
version = "0.1.0"
dependencies = [
"lzma-rs",
"thiserror 2.0.9",
]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.14" version = "1.0.14"

View file

@ -5,7 +5,8 @@ members = [
"cursor", "cursor",
"offset-finder", "offset-finder",
"pe-utils", "pe-utils",
"restorer" "restorer",
"tlzma"
] ]
[workspace.package] [workspace.package]
@ -15,6 +16,7 @@ edition = "2021"
[workspace.dependencies] [workspace.dependencies]
goblin = "0.9.2" goblin = "0.9.2"
log = "0.4.22" log = "0.4.22"
lzma-rs = { version = "0.3.0", features = ["raw_decoder"] }
patternscanner = "0.5.0" patternscanner = "0.5.0"
serde = { version = "1.0.217", features = ["derive"] } serde = { version = "1.0.217", features = ["derive"] }
thiserror = "2.0.9" thiserror = "2.0.9"

8
tlzma/Cargo.toml Normal file
View file

@ -0,0 +1,8 @@
[package]
name = "tlzma"
version.workspace = true
edition.workspace = true
[dependencies]
lzma-rs.workspace = true
thiserror.workspace = true

97
tlzma/src/lib.rs Normal file
View file

@ -0,0 +1,97 @@
use std::io::Cursor;
use lzma_rs::decompress::raw::{LzmaDecoder, LzmaParams, LzmaProperties};
const LZMA_PROP_MAX: u32 = 9 * 5 * 5;
const LZMA_DIC_MIN: u32 = 1 << 12;
const PROPS_XK: u8 = 0x9d ^ 0xf3;
const PROPS_AK: u8 = 0x65 ^ 0xa2;
const USIZE_XK: u32 = 0x218d5a0a ^ 0xab8832cb;
const USIZE_AK: u32 = 0x5e98c630 ^ 0x66e44294;
const CSIZE_XK: u32 = 0x6dfc36ff ^ 0xfc4c53bf;
const CSIZE_AK: u32 = 0xc68dd929 ^ 0x78bdb6e9;
const GOOD_PROPS: u32 = 0xcd386d0f ^ 0x996f3a58;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Lzma Error: {0}")]
Lzma(#[from] lzma_rs::error::Error),
#[error("TryFromSlice Error: {0}")]
TryFromSlice(#[from] std::array::TryFromSliceError),
#[error("LZMA header invalid properties: {0} must be < 225")]
PropsTooBig(u32),
}
#[repr(packed)]
#[derive(Debug)]
pub struct TlzmaHeader {
pub props: [u8; 5],
pub _1: [u8; 3],
pub uncompressed_size: u32,
pub compressed_size: u32,
}
impl TlzmaHeader {
pub fn tlzma_test_header(data: &[u8]) -> Result<bool, Error> {
let tlzma: TlzmaHeader = unsafe { std::ptr::read(data.as_ptr() as *const _) };
let good_props = u32::from_le_bytes(tlzma.props[1..].try_into()?);
Ok(GOOD_PROPS == good_props)
}
pub fn parse_header(data: &[u8]) -> Self {
let mut tlzma: TlzmaHeader = unsafe { std::ptr::read(data.as_ptr() as *const _) };
for i in 0..tlzma.props.len() {
tlzma.props[i] = (tlzma.props[i] ^ PROPS_XK).wrapping_add(PROPS_AK);
}
tlzma.uncompressed_size = (tlzma.uncompressed_size ^ USIZE_XK).wrapping_add(USIZE_AK);
tlzma.compressed_size = (tlzma.compressed_size ^ CSIZE_XK).wrapping_add(CSIZE_AK);
tlzma
}
pub fn get_next_chunk_offset(&self, offset: usize, data: &[u8]) -> Result<usize, Error> {
let tmp = offset + size_of::<Self>() + self.compressed_size as usize;
let input = &data[tmp..tmp + 16];
match Self::tlzma_test_header(input)? {
true => Ok(tmp),
false => Ok(offset + self.uncompressed_size as usize)
}
}
pub fn lzma_decode(&self, input: &[u8], offset: usize) -> Result<(Vec<u8>, u32), Error> {
let params = self.get_params_from_props()?;
let mut decoder = LzmaDecoder::new(params, None)?;
let start = offset + size_of::<Self>();
let end = start + self.compressed_size as usize;
let mut data = Cursor::new(&input[start..end]);
let mut output = Vec::with_capacity(self.uncompressed_size as usize);
decoder.decompress(&mut data, &mut output)?;
Ok((output, self.uncompressed_size))
}
fn get_params_from_props(&self) -> Result<LzmaParams, Error> {
let mut pb = self.props[0] as u32;
if pb >= LZMA_PROP_MAX {
return Err(Error::PropsTooBig(pb));
}
let mut dic = u32::from_le_bytes(self.props[1..].try_into()?);
if dic < LZMA_DIC_MIN {
dic = LZMA_DIC_MIN;
}
let lc: u32 = pb % 9;
pb /= 9;
let lp: u32 = pb % 5;
pb /= 5;
Ok(
LzmaParams::new(
LzmaProperties { lc, lp, pb },
dic,
Some(self.uncompressed_size as u64),
)
)
}
}