Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ To run the CLI via `cargo` directly, remember to add arguments after an extra
`--`; i.e., to print the general help, invoke `cargo run --relase -- -h`, or,
for a subcommand, e.g. `cargo run --relase -- me clean -h`.

For inspiration of how to explore and add new features, take a look at the notes
on the [analysis process](docs/analysis.md) and [extending](docs/extend.md).

### Caveats

Be careful: Always check claimed offsets and sizes in binary data structures to
be both reasonable and within the bounds of the given slices. Sizes can be
direct values, such as "this here points to that 8KB other thing", or counts,
which may lead to overallocation. Say a thing that typically has about a dozen
entries uses a `u32` to represent its count, which may end up being huge. As a
defensive measure, implement a maximum, say 100, to avoid running out of memory.

## TODOs

- [ ] sync up; <https://github.com/corna/me_cleaner> has another patch that
Expand Down
95 changes: 81 additions & 14 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
//! So we can only provide meaningful information by looking at a full firmware
//! image in its entirety. This tool brings together all publicly known details.

use std::fs;
use std::io::{self, Write};
use std::{fs, path};

use clap::{Parser, Subcommand};
use log::{debug, error, info, warn};
Expand Down Expand Up @@ -80,6 +80,15 @@ enum MeCommand {
/// File to read
file_name: String,
},
/// Extract directory partition
#[clap(verbatim_doc_comment)]
Extract {
/// Partition to extract
#[clap(long, short)]
part_name: Option<String>,
/// File to read
file_name: String,
},
}

#[derive(Subcommand)]
Expand Down Expand Up @@ -175,19 +184,13 @@ fn main() -> Result<(), io::Error> {
show::show(&fw, verbose);
println!();

let me_res = match fw.me {
Some(r) => r,
None => {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"no ME firmware recognized",
));
}
};
let me = match me_res {
Ok(r) => r,
Err(e) => return Err(io::Error::new(io::ErrorKind::InvalidData, e)),
};
let me = fw
.me
.ok_or(io::Error::new(
io::ErrorKind::NotFound,
"no ME firmware recognized",
))?
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
if check {
let fpt = &me.fpt_area.fpt;
let cs = fpt.header_checksum();
Expand Down Expand Up @@ -263,6 +266,70 @@ fn main() -> Result<(), io::Error> {
let fw = Firmware::parse(&data, debug);
show::show(&fw, verbose);
}
MeCommand::Extract {
part_name,
file_name,
} => {
use intel_fw::part::gen2::Gen2Partition;
use intel_fw::part::gen3::Gen3Partition;
use intel_fw::part::partitions::Partitions;

let data = fs::read(&file_name)?;
info!("Extracting {}", file_name);

let fw = Firmware::parse(&data, debug);
let me = fw
.me
.ok_or(io::Error::new(
io::ErrorKind::NotFound,
"no ME firmware recognized",
))?
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;

let extract_dir = |dir_name: &String| -> Result<(), std::io::Error> {
let target_dir = path::Path::new("extracted").join(dir_name);
fs::create_dir_all(&target_dir)?;
let mods = &me.fpt_area.files_for_dir(dir_name);
for m in mods {
let f = target_dir.join(&m.name);
info!(" Extracting file {f:?}");
let mut f = fs::File::create(f)?;
f.write_all(&m.data)?;
}
Ok(())
};

// TODO: For partitions not holding a directory, extract the
// whole partition.
if let Some(p) = part_name {
extract_dir(&p)?;
} else {
match &me.fpt_area.partitions {
Partitions::Gen2(parts) => {
info!("ME Gen 2 recognized");
warn!("NOTE: Huffman modules will not be decompressed yet.");
for p in parts {
if let Gen2Partition::Dir(d) = p {
let pname = &d.dir.name;
info!(" Extracting partition {pname}");
extract_dir(pname)?;
}
}
}
Partitions::Gen3(parts) => {
info!("ME Gen 3 recognized");
for p in parts {
if let Gen3Partition::Dir(d) = p {
let pname = &d.cpd.name;
info!(" Extracting partition {pname}");
extract_dir(pname)?;
}
}
}
_ => error!("Unknown/unrecognized partitioning scheme"),
}
}
}
},
}

Expand Down
68 changes: 68 additions & 0 deletions src/me.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use serde::{Deserialize, Serialize};

use crate::EMPTY;
use crate::dir::gen2::Module;
use crate::dir::{
gen2::Directory as Gen2Directory,
gen3::{CPD_MAGIC_BYTES, CodePartitionDirectory},
Expand Down Expand Up @@ -50,6 +51,12 @@ pub struct FPTArea {
pub original_size: usize,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct File {
pub name: String,
pub data: Vec<u8>,
}

impl FPTArea {
/// Clear out fully removable partitions and adjust FPT
pub fn clean(&mut self, options: &ClearOptions) {
Expand All @@ -67,6 +74,67 @@ impl FPTArea {
}
}

pub fn files_for_dir(&self, part_name: &String) -> Vec<File> {
match &self.partitions {
Partitions::Gen2(parts) => {
let dir = parts.iter().find(|p| p.entry().name() == *part_name);
match dir {
Some(Gen2Partition::Dir(d)) => {
let mut res = vec![];
for m in &d.dir.modules {
match m {
Module::Huffman(Ok((e, _))) => {
let o = e.offset as usize;
let s = e.size as usize;
let data = d.data[o..o + s].to_vec();
let name = format!("{}.huff", e.name());
res.push(File { name, data });
}
Module::Uncompressed(e) => {
let o = e.offset as usize;
let s = e.size as usize;
let data = d.data[o..o + s].to_vec();
let name = format!("{}.bin", e.name());
res.push(File { name, data });
}
Module::Lzma(Ok(e)) => {
let o = e.offset as usize;
let s = e.size as usize;
let data = d.data[o..o + s].to_vec();
let name = format!("{}.lzma", e.name());
res.push(File { name, data });
}
_ => {}
}
}
res
}
_ => vec![],
}
}
Partitions::Gen3(parts) => {
let dir = parts.iter().find(|p| p.entry().name() == *part_name);
match dir {
Some(Gen3Partition::Dir(d)) => {
let mut res = vec![];
for e in &d.cpd.entries {
let f = e.flags_and_offset;
let o = f.offset() as usize;
let s = e.size as usize;
let data = d.data[o..o + s].to_vec();
// NOTE: already includes extension
let name = e.name();
res.push(File { name, data });
}
res
}
_ => vec![],
}
}
_ => vec![],
}
}

pub fn check_dir_sigs(&self) -> Vec<(String, Result<(), String>)> {
match &self.partitions {
Partitions::Gen2(parts) => {
Expand Down
Loading