use crate::function::DecodedInsn;
use crate::{
DisassemblyResult, FunctionAnalysisState, Result, error::Error,
function_candidate::FunctionCandidate,
};
use iced_x86::{Decoder, DecoderOptions, Mnemonic};
use itertools::Itertools;
use regex::bytes::Regex as BytesRegex;
use std::sync::LazyLock;
use std::{collections::BTreeMap, collections::HashMap, convert::TryInto};
static DEFAULT_PROLOGUES: LazyLock<Vec<BytesRegex>> = LazyLock::new(|| {
vec![
BytesRegex::new(r"(?-u)\x8B\xFF\x55\x8B\xEC").unwrap(),
BytesRegex::new(r"(?-u)\x89\xFF\x55\x8B\xEC").unwrap(),
BytesRegex::new(r"(?-u)\x55\x8B\xEC").unwrap(),
BytesRegex::new(r"(?-u)\x55\x89\xE5").unwrap(),
BytesRegex::new(r"(?-u)\xF3\x0F\x1E\xFA").unwrap(), BytesRegex::new(r"(?-u)\xF3\x0F\x1E\xFB").unwrap(), BytesRegex::new(r"(?-u)\x48\x89\x5C\x24[\S\s]").unwrap(), BytesRegex::new(r"(?-u)\x48\x83\xEC[\S\s]").unwrap(), BytesRegex::new(r"(?-u)\x41\x57\x41\x56").unwrap(), BytesRegex::new(r"(?-u)\x55\x48\x89\xE5").unwrap(), BytesRegex::new(r"(?-u)\x48\x81\xEC[\S\s]{4}").unwrap(), BytesRegex::new(r"(?-u)\x48\x89\x6C\x24[\S\s]").unwrap(), BytesRegex::new(r"(?-u)\x41\x56\x53").unwrap(), BytesRegex::new(r"(?-u)\x41\x55\x41\x54").unwrap(), BytesRegex::new(r"(?-u)\x53\x48\x83\xEC").unwrap(), ]
});
static REF_CANDIDATE: LazyLock<BytesRegex> =
LazyLock::new(|| BytesRegex::new(r"(?-u)\xE8").unwrap());
static BITNESS: LazyLock<BytesRegex> = LazyLock::new(|| BytesRegex::new(r"(?-u)\xFF\x25").unwrap());
static STUB_CHAIN: LazyLock<BytesRegex> =
LazyLock::new(|| BytesRegex::new(r"(?-u)(?P<block>(\xFF\x25[\S\s]{4}){2,})").unwrap());
static STUB_CHAIN_FUNC: LazyLock<BytesRegex> =
LazyLock::new(|| BytesRegex::new(r"(?-u)\xFF\x25(?P<function>[\S\s]{4})").unwrap());
static STUB_CHAIN_BLOCK: LazyLock<BytesRegex> = LazyLock::new(|| {
BytesRegex::new(r"(?-u)(?P<block>(\xFF\x25[\S\s]{4}\x68[\S\s]{4}\xE9[\S\s]{4}){2,})").unwrap()
});
static STUB_CHAIN_BLOCK_FUNC: LazyLock<BytesRegex> =
LazyLock::new(|| BytesRegex::new(r"(?-u)\xFF\x25(?P<function>[\S\s]{4})").unwrap());
static CELL_MATCH: LazyLock<BytesRegex> =
LazyLock::new(|| BytesRegex::new(r"(?-u)\xFF\x15").unwrap());
#[derive(Debug)]
struct GapSequences {
gs: HashMap<usize, Vec<Vec<u8>>>,
}
impl GapSequences {
pub fn new() -> GapSequences {
let mut gs = GapSequences { gs: HashMap::new() };
gs.gs.insert(
1,
vec![
b"\x90".to_vec(), b"\xCC".to_vec(), b"\x00".to_vec(), ],
);
gs.gs.insert(
2,
vec![
b"\x66\x90".to_vec(), b"\x8b\xc0".to_vec(),
b"\x8b\xff".to_vec(), b"\x8d\x00".to_vec(), b"\x86\xc0".to_vec(), b"\x66\x2e".to_vec(), b"\x89\xf6".to_vec(), ],
);
gs.gs.insert(
3,
vec![
b"\x0f\x1f\x00".to_vec(), b"\x8d\x40\x00".to_vec(), b"\x8d\x00\x00".to_vec(), b"\x8d\x49\x00".to_vec(), b"\x8d\x64\x24".to_vec(), b"\x8d\x76\x00".to_vec(),
b"\x66\x66\x90".to_vec(),
],
);
gs.gs.insert(
4,
vec![
b"\x0f\x1f\x40\x00".to_vec(), b"\x8d\x74\x26\x00".to_vec(),
b"\x66\x66\x66\x90".to_vec(),
],
);
gs.gs.insert(
5,
vec![
b"\x0f\x1f\x44\x00\x00".to_vec(), b"\x90\x8d\x74\x26\x00".to_vec(),
],
);
gs.gs.insert(
6,
vec![
b"\x66\x0f\x1f\x44\x00\x00".to_vec(), b"\x8d\xb6\x00\x00\x00\x00".to_vec(),
],
);
gs.gs.insert(
7,
vec![
b"\x0f\x1f\x80\x00\x00\x00\x00".to_vec(), b"\x8d\xb4\x26\x00\x00\x00\x00".to_vec(),
b"\x8D\xBC\x27\x00\x00\x00\x00".to_vec(),
],
);
gs.gs.insert(
8,
vec![
b"\x0f\x1f\x84\x00\x00\x00\x00\x00".to_vec(), b"\x90\x8d\xb4\x26\x00\x00\x00\x00".to_vec(),
],
);
gs.gs.insert(
9,
vec![
b"\x66\x0f\x1f\x84\x00\x00\x00\x00\x00".to_vec(), b"\x89\xf6\x8d\xbc\x27\x00\x00\x00\x00".to_vec(),
],
);
gs.gs.insert(
10,
vec![
b"\x66\x66\x0f\x1f\x84\x00\x00\x00\x00\x00".to_vec(), b"\x8d\x76\x00\x8d\xbc\x27\x00\x00\x00\x00".to_vec(),
b"\x66\x2e\x0f\x1f\x84\x00\x00\x00\x00\x00".to_vec(),
],
);
gs.gs.insert(
11,
vec![
b"\x66\x66\x66\x0f\x1f\x84\x00\x00\x00\x00\x00".to_vec(), b"\x8d\x74\x26\x00\x8d\xbc\x27\x00\x00\x00\x00".to_vec(),
b"\x66\x66\x2e\x0f\x1f\x84\x00\x00\x00\x00\x00".to_vec(),
],
);
gs.gs.insert(
12,
vec![
b"\x8d\xb6\x00\x00\x00\x00\x8d\xbf\x00\x00\x00\x00".to_vec(),
b"\x66\x66\x66\x2e\x0f\x1f\x84\x00\x00\x00\x00\x00".to_vec(),
],
);
gs.gs.insert(
13,
vec![
b"\x8d\xb6\x00\x00\x00\x00\x8d\xbc\x27\x00\x00\x00\x00".to_vec(),
b"\x66\x66\x66\x66\x2e\x0f\x1f\x84\x00\x00\x00\x00\x00".to_vec(),
],
);
gs.gs.insert(
14,
vec![
b"\x8d\xb4\x26\x00\x00\x00\x00\x8d\xbc\x27\x00\x00\x00\x00".to_vec(),
b"\x66\x66\x66\x66\x66\x2e\x0f\x1f\x84\x00\x00\x00\x00\x00".to_vec(),
],
);
gs.gs.insert(
15,
vec![b"\x66\x66\x66\x66\x66\x66\x2e\x0f\x1f\x84\x00\x00\x00\x00\x00".to_vec()],
);
gs
}
}
#[derive(Debug)]
pub struct FunctionCandidateManager {
pub bitness: u32,
identified_alignment: u32,
code_areas: Vec<(u64, u64)>,
all_call_refs: HashMap<u64, u64>,
pub symbol_addresses: Vec<u64>,
pub candidates: BTreeMap<u64, FunctionCandidate>,
candidate_offsets: Vec<u64>,
gs: GapSequences,
candidate_queue: Vec<u64>,
gap_pointer: u64,
previously_analyzed_gap: u64,
function_gaps: Vec<(u64, u64, u64)>,
}
impl FunctionCandidateManager {
pub fn new() -> FunctionCandidateManager {
FunctionCandidateManager {
bitness: 0,
identified_alignment: 0,
code_areas: vec![],
all_call_refs: HashMap::new(),
symbol_addresses: vec![],
candidates: BTreeMap::<u64, FunctionCandidate>::new(),
candidate_offsets: vec![],
gs: GapSequences::new(),
candidate_queue: vec![],
gap_pointer: 0,
previously_analyzed_gap: 0,
function_gaps: vec![],
}
}
pub fn init(&mut self, disassembly: &DisassemblyResult) -> Result<()> {
self.bitness = disassembly.binary_info.bitness;
self.identified_alignment = 0;
self.code_areas = disassembly.binary_info.code_areas.clone();
self.locate_candidates(disassembly)?;
Ok(())
}
fn locate_candidates(&mut self, disassembly: &DisassemblyResult) -> Result<()> {
self.locate_symbol_candidates(disassembly)?;
self.locate_reference_candidates(disassembly)?;
self.locate_prologue_candidates(disassembly)?;
self.locate_stub_chain_candidates(disassembly)?;
self.locate_exception_handler_candidates(disassembly)?;
self.identified_alignment = self.identify_alignment()?;
Ok(())
}
fn locate_symbol_candidates(&mut self, disassembly: &DisassemblyResult) -> Result<()> {
let s = self.symbol_addresses.clone();
for symbol_addr in s {
self.add_symbol_candidate(&symbol_addr, disassembly)?;
}
Ok(())
}
fn add_symbol_candidate(
&mut self,
addr: &u64,
disassembly: &DisassemblyResult,
) -> Result<bool> {
if !self.passes_code_filter(Some(*addr))? {
return Ok(false);
}
self.ensure_candidate(*addr, disassembly)?;
if let Some(s) = self.candidates.get_mut(addr) {
s.set_is_symbol(true)?;
s.set_initial_candidate();
}
Ok(true)
}
fn identify_alignment(&self) -> Result<u32> {
let mut identified_alignment = 0;
let mut num_candidates = 0;
for candidate in self.candidates.values() {
if candidate.call_ref_sources.len() > 1 {
num_candidates += 1;
}
}
let mut num_aligned_16_candidates = 0;
for candidate in self.candidates.values() {
if candidate.call_ref_sources.len() > 1 && candidate.alignment == 16 {
num_aligned_16_candidates += 1;
}
}
let mut num_aligned_4_candidates = 0;
for candidate in self.candidates.values() {
if candidate.call_ref_sources.len() > 1 && candidate.alignment == 4 {
num_aligned_4_candidates += 1;
}
}
if num_candidates > 0 {
let alignment_16_ratio = 1.0 * num_aligned_16_candidates as f32 / num_candidates as f32;
let alignment_4_ratio = 1.0 * num_aligned_4_candidates as f32 / num_candidates as f32;
if num_candidates > 20 && alignment_4_ratio > 0.95 {
identified_alignment = 4;
}
if num_candidates > 20 && alignment_16_ratio > 0.95 {
identified_alignment = 16;
}
}
Ok(identified_alignment)
}
fn locate_exception_handler_candidates(
&mut self,
disassembly: &DisassemblyResult,
) -> Result<()> {
if self.bitness != 64 {
return Ok(());
}
let base_addr = disassembly.binary_info.base_addr;
let bi = &disassembly.binary_info;
const MAX_FUNC_SPAN: u64 = 16 * 1024 * 1024; for (section_name, section_va_start, section_va_end) in bi.get_sections()? {
if section_name != ".pdata" {
continue;
}
let mut va = section_va_start;
while va.checked_add(12).is_some_and(|e| e <= section_va_end) {
let Ok(packed) = bi.bytes_at(va, 12) else {
break;
};
let begin_rva =
u32::from_le_bytes(packed[0..4].try_into().unwrap_or([0; 4])) as u64;
let end_rva = u32::from_le_bytes(packed[4..8].try_into().unwrap_or([0; 4])) as u64;
let unwind_rva =
u32::from_le_bytes(packed[8..12].try_into().unwrap_or([0; 4])) as u64;
let Some(next) = va.checked_add(12) else {
break;
};
va = next;
if begin_rva == 0 {
continue;
}
if end_rva <= begin_rva || (end_rva - begin_rva) > MAX_FUNC_SPAN {
continue;
}
let Some(unwind_va) = base_addr.checked_add(unwind_rva) else {
continue;
};
let Ok(unwind_hdr) = bi.bytes_at(unwind_va, 4) else {
continue;
};
let version = unwind_hdr[0] & 0x07;
if version != 1 && version != 2 {
continue;
}
let count_of_codes = unwind_hdr[2] as u32;
let unwind_total = 4u32.saturating_add(count_of_codes.saturating_mul(2));
if bi.bytes_at(unwind_va, unwind_total).is_err() {
continue;
}
if let Some(addr) = base_addr.checked_add(begin_rva) {
self.add_exception_candidate(addr, disassembly)?;
}
}
}
Ok(())
}
fn locate_stub_chain_candidates(&mut self, disassembly: &DisassemblyResult) -> Result<()> {
let base_addr = disassembly.binary_info.base_addr;
for (section_va, section_bytes) in disassembly.binary_info.section_slices() {
for block in STUB_CHAIN.find_iter(section_bytes) {
for call_match in
STUB_CHAIN_FUNC.find_iter(§ion_bytes[block.start()..block.end()])
{
let stub_addr = section_va + block.start() as u64 + call_match.start() as u64;
if !self.passes_code_filter(Some(stub_addr))? {
continue;
}
if self.add_prologue_candidate(stub_addr & self.get_bitmask(), disassembly)? {
self.set_initial_candidate(stub_addr & self.get_bitmask())?;
self.candidates
.get_mut(&stub_addr)
.ok_or(Error::LogicError(file!(), line!()))?
.set_is_stub();
}
}
}
for block in STUB_CHAIN_BLOCK.find_iter(section_bytes) {
for call_match in
STUB_CHAIN_BLOCK_FUNC.find_iter(§ion_bytes[block.start()..block.end()])
{
let stub_addr = section_va + block.start() as u64 + call_match.start() as u64;
if !self.passes_code_filter(Some(stub_addr))? {
continue;
}
if self.add_prologue_candidate(stub_addr & self.get_bitmask(), disassembly)? {
self.set_initial_candidate(stub_addr & self.get_bitmask())?;
self.candidates
.get_mut(&stub_addr)
.ok_or(Error::LogicError(file!(), line!()))?
.set_is_stub();
}
}
}
}
let _ = base_addr; Ok(())
}
fn locate_prologue_candidates(&mut self, disassembly: &DisassemblyResult) -> Result<()> {
for re_prologue in DEFAULT_PROLOGUES.iter() {
for (section_va, section_bytes) in disassembly.binary_info.section_slices() {
for prologue_match in re_prologue.find_iter(section_bytes) {
let va = section_va + prologue_match.start() as u64;
if !self.passes_code_filter(Some(va))? {
continue;
}
self.add_prologue_candidate(va & self.get_bitmask(), disassembly)?;
self.set_initial_candidate(va & self.get_bitmask())?;
}
}
}
Ok(())
}
fn locate_reference_candidates(&mut self, disassembly: &DisassemblyResult) -> Result<()> {
let base_addr = disassembly.binary_info.base_addr;
for (section_va, section_bytes) in disassembly.binary_info.section_slices() {
for call_match in REF_CANDIDATE.find_iter(section_bytes) {
let va = section_va + call_match.start() as u64;
if !self.passes_code_filter(Some(va))? {
continue;
}
if section_bytes.len() - call_match.start() > 5 {
let packed_call: &[u8; 4] = §ion_bytes
[call_match.start() + 1..call_match.start() + 5]
.try_into()?;
let rel_call_offset = i32::from_le_bytes(*packed_call) as i64;
if rel_call_offset == 0 {
continue;
}
let call_destination =
((va as i64 + rel_call_offset + 5) & self.get_bitmask() as i64) as u64;
if disassembly.is_addr_within_memory_image(call_destination)?
&& self.add_reference_candidate(call_destination, va, disassembly)?
{
self.set_initial_candidate(call_destination)?;
}
}
}
}
if self.bitness == 32 {
for (section_va, section_bytes) in disassembly.binary_info.section_slices() {
for call_match in BITNESS.find_iter(section_bytes) {
let va = section_va + call_match.start() as u64;
let Some(rel) = va.checked_sub(base_addr) else {
continue;
};
let function_addr = self.resolve_pointer_reference(rel, disassembly).ok();
if !self.passes_code_filter(function_addr)? {
continue;
}
let function_addr = function_addr.unwrap();
if disassembly.is_addr_within_memory_image(function_addr)?
&& self.add_reference_candidate(function_addr, va, disassembly)?
{
self.set_initial_candidate(function_addr)?;
}
}
}
for (section_va, section_bytes) in disassembly.binary_info.section_slices() {
for call_match in CELL_MATCH.find_iter(section_bytes) {
let va = section_va + call_match.start() as u64;
let Some(rel) = va.checked_sub(base_addr) else {
continue;
};
let function_addr = self.resolve_pointer_reference(rel, disassembly).ok();
if !self.passes_code_filter(function_addr)? {
continue;
}
let function_addr = function_addr.unwrap();
if disassembly.is_addr_within_memory_image(function_addr)?
&& self.add_reference_candidate(function_addr, va, disassembly)?
{
self.set_initial_candidate(function_addr)?;
}
}
}
}
Ok(())
}
fn resolve_pointer_reference(
&self,
offset: u64,
disassembly: &DisassemblyResult,
) -> Result<u64> {
let addr_offset = offset.checked_add(2).ok_or(Error::IntegerOverflow(
"resolve_pointer_reference offset+2",
offset,
2,
))?;
if self.bitness == 32 {
let addr_block: &[u8; 4] = disassembly.get_raw_bytes(addr_offset, 4)?.try_into()?;
let function_pointer = u32::from_le_bytes(*addr_block) as u64;
return disassembly.dereference_dword(function_pointer);
}
if self.bitness == 64 {
let addr_block: &[u8; 4] = disassembly.get_raw_bytes(addr_offset, 4)?.try_into()?;
let mut function_pointer = u32::from_le_bytes(*addr_block) as u64;
let prefix = disassembly.get_raw_bytes(offset, 2)?;
if prefix == b"\xFF\x25" {
function_pointer = function_pointer.wrapping_add(offset).wrapping_add(7);
} else if prefix == b"\xFF\x15" {
function_pointer = function_pointer.wrapping_add(offset).wrapping_add(6);
} else {
return Err(Error::LogicError(file!(), line!()));
}
let absolute = disassembly
.binary_info
.base_addr
.checked_add(function_pointer)
.ok_or(Error::IntegerOverflow(
"resolve_pointer_reference base+ptr",
disassembly.binary_info.base_addr,
function_pointer,
))?;
return Ok(absolute);
}
Err(Error::LogicError(file!(), line!()))
}
fn get_bitmask(&self) -> u64 {
0xFFFFFFFFFFFFFFFF
}
fn passes_code_filter(&self, address: Option<u64>) -> Result<bool> {
match address {
Some(addr) => {
for (start, end) in &self.code_areas {
if *start <= addr && *end > addr {
return Ok(true);
}
}
Ok(false)
}
_ => Ok(false),
}
}
fn ensure_candidate(&mut self, addr: u64, disassembly: &DisassemblyResult) -> Result<bool> {
if let std::collections::btree_map::Entry::Vacant(e) = self.candidates.entry(addr) {
e.insert(FunctionCandidate::new(&disassembly.binary_info, addr)?);
return Ok(true);
}
Ok(true)
}
pub fn add_reference_candidate(
&mut self,
addr: u64,
source_ref: u64,
disassembly: &DisassemblyResult,
) -> Result<bool> {
if !self.passes_code_filter(Some(addr))? {
return Ok(false);
}
if self.ensure_candidate(addr, disassembly)? {
self.all_call_refs.insert(source_ref, addr);
}
self.candidates
.get_mut(&addr)
.ok_or(Error::LogicError(file!(), line!()))?
.add_call_ref(source_ref)?;
Ok(true)
}
fn add_prologue_candidate(
&mut self,
addr: u64,
disassembly: &DisassemblyResult,
) -> Result<bool> {
if !self.passes_code_filter(Some(addr))? {
return Ok(false);
}
self.ensure_candidate(addr, disassembly)?;
Ok(true)
}
fn set_initial_candidate(&mut self, addr: u64) -> Result<()> {
self.candidates
.get_mut(&addr)
.ok_or(Error::LogicError(file!(), line!()))?
.set_initial_candidate();
Ok(())
}
fn add_exception_candidate(
&mut self,
addr: u64,
disassembly: &DisassemblyResult,
) -> Result<bool> {
if !self.passes_code_filter(Some(addr))? {
return Ok(false);
}
self.ensure_candidate(addr, disassembly)?;
self.candidates
.get_mut(&addr)
.ok_or(Error::LogicError(file!(), line!()))?
.set_is_exception_handler();
self.candidates
.get_mut(&addr)
.ok_or(Error::LogicError(file!(), line!()))?
.set_initial_candidate();
Ok(true)
}
pub fn get_queue(&self) -> Result<Vec<u64>> {
let mut res = vec![];
for addr in self.candidates.keys() {
res.push(*addr);
}
Ok(res)
}
pub fn get_function_start_candidates(&self) -> Result<Vec<u64>> {
Ok(self.candidate_offsets.clone())
}
pub fn is_alignment_sequence(
&self,
instruction_sequence: &[DecodedInsn],
disassembly: &DisassemblyResult,
) -> Result<bool> {
let mut is_alignment_sequence = false;
if !instruction_sequence.is_empty() {
let mut current_offset = instruction_sequence[0].offset;
for instruction in instruction_sequence {
let len = instruction.length as usize;
let Ok(bytes) = instruction.bytes_in(&disassembly.binary_info) else {
break;
};
if self
.gs
.gs
.get(&len)
.is_some_and(|set| set.contains(&bytes.to_vec()))
{
current_offset += len as u64;
if current_offset.is_multiple_of(16) {
is_alignment_sequence = true;
break;
}
} else {
break;
}
}
}
Ok(is_alignment_sequence)
}
pub fn is_function_candidate(&self, addr: u64) -> Result<bool> {
Ok(self.candidates.contains_key(&addr))
}
pub fn add_candidate(
&mut self,
addr: u64,
is_gap: bool,
reference_source: Option<u64>,
disassembly: &DisassemblyResult,
) -> Result<()> {
if !self.passes_code_filter(Some(addr))? {
return Err(Error::LogicError(file!(), line!()));
}
self.ensure_candidate(addr, disassembly)?;
self.candidates
.get_mut(&addr)
.ok_or(Error::LogicError(file!(), line!()))?
.set_is_gap_candidate(is_gap)?;
if let Some(reference_source) = reference_source {
self.candidates
.get_mut(&addr)
.ok_or(Error::LogicError(file!(), line!()))?
.add_call_ref(reference_source)?;
}
self.candidate_queue.push(addr);
Ok(())
}
pub fn update_analysis_aborted(&mut self, addr: &u64, reason: &str) -> Result<()> {
if let Some(mm) = self.candidates.get_mut(addr) {
mm.set_analysis_aborted(reason)?;
}
Ok(())
}
pub fn update_analysis_finished(&mut self, addr: &u64) -> Result<()> {
if let Some(mm) = self.candidates.get_mut(addr) {
mm.set_analysis_completed()?;
}
Ok(())
}
pub fn update_candidates(&mut self, state: &FunctionAnalysisState) -> Result<()> {
if let Ok(conflicts) = state.identify_call_conflicts(&self.all_call_refs) {
for (candidate_addr, conflict) in conflicts {
if let Some(c) = self.candidates.get_mut(&candidate_addr) {
c.remove_call_refs(conflict)?;
}
}
}
Ok(())
}
pub fn next_gap_candidate(
&mut self,
start_gap_pointer: Option<u64>,
disassembly: &DisassemblyResult,
) -> Result<u64> {
if let Some(s) = start_gap_pointer {
self.gap_pointer = s;
}
if self.gap_pointer == 0 {
self.init_gap_search(disassembly)?;
}
loop {
if disassembly.binary_info.base_addr + disassembly.binary_info.binary_size
< self.gap_pointer
{
return Err(Error::LogicError(file!(), line!()));
}
let gap_offset = self.gap_pointer - disassembly.binary_info.base_addr;
if gap_offset >= disassembly.binary_info.binary_size {
return Err(Error::LogicError(file!(), line!()));
}
let byte = disassembly.get_raw_byte(gap_offset)?;
if self.gs.gs[&1].contains(&vec![byte]) {
self.gap_pointer += 1;
continue;
}
{
let buf = disassembly.get_raw_bytes(gap_offset, 15)?;
let mut decoder =
Decoder::with_ip(self.bitness, buf, gap_offset, DecoderOptions::NONE);
if decoder.can_decode() {
let insn = decoder.decode();
if !insn.is_invalid() && matches!(insn.mnemonic(), Mnemonic::Nop) {
self.gap_pointer += decoder.position() as u64;
continue;
}
}
let mut found_multi_byte_nop = false;
let max_gap = *self
.gs
.gs
.keys()
.max()
.ok_or(Error::LogicError(file!(), line!()))?
as u32;
for gap_length in (2..=max_gap).rev() {
if self.gs.gs[&(gap_length as usize)].contains(
&disassembly
.get_raw_bytes(gap_offset, gap_length as u64)?
.to_vec(),
) {
self.gap_pointer += gap_length as u64;
found_multi_byte_nop = true;
break;
}
}
if found_multi_byte_nop {
continue;
}
if disassembly.data_map.contains(&self.gap_pointer) {
self.gap_pointer += 1;
continue;
}
if disassembly.code_map.contains_key(&self.gap_pointer) {
self.gap_pointer = self.get_next_gap(false, disassembly)?;
continue;
}
let _start_byte = disassembly.get_raw_byte(gap_offset)?;
}
let has_common_prologue = true; if (self.previously_analyzed_gap == self.gap_pointer) || !has_common_prologue {
self.gap_pointer = self.get_next_gap(true, disassembly)?;
} else {
self.previously_analyzed_gap = self.gap_pointer;
self.add_gap_candidate(self.gap_pointer, disassembly)?;
return Ok(self.gap_pointer);
}
}
}
pub fn get_next_gap(&self, dont_skip: bool, disassembly: &DisassemblyResult) -> Result<u64> {
let mut next_gap = self.get_bitmask();
for gap in &self.function_gaps {
if gap.0 > self.gap_pointer {
next_gap = gap.0;
break;
}
}
if dont_skip && disassembly.code_map.contains_key(&self.gap_pointer) {
let function = disassembly.ins2fn[&self.gap_pointer];
if next_gap > disassembly.function_borders[&function].1 {
next_gap = disassembly.function_borders[&function].1;
}
}
Ok(next_gap)
}
pub fn add_tailcall_candidate(
&mut self,
addr: &u64,
disassembly: &DisassemblyResult,
) -> Result<bool> {
if !self.passes_code_filter(Some(*addr))? {
return Ok(false);
}
self.ensure_candidate(*addr, disassembly)?;
self.candidates
.get_mut(addr)
.ok_or(Error::LogicError(file!(), line!()))?
.set_is_tailcall_candidate(true)?;
Ok(true)
}
pub fn get_aborted_candidates(&self) -> Result<Vec<u64>> {
let mut aborted = vec![];
for (addr, candidate) in &self.candidates {
if candidate.analysis_aborted {
aborted.push(*addr);
}
}
Ok(aborted)
}
pub fn init_gap_search(&mut self, disassembly: &DisassemblyResult) -> Result<()> {
if self.gap_pointer == 0 {
self.gap_pointer = self.get_bitmask();
self.update_function_gaps(disassembly)?;
if !self.function_gaps.is_empty() {
self.gap_pointer = self.function_gaps[0].0;
}
}
self.function_gaps.sort_by_key(|gap| gap.0);
for _gap in &self.function_gaps {
}
Ok(())
}
pub fn add_gap_candidate(
&mut self,
addr: u64,
disassembly: &DisassemblyResult,
) -> Result<bool> {
if !self.passes_code_filter(Some(addr))? {
return Ok(false);
}
self.ensure_candidate(addr, disassembly)?;
self.candidates
.get_mut(&addr)
.ok_or(Error::LogicError(file!(), line!()))?
.set_is_gap_candidate(true)?;
Ok(true)
}
pub fn update_function_gaps(&mut self, disassembly: &DisassemblyResult) -> Result<()> {
let mut gaps = vec![];
let mut prev_ins = 0;
let mut min_code = self.get_bitmask();
let mut max_code = 0;
for f in disassembly.code_map.keys() {
if min_code > *f {
min_code = *f;
}
if max_code < *f {
max_code = *f;
}
}
for code_area in &self.code_areas {
if code_area.0 < min_code && min_code < code_area.1 && min_code != code_area.0 {
gaps.push((code_area.0, min_code, min_code - code_area.0));
}
if code_area.0 < max_code && max_code < code_area.1 && max_code != code_area.1 {
gaps.push((max_code, code_area.1, code_area.1 - max_code));
}
}
for (ins, _) in disassembly.code_map.iter().sorted() {
if prev_ins != 0 && ins - prev_ins > 1 {
gaps.push((prev_ins + 1, *ins, ins - prev_ins))
}
prev_ins = *ins
}
self.function_gaps = gaps;
Ok(())
}
}