From a045dab178245b7a803e2c180b6d6466796986b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ege=20Balc=C4=B1?= Date: Wed, 10 Jul 2024 20:04:00 +0200 Subject: [PATCH] Several tracer and disassembler improvments. --- src/main.rs | 9 +- src/options.rs | 52 ++++++--- src/x86_64/deoptimizer.rs | 218 +++++++++++++------------------------ src/x86_64/disassembler.rs | 82 ++++++++++++++ src/x86_64/mod.rs | 1 + src/x86_64/tests.rs | 2 +- src/x86_64/tracer.rs | 111 +++++-------------- 7 files changed, 223 insertions(+), 252 deletions(-) create mode 100644 src/x86_64/disassembler.rs diff --git a/src/main.rs b/src/main.rs index 9e83f8a..497657c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +// use crate::x86_64::disassembler; +use crate::x86_64::disassembler::disassemble; use base64::prelude::*; use colored::Colorize; use log::{error, info, warn, LevelFilter}; @@ -53,16 +55,13 @@ fn main() { info!("Input file size: {}", file.len()); let mut deopt = x86_64::Deoptimizer::new(); deopt.freq = opts.freq; + deopt.trace = opts.trace; deopt.allow_invalid = opts.allow_invalid; deopt.set_skipped_offsets(opts.skip_offsets); if let Err(e) = deopt.set_transform_gadgets(opts.transforms) { error!("{}", e); return; } - if let Err(e) = deopt.set_syntax(opts.syntax) { - error!("{}", e); - return; - } let start_addr = match u64::from_str_radix(opts.addr.trim_start_matches("0x"), 16) { Ok(addr) => addr, Err(e) => { @@ -103,7 +102,7 @@ fn main() { } info!("De-optimized binary written into {}", opts.outfile); } else { - let source = match deopt.disassemble(opts.bitness, start_addr, output) { + let source = match disassemble(&output, opts.bitness, start_addr, opts.syntax) { Ok(s) => s, Err(e) => { error!("{}", e); diff --git a/src/options.rs b/src/options.rs index d2d2e72..17bb53c 100644 --- a/src/options.rs +++ b/src/options.rs @@ -17,6 +17,8 @@ pub enum ArgParseError { FileNotFound, #[error("Invalid offset values.")] InvalidOffsetValues, + #[error("Invalid formatter syntax.")] + InvalidSyntax, #[error("Address parsing failed: {0}")] AddressParseError(#[from] FromHexError), #[error("Integer parsing failed: {0}")] @@ -35,55 +37,59 @@ pub struct Options { #[arg(long, short = 'a', default_value_t = String::from("x86"))] pub arch: String, - /// target binary file name. + /// Target binary file name. #[arg(long, short = 'f', default_value_t = String::new())] pub file: String, - /// output file name. + /// Output file name. #[arg(long, short = 'o', default_value_t = String::new())] pub outfile: String, - /// source assembly file. + /// Source assembly file. #[arg(long, short = 's', default_value_t = String::new())] pub source: String, - /// assembler formatter syntax (nasm/masm/intel/gas). + /// Assembler formatter syntax (nasm/masm/intel/gas). #[arg(long, default_value_t = String::from("keystone"))] pub syntax: String, - /// bitness of the binary file (16/32/64). + /// Bitness of the binary file (16/32/64). #[arg(long, short = 'b', default_value_t = 64)] pub bitness: u32, - /// start address in hexadecimal form. + /// Start address in hexadecimal form. #[arg(long, short = 'A', default_value_t = String::from("0x0000000000000000"))] pub addr: String, /// File offset range for skipping deoptimization (eg: 0-10 for skipping first ten bytes). #[arg(long, value_parser=parse_offset, num_args = 1.., value_delimiter = ',')] - pub skip_offsets: Vec<(u32, u32)>, + pub skip_offsets: Vec<(u64, u64)>, - /// total number of deoptimization cycles. + /// Auto-skip dead-code and strings by control flow tracing. + #[arg(long, short = 'T')] + pub trace: bool, + + /// Total number of deoptimization cycles. #[arg(long, short = 'c', default_value_t = 1)] pub cycle: u32, - /// deoptimization frequency. + /// Deoptimization frequency. #[arg(long, short = 'F', default_value_t = 0.5)] pub freq: f32, - /// allowed transform routines (ap/li/lp/om/rs). + /// Allowed transform routines (ap/li/lp/om/rs). #[arg(long, default_value_t = String::from("ap,li,lp,om,rs"))] pub transforms: String, - /// allow processing of invalid instructions. + /// Allow processing of invalid instructions. #[arg(long)] pub allow_invalid: bool, - /// verbose output mode. + /// Verbose output mode. #[arg(long, short = 'v')] pub verbose: bool, - /// debug output mode. + /// Debug output mode. #[arg(long)] pub debug: bool, } @@ -106,6 +112,13 @@ pub fn parse_options() -> Result { opts.outfile = opts.source.clone(); } + if !matches!( + opts.syntax.to_uppercase().as_str(), + "KEYSTONE" | "NASM" | "MASM" | "INTEL" | "GAX" + ) { + return Err(ArgParseError::InvalidSyntax); + } + if opts.outfile.is_empty() { opts.outfile = format!("{}_deopt.bin", opts.file); } @@ -119,14 +132,14 @@ pub fn parse_options() -> Result { Ok(opts) } -pub fn parse_offset(offsets: &str) -> Result<(u32, u32), ArgParseError> { +pub fn parse_offset(offsets: &str) -> Result<(u64, u64), ArgParseError> { if offsets.matches('-').count() != 1 { return Err(ArgParseError::InvalidOffsetValues); } - let mut off: Vec = Vec::new(); + let mut off: Vec = Vec::new(); for part in offsets.split('-') { if part.starts_with("0x") { - off.push(u32::from_str_radix(part.trim_start_matches("0x"), 16)?) + off.push(u64::from_str_radix(part.trim_start_matches("0x"), 16)?) } else { off.push(part.parse()?) } @@ -147,6 +160,7 @@ pub fn print_summary(opts: &Options) { } let freq_str = format!("%{:.4}", opts.freq * 100.0); + let trace_str = format!("{:?}", opts.trace); println!( "\n[ {} {} {} ]", "#".repeat(wspace / 2 + 2).yellow().bold(), @@ -201,5 +215,11 @@ pub fn print_summary(opts: &Options) { opts.transforms, " ".repeat(wspace - opts.transforms.len()) ); + println!( + "[ {} {}{}]", + "Auto Trace: ".blue().bold(), + opts.trace, + " ".repeat(wspace - trace_str.len()) + ); println!(); } diff --git a/src/x86_64/deoptimizer.rs b/src/x86_64/deoptimizer.rs index 1e3554a..7425b0c 100644 --- a/src/x86_64/deoptimizer.rs +++ b/src/x86_64/deoptimizer.rs @@ -2,10 +2,11 @@ use crate::x86_64::*; use bitflags::bitflags; use iced_x86::code_asm::*; use iced_x86::*; -use log::{error, info, trace, warn}; +use log::{debug, error, info, trace, warn}; use rand::Rng; use std::collections::HashMap; use thiserror::Error; +use tracer::*; #[derive(Error, Debug)] pub enum DeoptimizerError { @@ -21,8 +22,6 @@ pub enum DeoptimizerError { // UnexpectedMemorySize, // #[error("Offset skipping failed!")] // OffsetSkipFail, - #[error("Invalid formatter syntax.")] - InvalidSyntax, #[error("Invalid processor mode(bitness). (16/32/64 accepted)")] InvalidProcessorMode, #[error("All available instruction transform gadgets failed.")] @@ -47,16 +46,18 @@ pub enum DeoptimizerError { TransposeFailed, #[error("Invalid transform gadget.")] InvalidTransformGadget, + #[error("Tracer Error: {0}")] + TracerError(#[from] TracerError), #[error("Instruction encoding failed: {0}")] EncodingFail(#[from] IcedError), } -enum AssemblySyntax { - Keystone, - Nasm, - Masm, - Intel, - Gas, +#[allow(dead_code)] +pub enum FileType { + Pe, + Elf, + Coff, + Shellcode, } bitflags! { @@ -78,26 +79,38 @@ impl AvailableTransforms { } } +#[allow(dead_code)] pub struct AnalyzedCode { - bitness: u32, bytes: Vec, + bitness: u32, start_addr: u64, - code: Vec, + file_type: FileType, + instructions: Vec, known_addr_table: Vec, branch_targets: Vec, - // addr_map: HashMap, + trace_results: Option, } impl AnalyzedCode { - fn is_known_address(&self, addr: u64) -> bool { + // pub fn get_bytes(&self) -> Vec { + // self.bytes + // } + // pub fn get_instructions(&self) -> Vec { + // self.instructions + // } + // pub fn get_bitness(&self) -> u32 { + // self.bitness + // } + // pub fn get_start_address(&self) -> u64 { + // self.start_addr + // } + pub fn is_known_address(&self, addr: u64) -> bool { self.known_addr_table.contains(&addr) } - fn is_branch_target(&self, addr: u64) -> bool { + pub fn is_branch_target(&self, addr: u64) -> bool { self.branch_targets.contains(&addr) } - // fn get_random_cfe_addr(&self) -> Option { - // self.cfe_addr_table.choose(&mut rand::thread_rng()).copied() - // } + // pub fn is_affecting_cf(&self, inst: &Instruction) -> Result { // // Check if the instruction exists in self.code // if self.addr_map.get(&inst.ip()).is_none() { @@ -135,9 +148,9 @@ pub struct Deoptimizer { pub transforms: AvailableTransforms, /// Allow processing of invalid instructions. pub allow_invalid: bool, - /// Disassembler syntax. - syntax: AssemblySyntax, - skipped_offsets: Option>, + /// Trace the control flow of the given binary + pub trace: bool, + skipped_offsets: HashMap, } impl Deoptimizer { @@ -145,10 +158,10 @@ impl Deoptimizer { Self { cycle: 1, freq: 0.5, + trace: false, allow_invalid: false, transforms: AvailableTransforms::All, - syntax: AssemblySyntax::Nasm, - skipped_offsets: None, + skipped_offsets: HashMap::new(), } } @@ -169,32 +182,13 @@ impl Deoptimizer { Ok(()) } - pub fn set_syntax(&mut self, syntax: String) -> Result<(), DeoptimizerError> { - match syntax.to_lowercase().as_str() { - "keystone" => self.syntax = AssemblySyntax::Keystone, - "nasm" => self.syntax = AssemblySyntax::Nasm, - "masm" => self.syntax = AssemblySyntax::Masm, - "intel" => self.syntax = AssemblySyntax::Intel, - "gas" => self.syntax = AssemblySyntax::Gas, - _ => return Err(DeoptimizerError::InvalidSyntax), - } - Ok(()) - } - - pub fn set_skipped_offsets(&mut self, skipped: Vec<(u32, u32)>) { - self.skipped_offsets = Some(skipped); - } - - fn is_offset_skipped(&self, offset: u32) -> bool { - if self.skipped_offsets.is_none() { - return false; - } - for (o1, o2) in self.skipped_offsets.clone().unwrap() { - if offset >= o1 && offset <= o2 { - return true; + pub fn set_skipped_offsets(&mut self, skipped: Vec<(u64, u64)>) { + for range in skipped { + let (o1, o2) = range; + for o in o1..o2 { + self.skipped_offsets.insert(o, true); } } - false } fn replace_skipped_offsets( @@ -202,13 +196,13 @@ impl Deoptimizer { bytes: &[u8], fill: u8, ) -> Result, DeoptimizerError> { - if self.skipped_offsets.is_none() { + if self.skipped_offsets.is_empty() { return Ok(bytes.to_vec()); } let mut replaced_bytes = Vec::new(); trace!("Replacing skipped offsets with NOPs..."); for (i, b) in bytes.iter().enumerate() { - if self.is_offset_skipped(i as u32) { + if self.skipped_offsets.contains_key(&(i as u64)) { replaced_bytes.push(fill); continue; } @@ -229,11 +223,30 @@ impl Deoptimizer { bitness ); - // let trace_results = tracer::trace(bytes, bitness, start_addr)?; + // Determine the file type here... + if bytes.len() > 1000 && self.trace { + warn!("Given shellcode seems to be too large for effective tracing.") + } + + let mut trace_results = None; + if self.trace { + info!("Tracing the execution control flow..."); + let tr = tracer::trace(bytes, bitness, start_addr)?; + info!("Done tracing!"); + info!("Found {} possible strings.", tr.possible_strings.len()); + debug!("Total coverage: {}", tr.total_coverage); + debug!("Coverage without strings: {}", tr.coverage_whitout_strings); + for o in 0..bytes.len() { + if !tr.active_offsets.contains(&(o as u64 + start_addr)) { + self.skipped_offsets.insert(o as u64 + start_addr, true); + } + } + trace_results = Some(tr); + } let mut decoder = Decoder::with_ip(bitness, bytes, start_addr, DecoderOptions::NONE); let replaced_bytes: Vec; - if self.skipped_offsets.is_some() { + if !self.skipped_offsets.is_empty() { replaced_bytes = self.replace_skipped_offsets(bytes, 0x90)?; decoder = Decoder::with_ip(bitness, &replaced_bytes, start_addr, DecoderOptions::NONE); } @@ -241,22 +254,23 @@ impl Deoptimizer { let mut inst = Instruction::default(); let mut known_addr_table = Vec::new(); let mut branch_targets = Vec::new(); - let mut code = Vec::new(); + let mut instructions = Vec::new(); let mut addr_map: HashMap = HashMap::new(); let mut offset = 0; while decoder.can_decode() { decoder.decode_out(&mut inst); - if self.is_offset_skipped(offset) { + if self.skipped_offsets.contains_key(&offset) { let mut db = Instruction::with_declare_byte_1(bytes[offset as usize]); db.set_ip(inst.ip()); db.set_code(Code::DeclareByte); // Push to known address table known_addr_table.push(db.ip()); addr_map.insert(db.ip(), db); - code.push(db); + instructions.push(db); offset += 1; continue; } + if inst.is_invalid() { warn!("Found invalid instruction at: 0x{:016X}", inst.ip()); if !self.allow_invalid { @@ -266,21 +280,13 @@ impl Deoptimizer { // Push to known address table known_addr_table.push(inst.ip()); addr_map.insert(inst.ip(), inst); - code.push(inst); + instructions.push(inst); let bt = get_branch_target(&inst).unwrap_or(0); if bt != 0 { branch_targets.push(bt); } - - // // Push to control flow exit address table if it is a JMP of RET - // if inst.mnemonic() == Mnemonic::Ret - // || inst.mnemonic() == Mnemonic::Retf - // || inst.mnemonic() == Mnemonic::Jmp - // { - // cfe_addr_table.push(inst.ip()) - // } - offset += inst.len() as u32; + offset += inst.len() as u64; } for bt in branch_targets.iter() { @@ -296,92 +302,14 @@ impl Deoptimizer { bitness, bytes: bytes.to_vec(), start_addr, - code, + file_type: FileType::Shellcode, + instructions, known_addr_table, branch_targets, - // addr_map, + trace_results, }) } - pub fn format_instruction(&self, inst: &Instruction) -> String { - let mut result = String::new(); - match self.syntax { - AssemblySyntax::Keystone => { - let mut formatter = IntelFormatter::new(); - formatter.options_mut().set_uppercase_keywords(false); - formatter - .options_mut() - .set_memory_size_options(iced_x86::MemorySizeOptions::Always); - formatter.options_mut().set_hex_prefix("0x"); - formatter.options_mut().set_hex_suffix(""); - formatter.format(inst, &mut result); - } - AssemblySyntax::Nasm => { - let mut formatter = NasmFormatter::new(); - formatter.format(inst, &mut result); - } - AssemblySyntax::Masm => { - let mut formatter = MasmFormatter::new(); - formatter.format(inst, &mut result); - } - AssemblySyntax::Intel => { - let mut formatter = IntelFormatter::new(); - formatter.format(inst, &mut result); - } - AssemblySyntax::Gas => { - let mut formatter = GasFormatter::new(); - formatter.format(inst, &mut result); - } - }; - result - } - - pub fn disassemble( - &mut self, - bitness: u32, - start_addr: u64, - bytes: Vec, - ) -> Result { - info!( - "Disassembling at -> 0x{:016X} (mode={})", - start_addr, bitness - ); - let acode = self.analyze(bytes.as_slice(), bitness, start_addr)?; - let mut result = String::new(); - let mut decoder = Decoder::new(acode.bitness, bytes.as_slice(), DecoderOptions::NONE); - let mut inst = Instruction::default(); - while decoder.can_decode() { - decoder.decode_out(&mut inst); - if inst.is_invalid() { - warn!( - "Inlining invalid instruction bytes at: 0x{:016X}", - inst.ip() - ); - let start_index = (inst.ip() - acode.start_addr) as usize; - let instr_bytes = &acode.bytes[start_index..start_index + inst.len()]; - result += &format!("loc_{:016X}: {}\n", inst.ip(), to_db_mnemonic(instr_bytes)); - continue; - } - let temp = self.format_instruction(&inst); - let nbt = inst.near_branch_target(); - if nbt != 0 { - if acode.is_known_address(nbt) { - result += &format!( - "loc_{:016X}: {} {}\n", - inst.ip(), - temp.split(' ').next().unwrap(), - &format!("loc_{:016X}", nbt) - ); - continue; - } else { - warn!("Misaligned instruction detected at {}", inst.ip()) - } - } - result += &format!("loc_{:016X}: {}\n", inst.ip(), temp); - } - Ok(result) - } - pub fn apply_transform( bitness: u32, inst: &Instruction, @@ -455,7 +383,7 @@ impl Deoptimizer { let mut ip_to_index_table: HashMap = HashMap::new(); let mut index_to_index_table: HashMap = HashMap::new(); // let mut new_ip: u64 = acode.start_addr; - for inst in acode.code.clone() { + for inst in acode.instructions.clone() { if acode.is_branch_target(inst.ip()) { ip_to_index_table.insert(inst.ip(), result.len()); } diff --git a/src/x86_64/disassembler.rs b/src/x86_64/disassembler.rs new file mode 100644 index 0000000..ae7d8d2 --- /dev/null +++ b/src/x86_64/disassembler.rs @@ -0,0 +1,82 @@ +use crate::x86_64::*; +use iced_x86::*; +use log::{error, info, warn}; + +pub fn format_instruction(syntax: String, inst: &Instruction) -> String { + let mut result = String::new(); + match syntax.to_uppercase().as_str() { + "KEYSTONE" => { + let mut formatter = IntelFormatter::new(); + formatter.options_mut().set_uppercase_keywords(false); + formatter + .options_mut() + .set_memory_size_options(iced_x86::MemorySizeOptions::Always); + formatter.options_mut().set_hex_prefix("0x"); + formatter.options_mut().set_hex_suffix(""); + formatter.format(inst, &mut result); + } + "NASM" => { + let mut formatter = NasmFormatter::new(); + formatter.format(inst, &mut result); + } + "MASM" => { + let mut formatter = MasmFormatter::new(); + formatter.format(inst, &mut result); + } + "INTEL" => { + let mut formatter = IntelFormatter::new(); + formatter.format(inst, &mut result); + } + "GAS" => { + let mut formatter = GasFormatter::new(); + formatter.format(inst, &mut result); + } + _ => { + error!("Unknown disassembler syntax: {}", syntax); + panic!("Instruction formatting failed!") + } + }; + result +} + +pub fn disassemble( + bytes: &[u8], + bitness: u32, + start_addr: u64, + syntax: String, +) -> Result { + info!( + "Disassembling at -> 0x{:016X} (mode={})", + start_addr, bitness + ); + + let mut result = String::new(); + let mut decoder = Decoder::with_ip(bitness, bytes, start_addr, DecoderOptions::NONE); + let mut inst = Instruction::default(); + while decoder.can_decode() { + decoder.decode_out(&mut inst); + if inst.is_invalid() { + warn!( + "Inlining invalid instruction bytes at: 0x{:016X}", + inst.ip() + ); + let start_index = (inst.ip() - start_addr) as usize; + let instr_bytes = &bytes[start_index..start_index + inst.len()]; + result += &format!("loc_{:016X}: {}\n", inst.ip(), to_db_mnemonic(instr_bytes)); + continue; + } + let temp = format_instruction(syntax.clone(), &inst); + let nbt = inst.near_branch_target(); + if nbt != 0 { + result += &format!( + "loc_{:016X}: {} {}\n", + inst.ip(), + temp.split(' ').next().unwrap(), + &format!("loc_{:016X}", nbt) + ); + continue; + } + result += &format!("loc_{:016X}: {}\n", inst.ip(), temp); + } + Ok(result) +} diff --git a/src/x86_64/mod.rs b/src/x86_64/mod.rs index f25b001..cbe93c5 100644 --- a/src/x86_64/mod.rs +++ b/src/x86_64/mod.rs @@ -1,4 +1,5 @@ mod deoptimizer; +pub mod disassembler; mod helpers; mod tests; mod tracer; diff --git a/src/x86_64/tests.rs b/src/x86_64/tests.rs index 3c01c3f..fa0e65c 100644 --- a/src/x86_64/tests.rs +++ b/src/x86_64/tests.rs @@ -529,7 +529,7 @@ mod tests { } #[test] - fn test_trace_exec_flow() { + fn test_trace() { let code_64: &[u8] = &[ 0xfc, 0x48, 0x83, 0xe4, 0xf0, 0xe8, 0xcc, 0x00, 0x00, 0x00, 0x41, 0x51, 0x41, 0x50, 0x52, 0x48, 0x31, 0xd2, 0x65, 0x48, 0x8b, 0x52, 0x60, 0x51, 0x48, 0x8b, 0x52, 0x18, diff --git a/src/x86_64/tracer.rs b/src/x86_64/tracer.rs index c60cc44..c110420 100644 --- a/src/x86_64/tracer.rs +++ b/src/x86_64/tracer.rs @@ -1,51 +1,15 @@ use crate::x86_64::*; use iced_x86::code_asm::*; use iced_x86::*; -use log::{error, info, trace, warn}; +use log::{error, trace}; use regex::bytes::Regex; use std::collections::HashMap; use thiserror::Error; #[derive(Error, Debug)] pub enum TracerError { - // #[error("Instruction with unexpected operand count.")] - // UnexpectedOperandCount, - // #[error("Given instruction not found in code map.")] - // InstructionNotFound, - // #[error("Code analysis results are not found.")] - // MissingCodeAnalysis, - // #[error("Near branch value too large.")] - // NearBranchTooBig, - // #[error("Unexpected memory size given.")] - // UnexpectedMemorySize, - // #[error("Offset skipping failed!")] - // OffsetSkipFail, - #[error("Unknown stack operation.")] - UnknownStackOp, - #[error("Invalid formatter syntax.")] - InvalidSyntax, - #[error("Invalid processor mode(bitness). (16/32/64 accepted)")] - InvalidProcessorMode, - #[error("All available instruction transform gadgets failed.")] - AllTransformsFailed, - #[error("Far branch value too large.")] - FarBranchTooBig, - #[error("Found invalid instruction.")] - InvalidInstruction, - #[error("Branch target not found.")] - BracnhTargetNotFound, - #[error("This transform not possible for given instruction.")] - TransformNotPossible, - #[error("Unexpected register size given.")] - UnexpectedRegisterSize, #[error("Unexpected operand type encountered.")] UnexpectedOperandType, - #[error("No GP register found with given parameters.")] - RegisterNotFound, - #[error("Instruction transpose attempt failed.")] - TransposeFailed, - #[error("Invalid transform gadget.")] - InvalidTransformGadget, #[error("Instruction encoding failed: {0}")] EncodingFail(#[from] IcedError), } @@ -81,29 +45,29 @@ pub struct TraceResults { pub coverage_whitout_strings: f64, } -impl TraceResults { - pub fn print_dead_code(&self) { - let mut last = 0; - for (i, b) in self.bytes.iter().enumerate() { - if self.active_offsets.contains(&(i as u64)) { - continue; - } - if i - last != 1 { - print!("\n0x{:016X}:\t", i); - } - - if *b >= 0x20 && *b <= 0x7E { - print!("{}", String::from_utf8_lossy(&[*b])); - } else if *b == 0x00 { - continue; - } else { - print!("\\x{:X}", b); - } - last = i - } - println!("\n"); - } -} +// impl TraceResults { +// pub fn print_dead_code(&self) { +// let mut last = 0; +// for (i, b) in self.bytes.iter().enumerate() { +// if self.active_offsets.contains(&(i as u64)) { +// continue; +// } +// if i - last != 1 { +// print!("\n0x{:016X}:\t", i); +// } +// +// if *b >= 0x20 && *b <= 0x7E { +// print!("{}", String::from_utf8_lossy(&[*b])); +// } else if *b == 0x00 { +// continue; +// } else { +// print!("\\x{:X}", b); +// } +// last = i +// } +// println!("\n"); +// } +// } impl Tracer { fn new(bytes: &[u8], bitness: u32) -> Self { @@ -121,7 +85,7 @@ impl Tracer { pso.push(o as u64); } } - info!("Found {} possible strings.", ps.len()); + // info!("Found {} possible strings.", ps.len()); Self { bytes: bytes.to_vec(), bitness, @@ -141,7 +105,7 @@ impl Tracer { } fn trace_code_paths(&mut self, start_offset: u64) -> Result { - info!("[TRACER] Tracing -> 0x{:016X}", start_offset); + trace!("[TRACER] Tracing -> 0x{:016X}", start_offset); let mut ip = start_offset; loop { if ip >= self.bytes.len() as u64 { @@ -293,32 +257,9 @@ fn is_conditional_branch(inst: Instruction) -> bool { || inst.is_loopcc() } -// NO_BRANCH -// - Check if the stack is not empty -// - Check if current instruction is POP -// - Save poped address in current context with register -// - Pop the value from stack -// - Add to cf_addr_map -// - Add to active_offsets -// - Advance to next_ip -// CONDITIONAL_BRANCH -// - Step-in -// - Advance to next_ip -// ADDRESS_CALL -// - Push next_ip to stack -// - Step-in -// - Continue if HaltResason is Return -// REGISTER_CALL -// - Check if current context contains the register value -// - Step-in -// - Halt->DynamicBranch -// - pub fn trace(bytes: &[u8], bitness: u32, start_addr: u64) -> Result { let mut tracer = Tracer::new(bytes, bitness); - info!("Tracing execution flow and possible code paths..."); tracer.trace_code_paths(start_addr)?; - info!("Done tracing!"); tracer.active_offsets.sort_unstable(); tracer.active_offsets.dedup();