Skip to content

Commit

Permalink
'data' subcommand to print contents of section
Browse files Browse the repository at this point in the history
  • Loading branch information
Arsynth committed Jun 7, 2023
1 parent bbcbf3f commit 823d45f
Show file tree
Hide file tree
Showing 13 changed files with 254 additions and 7 deletions.
4 changes: 3 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
}
},
// "args": ["syms", "/bin/cat", "--arch", "x86_64"],
"args": ["rel", "target/debug/incremental/schnauzer-10au2ifpzd0gz/s-gkcav42b0p-1if0ww9-1gb8dkls2cwuy/1611yq7cyka4fmb4.o"],
// "args": ["data", "/bin/cat", "--arch", "x86_64", "-s", "__TEXT", "__cstring"],
"args": ["data", "/bin/cat", "-s", "__TEXT", "__cstring"],
// "args": ["rel", "target/debug/incremental/schnauzer-10au2ifpzd0gz/s-gkcav42b0p-1if0ww9-1gb8dkls2cwuy/1611yq7cyka4fmb4.o"],
"cwd": "${workspaceFolder}"
},
{
Expand Down
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = "2021"
license = "MIT OR Apache-2.0"
repository = "https://github.com/Arsynth/schnauzer"
keywords = ["macho", "mach-o", "parser", "otool", "stab"]
categories = ["command-line-interface"]
categories = ["command-line-interface", "parsing"]
description = "Library for parsing Mach-O files"
exclude = [
"testable/*"
Expand All @@ -18,4 +18,5 @@ scroll = { version = "0.11.0", features = ["derive"] }
uuid = "1.3.1"
colored = "2"
getopts = "0.2.21"
kex = "0.1.5"
schnauzer-derive = { version = "0.1.0", path = "schnauzer-derive" }
2 changes: 1 addition & 1 deletion src/commands/common/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ impl Format {
pub(crate) fn build(opts: &mut Options, args: &[String]) -> crate::result::Result<Self> {
let matches = match opts.parse(args) {
Ok(m) => m,
Err(_) => return Err(crate::result::Error::CantParseArguments),
Err(f) => return Err(crate::result::Error::Text(f.to_string())),
};

Ok(Self {
Expand Down
2 changes: 1 addition & 1 deletion src/commands/common/object_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ impl ObjectFilter {
pub(crate) fn build(opts: &mut Options, args: &[String]) -> Result<Self> {
let matches = match opts.parse(args) {
Ok(m) => m,
Err(_) => return Err(Error::CantParseArguments),
Err(f) => return Err(Error::Text(f.to_string())),
};

Ok(Self {
Expand Down
88 changes: 88 additions & 0 deletions src/commands/data/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use getopts::Options;

use crate::{commands::common::{options::*, Format}, result::Error};

const COMMON_ERROR: &str = "Unable to obtain section";

const SECT_FLAG_SHORT: &str = "s";
const SECT_FLAG_LONG: &str = "sect";

pub(super) struct Config {
pub(super) format: Format,
pub(super) seg: String,
pub(super) sect: String,
}

impl Config {
pub(super) fn build(opts: &mut Options, args: &[String]) -> crate::result::Result<Self> {
let matches = match opts.parse(args.clone()) {
Ok(m) => m,
Err(f) => return Err(crate::result::Error::Text(f.to_string())),
};

let _ = match matches.opt_positions(SECT_FLAG_SHORT).first() {
Some(pos) => pos,
None => return Err(Error::Text(COMMON_ERROR.to_string())),
};

match Self::search_after_sect_opt(args) {
Ok((seg, sect)) => {
Ok(Self {
format: Format::build(opts, args)?,
seg: seg.clone(),
sect: sect.clone(),
})
},
Err(e) => Err(e),
}
}

fn search_after_sect_opt(args: &[String]) -> crate::result::Result<(&String, &String)> {
let short = &format!("-{SECT_FLAG_SHORT}");
let long = &format!("--{SECT_FLAG_LONG}");
let pos = args.iter().enumerate().find(|a| {
let arg = a.1;
arg == short || arg == long
});

match pos {
Some((pos, _)) => Self::search_names_at(args, pos + 1),
None => Err(Error::Text(COMMON_ERROR.to_string())),
}
}

fn search_names_at(args: &[String], pos: usize) -> crate::result::Result<(&String, &String)> {
const ERROR_STR: &str = "Incorrect name pattern. Provide \"segname sectname\"";
let strs = args.get(pos..=pos+1);
match strs {
Some(strs) => {
let filtered: Vec<&String> = strs.iter().filter(|s| !s.starts_with("-")).collect();
if filtered.len() == 2 {
Ok((filtered[0], filtered[1]))
} else {
return Err(Error::Text(ERROR_STR.to_string()));
}
},
None => Err(Error::Text(ERROR_STR.to_string())),
}
}
}

impl Config {
fn required_option_items() -> Vec<OptionItem> {
vec![
OptionItem {
option_type: OptionType::Arg(IsRequired(true)),
name: OptionName::ShortLong(SECT_FLAG_SHORT.to_string(), SECT_FLAG_LONG.to_string()),
description: "Section to display".to_string(),
hint: "segname sectname".to_string(),
},
]
}

pub(super) fn option_items() -> Vec<OptionItem> {
let mut items = Self::required_option_items();
items.append(&mut Format::option_items());
items
}
}
100 changes: 100 additions & 0 deletions src/commands/data/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
use super::common;
use super::common::options::AddToOptions;
use super::common::ObjectFilter;
use super::handler;
use super::handler::*;
use super::Printer;
use super::Result;
use crate::*;
use colored::*;

mod config;
use config::*;
use getopts::Options;

static SUBCOMM_NAME: &str = "data";

pub(super) struct DataHandler {
printer: Printer,
}

impl DataHandler {
pub(super) fn new(printer: Printer) -> Self {
Self { printer }
}
}

impl Handler for DataHandler {
fn command_name(&self) -> String {
SUBCOMM_NAME.to_string()
}

fn description(&self) -> String {
"Prints hex dump".to_string()
}

fn can_handle_with_name(&self, name: &str) -> bool {
SUBCOMM_NAME == name
}

fn handle_object(&self, object: ObjectType, other_args: Vec<String>) -> Result<()> {
let mut opts = Options::new();
self.accepted_option_items().add_to_opts(&mut opts);
let config = Config::build(&mut opts, &other_args)?;
let filter = ObjectFilter::build(&mut opts, &other_args)?;

let objects = &filter.get_objects(object);
let out_arch = objects.len() > 1;
for (idx, obj) in objects.iter().enumerate() {
if out_arch {
common::out_single_arch_title(
&self.printer,
&obj.header(),
idx,
config.format.short,
);
}
self.handle_load_commands(obj.load_commands_iterator(), &config);
}

Ok(())
}

fn accepted_option_items(&self) -> Vec<common::options::OptionItem> {
let mut items = handler::default_option_items();
items.append(&mut Config::option_items());
items
}
}

impl DataHandler {
fn handle_load_commands(&self, commands: LoadCommandIterator, config: &Config) {
let segs = commands.filter_map(|cmd| match cmd.variant {
LcVariant::Segment32(s) | LcVariant::Segment64(s) => {
if s.segname.to_string() == config.seg {
Some(s)
} else {
None
}
}
_ => None,
});

let sections = segs.filter_map(|seg| {
seg.sections_iterator()
.find(|s| s.sectname.to_string() == config.sect)
});

for sect in sections {
self.handle_section(sect);
}
}

fn handle_section(&self, sect: Section) {
use crate::output::hex::*;
println!("{} {}", sect.segname.to_string().yellow(), sect.sectname.to_string().yellow());

_ = dump_section(&sect);
println!("")
}
}
3 changes: 3 additions & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod segs;
mod syms;
mod rel;
mod lc;
mod data;

mod common;

Expand All @@ -29,6 +30,7 @@ use segs::*;
use syms::*;
use rel::*;
use lc::*;
use data::*;

use std::process::exit;

Expand Down Expand Up @@ -162,5 +164,6 @@ fn available_handlers() -> Vec<Box<dyn Handler>> {
Box::new(HeadersHandler::new(printer.clone())),
Box::new(RelHandler::new(printer.clone())),
Box::new(LcHandler::new(printer.clone())),
Box::new(DataHandler::new(printer.clone())),
]
}
2 changes: 1 addition & 1 deletion src/commands/segs/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ impl Config {

let matches = match opts.parse(args.clone()) {
Ok(m) => m,
Err(_) => return Err(crate::result::Error::CantParseArguments),
Err(f) => return Err(crate::result::Error::Text(f.to_string())),
};

let segs_only = matches.opt_present(SEGS_FLAG);
Expand Down
18 changes: 18 additions & 0 deletions src/output/hex.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use crate::result::Result;
use kex::*;
use std::io::{stdout};

use crate::Section;

pub(crate) fn dump_section(sect: &Section) -> Result<()> {
let fmt = Formatters::new(
AddressFormatter::new(16),
ByteFormatter::new(),
CharFormatter::new(),
);
let config = Config::new(fmt, 16, 4, ("|".to_string(), '|'.to_string()));
let mut out = Printer::new(Box::new(stdout()), sect.addr.0 as usize, config);
let result = sect.read_data_to(&mut out);
out.finish();
result
}
1 change: 1 addition & 0 deletions src/output/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod table;
pub mod hex;

use colored::{self, ColoredString, Colorize};
use std::fmt::Display;
Expand Down
4 changes: 2 additions & 2 deletions src/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::fmt;
pub enum Error {
BadMagic(u32),
BadBufferLength,
CantParseArguments,
Text(String),
Other(Box<dyn std::error::Error>),
}

Expand All @@ -13,7 +13,7 @@ impl Error {
match self {
Error::BadMagic(v) => format!("Unknown magic: {v}"),
Error::BadBufferLength => format!("Invalid buffer length"),
Error::CantParseArguments => format!("Error while parsing arguments"),
Error::Text(txt) => txt.clone(),
Error::Other(e) => format!("Internal error: {:#?}", e),
}
}
Expand Down
18 changes: 18 additions & 0 deletions src/types/section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ use scroll::ctx::SizeWith;
use scroll::Endian;
use scroll::IOread;
use std::fmt::Debug;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::io::Write;

/// Both `section` and `section_64`
#[derive(Debug, AutoEnumFields)]
Expand Down Expand Up @@ -80,6 +82,22 @@ impl Section {
}
}

impl Section {
pub fn read_data_to(&self, out: &mut dyn Write) -> Result<()> {
let mut reader = self.reader.borrow_mut();
reader.seek(SeekFrom::Start(self.object_file_offset + self.offset as u64))?;

let buf = &mut vec![0u8; self.size.0 as usize];
match reader.read_exact(buf) {
Ok(_) => match out.write_all(buf) {
Ok(_) => Ok(()),
Err(e) => Err(crate::result::Error::Other(Box::new(e))),
},
Err(e) => Err(crate::result::Error::Other(Box::new(e))),
}
}
}

impl SizeWith<X64Context> for Section {
fn size_with(ctx: &X64Context) -> usize {
let endian = ctx.endian();
Expand Down

0 comments on commit 823d45f

Please sign in to comment.