From e752d647ad9fe4cea47e283270efa6910770331f Mon Sep 17 00:00:00 2001 From: hugsy Date: Mon, 8 Apr 2024 20:56:50 -0700 Subject: [PATCH] checkpoint --- Cargo.toml | 6 +- examples/rp-rs.rs | 12 ++- src/cpu/mod.rs | 37 --------- src/engine.rs | 1 - src/format/elf.rs | 201 ++------------------------------------------- src/format/mach.rs | 13 --- src/format/mod.rs | 80 +++--------------- src/format/pe.rs | 11 ++- src/gadget.rs | 47 ++++------- src/lib.rs | 9 +- src/section.rs | 86 +++++++++---------- src/session.rs | 94 ++++++++++++--------- 12 files changed, 154 insertions(+), 443 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d00e9ba..4020d23 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,12 +24,12 @@ keywords = [ include = ["/Cargo.toml", "/LICENSE", "README.md", "/src/**", "/examples/**"] [dependencies] -goblin = "0.8.0" -capstone = "0.12.0" +log = { version = "0.4.11", features = ["std"] } clap = { version = "4.0.29", features = ["derive"] } colored = "2" bitflags = "2.4.2" -log = { version = "0.4.11", features = ["std"] } +capstone = "0.12.0" +goblin = "0.8.0" [lib] crate-type = ["dylib", "rlib"] diff --git a/examples/rp-rs.rs b/examples/rp-rs.rs index 431c814..b52cb76 100644 --- a/examples/rp-rs.rs +++ b/examples/rp-rs.rs @@ -28,7 +28,7 @@ pub struct Args { /// The verbosity level #[arg(short, long = "verbose", action = clap::ArgAction::Count)] - verbosity: usize, + verbosity: u8, /// Unique gadgets #[arg(short, long, action = ArgAction::SetTrue)] @@ -69,7 +69,13 @@ pub struct Args { fn main() -> GenericResult<()> { let args = Args::parse(); - let verbosity = LevelFilter::Debug; //from(args.verbosity); + let verbosity = match args.verbosity { + 1 => LevelFilter::Warn, + 2 => LevelFilter::Info, + 3 => LevelFilter::Debug, + 4 => LevelFilter::Trace, + _ => LevelFilter::Error, + }; let _output = match args.output_file { None => RopGadgetOutput::Console, @@ -80,7 +86,7 @@ fn main() -> GenericResult<()> { .nb_thread(args.thread_num.into()) .output(_output) .unique_only(args.unique) - // .verbosity(verbosity) + .verbosity(verbosity) .use_color(!args.no_color); info!("Created session: {:?}", sess); diff --git a/src/cpu/mod.rs b/src/cpu/mod.rs index cd76e2a..69a3415 100644 --- a/src/cpu/mod.rs +++ b/src/cpu/mod.rs @@ -49,40 +49,3 @@ impl std::fmt::Debug for dyn Cpu { .finish() } } - -// impl From<&goblin::elf::header::Header> for CpuType { -// fn from(value: &goblin::elf::header::Header) -> Self { -// match value.e_machine { -// goblin::elf::header::EM_386 => CpuType::X86, -// goblin::elf::header::EM_X86_64 => CpuType::X64, -// goblin::elf::header::EM_ARM => CpuType::ARM, -// goblin::elf::header::EM_AARCH64 => CpuType::ARM64, -// _ => panic!("ELF machine format is unsupported"), -// } -// } -// } - -// impl From<&goblin::mach::header::Header> for CpuType { -// fn from(value: &goblin::mach::header::Header) -> Self { -// match value.cputype { -// goblin::mach::constants::cputype::CPU_TYPE_X86 => CpuType::X86, -// goblin::mach::constants::cputype::CPU_TYPE_X86_64 => CpuType::X64, -// goblin::mach::constants::cputype::CPU_TYPE_ARM => CpuType::ARM, -// goblin::mach::constants::cputype::CPU_TYPE_ARM64 => CpuType::ARM64, -// _ => panic!("MachO is corrupted"), -// } -// } -// } - -// impl From<&goblin::pe::header::CoffHeader> for CpuType { -// fn from(obj: &goblin::pe::header::CoffHeader) -> Self { -// match obj.machine { -// goblin::pe::header::COFF_MACHINE_X86 => CpuType::X86, -// goblin::pe::header::COFF_MACHINE_X86_64 => CpuType::X64, -// goblin::pe::header::COFF_MACHINE_ARM => CpuType::ARM, -// goblin::pe::header::COFF_MACHINE_ARMNT => CpuType::ARM, -// goblin::pe::header::COFF_MACHINE_ARM64 => CpuType::ARM64, -// _ => panic!("Unsupported format"), -// } -// } -// } diff --git a/src/engine.rs b/src/engine.rs index f5eab44..bd886bf 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -44,7 +44,6 @@ impl DisassemblyEngine { DisassemblyEngineType::Capstone => Self { disassembler: Box::new(CapstoneDisassembler::new(cpu)), }, - _ => panic!("invalid disassembler"), } } } diff --git a/src/format/elf.rs b/src/format/elf.rs index 0119ab9..223c0df 100644 --- a/src/format/elf.rs +++ b/src/format/elf.rs @@ -1,6 +1,6 @@ // use colored::Colorize; // use goblin; -use log::debug; + use std::convert::TryInto; // use std::fs::File; // use std::io::{BufReader, Read, Seek, SeekFrom}; @@ -8,7 +8,7 @@ use std::mem; // use std::path::PathBuf; use crate::common::GenericResult; -use crate::cpu::{self, CpuType}; +use crate::cpu::{self}; use crate::error; use crate::section::Permission; use crate::{format::FileFormat, section::Section}; @@ -91,122 +91,6 @@ struct ElfSectionHeader64 { sh_entsize: u64, } -// #[derive(Default)] -// pub struct ElfParser<'a> { -// bytes: &'a [u8], -// machine: CpuType, -// number_of_sections: usize, -// section_table_offset: usize, -// entry_point: u64, -// } - -// impl<'a> FileFormatParser<'a> for Parser<'a, Elf> { -// fn parse(bytes: &'a [u8]) -> GenericResult { -// let elf_header: &[u8] = bytes.as_ref(); - -// match elf_header.get(0..ELF_HEADER_MAGIC.len()) { -// Some(ELF_HEADER_MAGIC) => {} -// _ => return Err(error::Error::InvalidMagicParsingError), -// }; - -// let is_64b = { -// let ei_class_off = mem::offset_of!(ElfIdentHeader, ei_class); -// match elf_header.get(ei_class_off) { -// Some(val) => match *val { -// ELF_CLASS_32 => false, -// ELF_CLASS_64 => true, -// _ => { -// return Err(error::Error::InvalidFileError); -// } -// }, -// None => { -// return Err(error::Error::InvalidFileError); -// } -// } -// }; - -// let machine = { -// let ei_class_off = mem::offset_of!(ElfHeader64, e_machine); -// let machine = { -// let mut dst = [0u8; 2]; -// dst.clone_from_slice(elf_header.get(ei_class_off..ei_class_off + 2).unwrap()); -// u16::from_le_bytes(dst) -// }; - -// match machine { -// ELF_MACHINE_386 => Ok(cpu::CpuType::X86), -// ELF_MACHINE_AMD64 => Ok(cpu::CpuType::X64), -// ELF_MACHINE_ARM => match is_64b { -// true => Ok(cpu::CpuType::ARM64), -// false => Ok(cpu::CpuType::ARM), -// }, - -// _ => Err(error::Error::UnsupportedCpuError), -// } -// }?; - -// let entrypoint = { -// match is_64b { -// true => { -// let e_entry_off = mem::offset_of!(ElfHeader64, e_entry); -// u64::from_le_bytes(elf_header[e_entry_off..e_entry_off + 8].try_into().unwrap()) -// } -// false => { -// let e_entry_off = mem::offset_of!(ElfHeader32, e_entry); -// u32::from_le_bytes(elf_header[e_entry_off..e_entry_off + 4].try_into().unwrap()) -// as u64 -// } -// } -// }; - -// let number_of_sections = { -// let e_shnum_off = match is_64b { -// true => mem::offset_of!(ElfHeader64, e_shnum), -// false => mem::offset_of!(ElfHeader32, e_shnum), -// }; -// u16::from_le_bytes(elf_header[e_shnum_off..e_shnum_off + 2].try_into().unwrap()) -// } as usize; - -// let section_table_offset = { -// match is_64b { -// true => { -// let e_shoff_off = mem::offset_of!(ElfHeader64, e_shoff); -// u64::from_le_bytes(elf_header[e_shoff_off..e_shoff_off + 8].try_into().unwrap()) -// as usize -// } -// false => { -// let e_shoff_off = mem::offset_of!(ElfHeader32, e_shoff); -// u32::from_le_bytes(elf_header[e_shoff_off..e_shoff_off + 4].try_into().unwrap()) -// as usize -// } -// } -// }; - -// Ok(Parser:: { -// bytes: elf_header, -// machine: machine, -// number_of_sections: number_of_sections, -// entry_point: entrypoint, -// section_table_offset: section_table_offset, -// image_base: 0, -// }) -// } - -// fn sections(&self) -> GenericResult> { -// Ok(ElfSectionIterator { -// index: 0, -// elf: self, -// } -// .into_iter() -// .collect()) -// } -// } - -// pub struct ElfSectionIterator<'a> { -// index: usize, -// elf: &'a Parser<'a, Elf>, -// } - type ElfSectionIterator<'a> = SectionIterator<'a, Elf>; pub type ElfCharacteristics = u64; @@ -240,7 +124,7 @@ impl<'a> Iterator for ElfSectionIterator<'a> { u64::from_le_bytes(current_section[0x18..0x20].try_into().unwrap()) as usize; Some(Section { - start_address: start_address, + start_address, end_address: start_address.checked_add(section_size as u64)?, name: Some(section_name), permission: Permission::from(flags), @@ -261,81 +145,11 @@ pub struct Elf { number_of_sections: usize, section_table_offset: usize, entry_point: u64, - image_base: u64, + // image_base: u64, } impl Elf { pub fn new(bytes: Vec) -> GenericResult { - // let filepath = path.to_str().unwrap(); - // let elf = Parser::::parse(buf)?; - // let executable_sections = elf - // .sections()? - // .into_iter() - // .filter(|s| s.is_executable()) - // .collect(); - // debug!("{:?}", &executable_sections); - // debug!( - // "looking for executable sections in ELF: '{}'", - // filepath.bold() - // ); - - // let file = File::open(&path).unwrap(); - // let mut reader = BufReader::new(file); - - // for current_section in &obj.section_headers { - // trace!("Testing section {:?}", s); - - // // - // // disregard non executable section - // // - // if !s.is_executable() { - // continue; - // } - - // debug!("Importing section {:?}", s); - - // let mut section = Section::from(s); - // section.name = Some(String::from(&obj.shdr_strtab[s.sh_name])); - - // let mut sect = - // Section::from(current_section).name(&obj.shdr_strtab[current_section.sh_name]); - - // if !sect.permission.contains(Permission::EXECUTABLE) { - // continue; - // } - - // if reader - // .seek(SeekFrom::Start(current_section.sh_addr)) - // .is_err() - // { - // panic!("Invalid offset {}", current_section.sh_addr,) - // } - - // match reader.read_exact(&mut sect.data) { - // Ok(_) => {} - // Err(e) => panic!( - // "Failed to extract section '{}' (size={:#x}) at offset {:#x}: {:?}", - // §.name.clone().unwrap_or_default(), - // §.size(), - // sect.start_address, - // e - // ), - // }; - - // debug!("Adding {}", sect); - // executable_sections.push(sect); - // } - - // let cpu_type = match obj.header.e_machine { - // goblin::elf::header::EM_386 => cpu::CpuType::X86, - // goblin::elf::header::EM_X86_64 => cpu::CpuType::X64, - // goblin::elf::header::EM_ARM => cpu::CpuType::ARM, - // goblin::elf::header::EM_AARCH64 => cpu::CpuType::ARM64, - // _ => { - // panic!("ELF machine format is unsupported") - // } - // }; - let elf_header: &[u8] = bytes.as_ref(); match elf_header.get(0..ELF_HEADER_MAGIC.len()) { @@ -425,7 +239,7 @@ impl Elf { cpu_type: machine, number_of_sections, section_table_offset, - image_base: 0, + // image_base: 0, entry_point: entrypoint, }) } @@ -442,8 +256,8 @@ impl ExecutableFileFormat for Elf { // &self.path // } - fn format(&self) -> FileFormat { - FileFormat::Elf + fn format(&self) -> &str { + "ELF" } fn executable_sections(&self) -> Vec
{ @@ -451,7 +265,6 @@ impl ExecutableFileFormat for Elf { index: 0, obj: self, } - .into_iter() .collect() } diff --git a/src/format/mach.rs b/src/format/mach.rs index 92ecf6e..e9f4e07 100644 --- a/src/format/mach.rs +++ b/src/format/mach.rs @@ -1,16 +1,3 @@ -// use std::fs::File; -// use std::io::{BufReader, Read, Seek, SeekFrom}; -use std::path::PathBuf; - -use colored::Colorize; -use goblin; -use log::debug; - -use crate::cpu; -use crate::{format::FileFormat, section::Permission, section::Section}; - -use super::ExecutableFileFormat; - pub const MACHO_HEADER_MAGIC32: &[u8] = b"\xce\xfa\xed\xfe"; // 0xfeedface pub const MACHO_HEADER_MAGIC64: &[u8] = b"\xcf\xfa\xed\xfe"; // 0xfeedfacf diff --git a/src/format/mod.rs b/src/format/mod.rs index 687288d..daef3e6 100644 --- a/src/format/mod.rs +++ b/src/format/mod.rs @@ -2,8 +2,6 @@ pub mod elf; pub mod mach; pub mod pe; -use std::{fs, path::PathBuf}; - use crate::{ common::GenericResult, cpu::{self, CpuType}, @@ -18,10 +16,10 @@ use crate::{ pub enum FileFormat { // #[default] // Auto, - Pe, - Pe2(pe::Pe), - Elf, - MachO, + // Pe, + Pe(pe::Pe), + Elf(elf::Elf), + // MachO, // todo: Raw, } @@ -30,16 +28,14 @@ impl FileFormat { match buf.get(0..4) { Some(magic) => { if &magic[0..pe::IMAGE_DOS_SIGNATURE.len()] == pe::IMAGE_DOS_SIGNATURE { - Ok(FileFormat::Pe2(pe::Pe::from(buf))) - } - // else if &magic[0..elf::ELF_HEADER_MAGIC.len()] == elf::ELF_HEADER_MAGIC { - // Ok(FileFormat::Elf) + Ok(FileFormat::Pe(pe::Pe::from(buf))) + } else if &magic[0..elf::ELF_HEADER_MAGIC.len()] == elf::ELF_HEADER_MAGIC { + Ok(FileFormat::Elf(elf::Elf::from(buf))) // } else if &magic[0..mach::MACHO_HEADER_MAGIC32.len()] == mach::MACHO_HEADER_MAGIC32 // || &magic[0..mach::MACHO_HEADER_MAGIC64.len()] == mach::MACHO_HEADER_MAGIC64 // { // Ok(FileFormat::MachO) - // } - else { + } else { Err(Error::InvalidMagicParsingError) } } @@ -59,7 +55,7 @@ impl std::fmt::Display for FileFormat { pub trait ExecutableFileFormat: Send + Sync { // fn path(&self) -> &PathBuf; - fn format(&self) -> FileFormat; + fn format(&self) -> &str; fn executable_sections(&self) -> Vec
; // fn executable_sections(&self) -> dyn Iterator; @@ -72,8 +68,8 @@ pub trait ExecutableFileFormat: Send + Sync { match self.cpu_type() { CpuType::X86 => Box::new(cpu::x86::X86 {}), CpuType::X64 => Box::new(cpu::x86::X64 {}), - CpuType::ARM => Box::new(cpu::arm::Arm64 {}), - CpuType::ARM64 => Box::new(cpu::arm::Arm {}), + CpuType::ARM => Box::new(cpu::arm::Arm {}), + CpuType::ARM64 => Box::new(cpu::arm::Arm64 {}), _ => panic!("CPU type is invalid"), } } @@ -96,61 +92,7 @@ impl std::fmt::Debug for dyn ExecutableFileFormat { } } -// /// Attempt to determine the file -// pub fn guess_file_format(file: &PathBuf) -> GenericResult> { -// if !file.as_path().exists() { -// return Err(Error::InvalidFileError); -// } - -// let buffer = fs::read(file.as_path())?; -// // let parsed = match Object::parse(&buffer) { -// // Ok(e) => e, -// // Err(_) => return Err(Error::InvalidFileError), -// // }; - -// match FileFormat::parse(&buffer)? { -// // Object::PE(_) => Ok(Box::new(pe::Pe::new(file.to_path_buf())?)), -// FileFormat::Pe2(pe) => Ok(Box::new(pe)), -// // Object::Elf(obj) => Ok(Box::new(elf::Elf::new(file.to_path_buf(), obj))), -// // Object::Mach(obj) => Ok(Box::new(mach::Mach::new(file.to_path_buf(), obj))), -// // Object::Archive(_) => Err(Error::InvalidFileError), -// // Object::Unknown(_) => Err(Error::InvalidFileError), -// _ => Err(Error::InvalidFileError), -// } -// } - pub struct SectionIterator<'a, T> { index: usize, obj: &'a T, } - -// pub struct Parser<'a> { -// bytes: &'a [u8], -// machine: CpuType, -// number_of_sections: usize, -// section_table_offset: usize, -// entry_point: u64, -// image_base: u64, -// } - -// impl<'a, T> std::fmt::Debug for Parser<'a, T> { -// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -// f.debug_struct("Parser") -// .field("machine", &self.machine) -// .field("number_of_sections", &self.number_of_sections) -// .field( -// "section_table_offset", -// &format_args!("{:#x}", &self.section_table_offset), -// ) -// .field("entry_point", &format_args!("{:#x}", &self.entry_point)) -// .finish() -// } -// } - -// pub trait FileFormatParser<'a> { -// fn parse(bytes: &'a [u8]) -> GenericResult -// where -// Self: Sized; - -// fn sections(&self) -> GenericResult>; -// } diff --git a/src/format/pe.rs b/src/format/pe.rs index bb59e4e..aecda14 100644 --- a/src/format/pe.rs +++ b/src/format/pe.rs @@ -1,3 +1,4 @@ +use std::any::Any; ///! ///! Basic implementation of a PE parser, supports x86/64 to extract quickly the sections ///! @@ -5,11 +6,10 @@ use std::convert::TryInto; // use std::fs::File; // use std::io::Read; // use std::path::PathBuf; -use std::{default, fmt, mem}; +use std::mem; // use goblin::mach::segment::SectionIterator; // use goblin; -use log::debug; use crate::common::GenericResult; use crate::cpu::{self, CpuType}; @@ -680,7 +680,7 @@ impl Pe { // executable_sections, // cpu, cpu_type: machine, - entry_point: entry_point, + entry_point, bytes, number_of_sections, section_table_offset, @@ -701,8 +701,8 @@ impl ExecutableFileFormat for Pe { // &self.path // } - fn format(&self) -> FileFormat { - FileFormat::Pe + fn format(&self) -> &str { + "PE" } // type Item = Section; @@ -715,7 +715,6 @@ impl ExecutableFileFormat for Pe { index: 0, obj: self, } - .into_iter() .filter(|s| s.permission.contains(Permission::EXECUTABLE)) .collect() } diff --git a/src/gadget.rs b/src/gadget.rs index 31e2580..1e9296c 100644 --- a/src/gadget.rs +++ b/src/gadget.rs @@ -1,7 +1,6 @@ extern crate capstone; -// use std::borrow::Borrow; -use std::{default, fmt, thread}; +use std::{fmt, thread}; use std::{ io::{Cursor, Read, Seek, SeekFrom}, sync::Arc, @@ -87,10 +86,7 @@ impl fmt::Display for Instruction { #[derive(Debug, PartialEq, Clone)] pub struct Gadget { - pub address: u64, pub insns: Vec, - pub size: usize, // sum() of sizeof(each_instruction) - pub raw: Vec, // concat() of instruction.raw } impl fmt::Display for Gadget { @@ -98,7 +94,7 @@ impl fmt::Display for Gadget { write!( f, "Gadget(addr={:#x}, text='{}')", - self.address, + self.address(), self.text(false) ) } @@ -106,26 +102,7 @@ impl fmt::Display for Gadget { impl Gadget { pub fn new(insns: Vec) -> Self { - // - // by nature, we should never be here if insns.len() is 0 (should at least have the - // ret insn) so we assert() to be notified - // - if insns.is_empty() { - std::panic::panic_any("GadgetBuildError"); - } - - let size = insns.iter().map(|x| x.size).sum(); - - let raw = insns.iter().flat_map(|x| x.raw.clone()).collect(); - - let address = insns.first().unwrap().address; - - Self { - size, - raw, - address, - insns, - } + Self { insns } } pub fn text(&self, use_color: bool) -> String { @@ -134,6 +111,18 @@ impl Gadget { .map(|i| i.text(use_color).clone() + " ; ") .collect() } + + pub fn address(&self) -> u64 { + self.insns.first().unwrap().address + } + + pub fn bytes(&self) -> Vec { + self.insns.iter().flat_map(|x| x.raw.clone()).collect() + } + + pub fn size(&self) -> usize { + self.insns.iter().map(|x| x.size).sum() + } } // @@ -287,12 +276,12 @@ pub fn find_gadgets_from_position( let last_insn = x.last().unwrap(); if &session.gadget_type == &last_insn.group { let gadget = Gadget::new(x); - if gadgets.iter().all(|x| x.address != gadget.address) { + if gadgets.iter().all(|x| x.address() != gadget.address()) { debug!( "{:?}: pushing new gadget(address={:x}, sz={})", thread::current().id(), - gadget.address, - gadget.raw.len() + &gadget.address(), + &gadget.bytes().len() ); gadgets.push(gadget); } diff --git a/src/lib.rs b/src/lib.rs index 49b7dff..1624b65 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,7 +74,7 @@ pub fn collect_all_gadgets(sess: Session) -> GenericResult> { // // sort by address // - gadgets.sort_by(|a, b| a.address.cmp(&b.address)); + gadgets.sort_by_key(|a| a.address()); // // Write to given output @@ -90,10 +90,10 @@ pub fn collect_all_gadgets(sess: Session) -> GenericResult> { for g in &*gadgets { let addr = match is_64b { true => { - format!("0x{:016x}", g.address) + format!("0x{:016x}", g.address()) } _ => { - format!("0x{:08x}", g.address) + format!("0x{:08x}", g.address()) } }; @@ -118,7 +118,7 @@ pub fn collect_all_gadgets(sess: Session) -> GenericResult> { let mut file = fs::File::create(&filename)?; for gadget in &*gadgets { - let addr = entrypoint_address + gadget.address; + let addr = entrypoint_address + gadget.address(); file.write_all((format!("{:#x} | {}\n", addr, gadget.text(false))).as_bytes())?; } @@ -155,6 +155,7 @@ mod tests { let input_fname = PathBuf::from(format!("tests/bin/{}-{}.{}", sz, arch, fmt)); let s = Session::new(input_fname) .output(RopGadgetOutput::None) + .verbosity(log::LevelFilter::Trace) .nb_thread(4); dbg!(&s); match collect_all_gadgets(s) { diff --git a/src/section.rs b/src/section.rs index 5e1ed3e..ddc96df 100644 --- a/src/section.rs +++ b/src/section.rs @@ -79,13 +79,13 @@ impl fmt::Display for Section { impl Section { pub fn new(start_address: u64, end_address: u64) -> Self { assert!(start_address < end_address); - let sz = (end_address - start_address) as usize; + let _sz = (end_address - start_address) as usize; Self { start_address, end_address, name: None, permission: Permission::NONE, - data: vec![0; sz], + data: Vec::default(), } } @@ -117,56 +117,56 @@ impl Section { } } -impl From<&goblin::elf::section_header::SectionHeader> for Section { - fn from(value: &goblin::elf::section_header::SectionHeader) -> Self { - let mut perm = Permission::NONE; +// impl From<&goblin::elf::section_header::SectionHeader> for Section { +// fn from(value: &goblin::elf::section_header::SectionHeader) -> Self { +// let mut perm = Permission::NONE; - if value.is_executable() { - perm |= Permission::READABLE | Permission::EXECUTABLE; - } +// if value.is_executable() { +// perm |= Permission::READABLE | Permission::EXECUTABLE; +// } - if value.is_writable() { - perm |= Permission::READABLE | Permission::WRITABLE; - } +// if value.is_writable() { +// perm |= Permission::READABLE | Permission::WRITABLE; +// } - let sz = value.sh_size as usize; +// let sz = value.sh_size as usize; - Self { - start_address: value.sh_addr, - end_address: value.sh_addr + sz as u64, - permission: perm, - name: None, - data: vec![0; sz], - } - } -} +// Self { +// start_address: value.sh_addr, +// end_address: value.sh_addr + sz as u64, +// permission: perm, +// name: None, +// data: vec![0; sz], +// } +// } +// } -impl From<&goblin::mach::segment::Segment<'_>> for Section { - fn from(value: &goblin::mach::segment::Segment) -> Self { - let mut perm = Permission::READABLE; +// impl From<&goblin::mach::segment::Segment<'_>> for Section { +// fn from(value: &goblin::mach::segment::Segment) -> Self { +// let mut perm = Permission::READABLE; - if value.flags & goblin::mach::constants::S_ATTR_PURE_INSTRUCTIONS == 0 - || value.flags & goblin::mach::constants::S_ATTR_SOME_INSTRUCTIONS == 0 - { - perm |= Permission::EXECUTABLE; - } +// if value.flags & goblin::mach::constants::S_ATTR_PURE_INSTRUCTIONS == 0 +// || value.flags & goblin::mach::constants::S_ATTR_SOME_INSTRUCTIONS == 0 +// { +// perm |= Permission::EXECUTABLE; +// } - let section_name = match std::str::from_utf8(&value.segname) { - Ok(v) => String::from(v).replace('\0', ""), - Err(_) => "".to_string(), - }; +// let section_name = match std::str::from_utf8(&value.segname) { +// Ok(v) => String::from(v).replace('\0', ""), +// Err(_) => "".to_string(), +// }; - let sz = value.vmsize as usize; +// let sz = value.vmsize as usize; - Self { - start_address: value.vmaddr, - end_address: value.vmaddr + sz as u64, - name: Some(section_name), - permission: perm, - data: vec![0; sz], - } - } -} +// Self { +// start_address: value.vmaddr, +// end_address: value.vmaddr + sz as u64, +// name: Some(section_name), +// permission: perm, +// data: vec![0; sz], +// } +// } +// } // impl From<&goblin::pe::section_table::SectionTable> for Section { // fn from(value: &goblin::pe::section_table::SectionTable) -> Self { diff --git a/src/session.rs b/src/session.rs index 65fa223..9ea73fb 100644 --- a/src/session.rs +++ b/src/session.rs @@ -7,10 +7,10 @@ use colored::*; use log::{debug, error, info, warn, Level, LevelFilter, Metadata, Record}; use crate::common::GenericResult; -use crate::cpu; + use crate::engine::{DisassemblyEngine, DisassemblyEngineType}; use crate::error::Error; -use crate::format::{self, ExecutableFileFormat, FileFormat}; +use crate::format::{self, FileFormat}; use crate::gadget::{ find_gadgets_from_position, get_all_valid_positions_and_length, Gadget, InstructionGroup, }; @@ -76,8 +76,8 @@ impl ExecutableDetails { let format = match FileFormat::parse(buffer)? { // Object::PE(_) => Ok(Box::new(pe::Pe::new(file.to_path_buf())?)), - FileFormat::Pe2(pe) => Box::new(pe), - // Object::Elf(obj) => Ok(Box::new(elf::Elf::new(file.to_path_buf(), obj))), + FileFormat::Pe(pe) => Box::new(pe), + // FileFormat::Elf(elf) => Box::new(elf), // Object::Mach(obj) => Ok(Box::new(mach::Mach::new(file.to_path_buf(), obj))), // Object::Archive(_) => Err(Error::InvalidFileError), // Object::Unknown(_) => Err(Error::InvalidFileError), @@ -86,22 +86,7 @@ impl ExecutableDetails { } }; - // let format = guess_file_format(&fpath)?; - - // let cpu: Box = match format.cpu_type() { - // cpu::CpuType::X86 => Box::new(cpu::x86::X86 {}), - // cpu::CpuType::X64 => Box::new(cpu::x86::X64 {}), - // cpu::CpuType::ARM64 => Box::new(cpu::arm::Arm64 {}), - // cpu::CpuType::ARM => Box::new(cpu::arm::Arm {}), - // _ => panic!("CPU type is invalid"), - // }; - // let cpu = Box::new( Cpu::from(format.cpu_type()) ); - - Ok(Self { - filepath, - // cpu, - format, - }) + Ok(Self { filepath, format }) } pub fn is_64b(&self) -> bool { @@ -123,6 +108,7 @@ pub enum RopGadgetOutput { File(PathBuf), } +#[derive(Debug)] struct RpLogger; impl log::Log for RpLogger { @@ -132,15 +118,20 @@ impl log::Log for RpLogger { fn log(&self, record: &Record) { if self.enabled(record.metadata()) { - let level = match record.level().to_string().as_str() { - "ERROR" => "ERROR".red(), - "WARN" => "WARN".magenta(), - "INFO" => "INFO".green(), - "DEBUG" => "DEBUG".cyan(), - _ => "TRACE".bold(), + let level = match record.level() { + Level::Error => "ERROR".bold().red(), + Level::Warn => "WARN".bold().yellow(), + Level::Info => "INFO".bold().green(), + Level::Debug => "DEBUG".bold().cyan(), + Level::Trace => "TRACE".bold().magenta(), }; - println!("[{}] - {}", level, record.args()); + println!( + "{} - {:?} - {}", + level, + std::thread::current().id(), + record.args() + ); } } @@ -152,6 +143,7 @@ pub struct Session { // // session required information // + // logger: RpLogger, pub info: ExecutableDetails, pub nb_thread: u8, // pub verbosity: LevelFilter, @@ -175,6 +167,8 @@ pub struct Session { pub profile_type: RopProfileStrategy, } +// static RP_LOGGER: RpLogger = RpLogger {}; + impl Session { pub fn new(filepath: PathBuf) -> Self { let info = match ExecutableDetails::new(filepath) { @@ -182,6 +176,12 @@ impl Session { Err(_) => panic!("Session initialization (ExecutableDetails) failed"), }; + let logger = Box::new(RpLogger {}); + match log::set_boxed_logger(logger) { + Ok(_) => {} + Err(e) => println!("set_logger failed: {}", &e.to_string()), + }; + Session { info, nb_thread: Default::default(), @@ -218,9 +218,11 @@ impl Session { Self { use_color, ..self } } - // pub fn verbosity(self, verbosity: LevelFilter) -> Self { - // Self { verbosity, ..self } - // } + pub fn verbosity(self, verbosity: LevelFilter) -> Self { + log::set_max_level(verbosity); + debug!("Verbosity changed to {}", &verbosity); + Self { ..self } + } pub fn filepath(&self) -> &PathBuf { &self.info.filepath @@ -277,7 +279,10 @@ pub fn find_gadgets(session: Arc) -> GenericResult<()> { let number_of_sections = info.format.executable_sections().len(); let nb_thread = session.nb_thread as usize; - debug!("Using {nb_thread} threads over {number_of_sections} section(s) of executable code..."); + debug!( + "Using {} threads over {} section(s) of executable code...", + &nb_thread, &number_of_sections + ); // // Multithread parsing of each section @@ -372,11 +377,11 @@ pub fn find_gadgets(session: Arc) -> GenericResult<()> { /// /// Worker routine to search for gadgets /// -fn thread_worker(session: Arc, index: usize, cursor: usize) -> Vec { +fn thread_worker(session: Arc, section_index: usize, cursor: usize) -> Vec { let cpu = session.info.format.cpu(); let engine = DisassemblyEngine::new(&session.engine_type, cpu.as_ref()); debug!( - "{:?}: Initialized engine {} for {:?}", + "{:?}: Initialized {} for {:?}", thread::current().id(), engine, cpu.cpu_type() @@ -384,7 +389,7 @@ fn thread_worker(session: Arc, index: usize, cursor: usize) -> Vec = Vec::new(); let sections = session.info.format.executable_sections(); - if let Some(section) = sections.get(index) { + if let Some(section) = sections.get(section_index) { debug!( "{:?}: Processing section '{:?}'", thread::current().id(), @@ -395,17 +400,24 @@ fn thread_worker(session: Arc, index: usize, cursor: usize) -> Vec e, + Ok(chunks) => chunks, Err(e) => { - error!("error in `get_all_valid_positions_and_length`: {:?}", e); - // TODO use errors - return Vec::default(); + error!("Error in `get_all_valid_positions_and_length`: {:?}", &e); + return gadgets; } }; + if chunks.is_empty() { + warn!( + "No pattern found in section {:?} at position={}", + §ion, &cursor + ); + return gadgets; + } + for (pos, len) in chunks { println!( - "{:?}: Processing Chunk{:?}[..{:x}+{:x}] (size={:x})", + "{0:?}: Processing Chunk {1:?}[{2:x}..{2:x}+{3:x}] (size={4:x})", thread::current().id(), §ion.name, pos, @@ -416,7 +428,7 @@ fn thread_worker(session: Arc, index: usize, cursor: usize) -> Vec gadgets.append(&mut g), Err(e) => { - println!("error in `find_gadgets_from_position`: {:?}", e); + error!("error in `find_gadgets_from_position`: {:?}", &e); break; } } @@ -431,7 +443,7 @@ fn thread_worker(session: Arc, index: usize, cursor: usize) -> Vec