use log::debug;
use anyhow::Result;
use crate::{analysis::dis, aspace::AddressSpace, loader::pe::PE, module::Permissions, util, VA};
pub fn find_pe_call_targets(pe: &PE) -> Result<Vec<VA>> {
let mut ret = vec![];
let decoder = dis::get_disassembler(&pe.module)?;
let mut call_count = 0usize;
for section in pe.executable_sections() {
let vstart: VA = section.virtual_range.start;
let vsize = (section.virtual_range.end - section.virtual_range.start) as usize;
let sec_buf = pe.module.address_space.read_bytes(vstart, vsize)?;
for (insn_offset, insn) in dis::linear_disassemble(&decoder, &sec_buf) {
if let Ok(Some(insn)) = insn {
if insn.meta.category != zydis::InstructionCategory::CALL {
continue;
}
let insn_va: VA = vstart + insn_offset as u64;
let op0 = &insn.operands[0];
match op0.ty {
zydis::OperandType::POINTER => {
let target = op0.ptr.offset as u64;
if pe.module.probe_va(target, Permissions::X) {
ret.push(target);
}
}
zydis::OperandType::IMMEDIATE => {
if op0.imm.is_relative {
let imm = if op0.imm.is_signed {
util::u64_i64(op0.imm.value)
} else {
op0.imm.value as i64
};
let target = ((insn_va + insn.length as u64) as i64 + imm) as u64;
if pe.module.probe_va(target, Permissions::X) {
ret.push(target);
}
} else {
debug!("CALL-IMM-ABS: {:#x}", insn_va);
panic!("call immediate absolute")
}
}
_ => continue,
}
call_count += 1;
}
}
debug!("call count: {}", call_count);
}
Ok(ret)
}
#[cfg(test)]
mod tests {
use crate::rsrc::*;
use anyhow::Result;
#[test]
fn k32() -> Result<()> {
let buf = get_buf(Rsrc::K32);
let pe = crate::loader::pe::PE::from_bytes(&buf)?;
let fns = crate::analysis::pe::call_targets::find_pe_call_targets(&pe)?;
assert_eq!(3610, fns.len());
Ok(())
}
#[test]
fn tiny() -> Result<()> {
let buf = get_buf(Rsrc::TINY);
let pe = crate::loader::pe::PE::from_bytes(&buf)?;
let fns = crate::analysis::pe::call_targets::find_pe_call_targets(&pe)?;
assert_eq!(0, fns.len());
Ok(())
}
#[test]
fn nop() -> Result<()> {
let buf = get_buf(Rsrc::NOP);
let pe = crate::loader::pe::PE::from_bytes(&buf)?;
let fns = crate::analysis::pe::call_targets::find_pe_call_targets(&pe)?;
assert_eq!(250, fns.len());
Ok(())
}
#[test]
fn mimi() -> Result<()> {
let buf = get_buf(Rsrc::MIMI);
let pe = crate::loader::pe::PE::from_bytes(&buf)?;
let fns = crate::analysis::pe::call_targets::find_pe_call_targets(&pe)?;
assert_eq!(10907, fns.len());
Ok(())
}
}