mirror of
https://codeberg.org/unspeaker/vestal.git
synced 2025-12-06 12:56:41 +01:00
hexdump callsites; need exports now
This commit is contained in:
parent
27dec3a72c
commit
cfd291b050
2 changed files with 104 additions and 49 deletions
|
|
@ -1,9 +1,9 @@
|
||||||
#![feature(slice_split_once)]
|
#![feature(slice_split_once)]
|
||||||
mod util;
|
mod util;
|
||||||
|
mod bang;
|
||||||
//mod load;
|
//mod load;
|
||||||
//mod show;
|
//mod show;
|
||||||
mod link;
|
//mod link;
|
||||||
mod bang;
|
|
||||||
pub(crate) use self::util::*;
|
pub(crate) use self::util::*;
|
||||||
|
|
||||||
fn main () -> Usually<()> {
|
fn main () -> Usually<()> {
|
||||||
|
|
@ -27,19 +27,59 @@ fn main () -> Usually<()> {
|
||||||
let path = rebuilder.find(path.to_str().expect("path must be unicode"), false)?
|
let path = rebuilder.find(path.to_str().expect("path must be unicode"), false)?
|
||||||
.unwrap_or_else(||panic!("Could not find: {path:?}"));
|
.unwrap_or_else(||panic!("Could not find: {path:?}"));
|
||||||
let main = rebuilder.load(&path, true)?;
|
let main = rebuilder.load(&path, true)?;
|
||||||
//let main = rebuilder.dlls.get(&name).unwrap();
|
println!("\n{main}");
|
||||||
for (name, dll) in rebuilder.dlls.iter() {
|
let main = rebuilder.dlls.get(&main).unwrap();
|
||||||
println!("\n{name}");
|
//println!("{:?}", main.code[0..1024.min(main.code.len())].hex_conf(HexConfig {
|
||||||
println!("{:?}", dll.code.hex_conf(HexConfig {
|
//title: false,
|
||||||
|
//width: 16,
|
||||||
|
//group: 4,
|
||||||
|
//chunk: 1,
|
||||||
|
//display_offset: main.code_base as usize,
|
||||||
|
//..HexConfig::default()
|
||||||
|
//}));
|
||||||
|
for (addr, call) in main.calls_by_source.iter() {
|
||||||
|
let start = (addr - main.code_base) as usize;
|
||||||
|
let end = start + call.length;
|
||||||
|
let mut decoder = Decoder::with_ip(64, &main.code[start..end], 0, DecoderOptions::NONE);
|
||||||
|
let instruction = decoder.decode();
|
||||||
|
let cfg = HexConfig {
|
||||||
title: false,
|
title: false,
|
||||||
width: 32,
|
width: 16,
|
||||||
group: 4,
|
group: 4,
|
||||||
chunk: 4,
|
chunk: 1,
|
||||||
display_offset: dll.code_start as usize,
|
display_offset: main.code_base as usize,
|
||||||
..HexConfig::default()
|
..HexConfig::default()
|
||||||
}));
|
};
|
||||||
|
let end = start + 16;
|
||||||
|
let snap = |x|(x/16)*16;
|
||||||
|
let a = snap(start - 32);
|
||||||
|
let b = start;
|
||||||
|
let c = snap(end);
|
||||||
|
let d = snap(c + 32);
|
||||||
|
println!("\n{BOLD}0x{addr:x}{RESET} {} {:?} {:?}\n{:?}\n{BOLD}{:?}{RESET}\n{:?}",
|
||||||
|
instruction,
|
||||||
|
call.module,
|
||||||
|
call.method,
|
||||||
|
&main.code[a..b].hex_conf(HexConfig { display_offset: main.code_base as usize + a, ..cfg }),
|
||||||
|
&main.code[b..c].hex_conf(HexConfig { display_offset: main.code_base as usize + b, ..cfg }),
|
||||||
|
&main.code[c..d].hex_conf(HexConfig { display_offset: main.code_base as usize + c, ..cfg }));
|
||||||
|
if let (Some(module_name), Some(method_name)) = (&call.module, &call.method) {
|
||||||
|
if let Some(module) = main.deps_by_library.get(module_name) {
|
||||||
|
if let Some(method) = module.get(method_name) {
|
||||||
|
println!("0x{method:>08x} {module_name} {method_name}");
|
||||||
|
if let Some(dll) = rebuilder.dlls.get(module_name) {
|
||||||
|
if let Some(address) = dll.exports.get(method_name) {
|
||||||
|
println!("# found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//let mut decoder = iced_x86::Decoder::with_ip(64, &main.code, 0x1000, 0);
|
//println!("{:#?}", &main.calls_by_source);
|
||||||
|
for (name, dll) in rebuilder.dlls.iter() {
|
||||||
|
}
|
||||||
|
//let mut decoder = Decoder::with_ip(64, &main.code, 0x1000, 0);
|
||||||
//while decoder.can_decode() {
|
//while decoder.can_decode() {
|
||||||
//let instruction = decoder.decode();
|
//let instruction = decoder.decode();
|
||||||
//if Dll::matches(&instruction) {
|
//if Dll::matches(&instruction) {
|
||||||
|
|
@ -61,18 +101,18 @@ struct Rebuilder {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Dll {
|
struct Dll {
|
||||||
/// Canonical name like `xxx.dll` (always lower case!)
|
/// Canonical name like `xxx.dll` (always lowercase)
|
||||||
name: Arc<str>,
|
name: Arc<str>,
|
||||||
/// Path to DLL on host filesystem.
|
/// Path to DLL on host filesystem.
|
||||||
path: Arc<PathBuf>,
|
path: Arc<PathBuf>,
|
||||||
/// Bytes of `#!`-instruction
|
/// Bytes of `#!`-instruction
|
||||||
bang: Arc<[u8]>,
|
bang: Arc<[u8]>,
|
||||||
/// Parsed portable executable
|
/// Parsed portable executable
|
||||||
pe: Arc<VecPE>,
|
pe: Arc<VecPE>,
|
||||||
/// Bytes of `.text` section
|
/// Bytes of `.text` section
|
||||||
code: Arc<[u8]>,
|
code: Arc<[u8]>,
|
||||||
/// Start of `.text` section
|
/// Start of `.text` section
|
||||||
code_start: u32,
|
code_base: u32,
|
||||||
/// Addresses of imported methods by library
|
/// Addresses of imported methods by library
|
||||||
deps_by_library: BTreeMap<Arc<str>, BTreeMap<Arc<str>, u32>>,
|
deps_by_library: BTreeMap<Arc<str>, BTreeMap<Arc<str>, u32>>,
|
||||||
/// Imported methods by address
|
/// Imported methods by address
|
||||||
|
|
@ -81,6 +121,8 @@ struct Dll {
|
||||||
calls_by_source: BTreeMap<u32, Arc<Call>>,
|
calls_by_source: BTreeMap<u32, Arc<Call>>,
|
||||||
/// Calls to dependencies by target address
|
/// Calls to dependencies by target address
|
||||||
calls_by_target: BTreeMap<u32, Vec<Arc<Call>>>,
|
calls_by_target: BTreeMap<u32, Vec<Arc<Call>>>,
|
||||||
|
/// Addresses of exported methods by name
|
||||||
|
exports: BTreeMap<Arc<str>, u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -146,11 +188,10 @@ impl Rebuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dll {
|
impl Dll {
|
||||||
fn new (
|
fn new (path: &Arc<PathBuf>, verbose: bool) -> Usually<Self> {
|
||||||
path: &Arc<PathBuf>,
|
if verbose {
|
||||||
verbose: bool
|
println!("\n(load {BOLD}{path:?}{RESET})");
|
||||||
) -> Usually<Self> {
|
}
|
||||||
println!("\n(load {BOLD}{path:?}{RESET})");
|
|
||||||
let name = path.file_name().expect("no file name");
|
let name = path.file_name().expect("no file name");
|
||||||
let name: Arc<str> = name.to_str().map(Arc::from).expect("non-unicode filename");
|
let name: Arc<str> = name.to_str().map(Arc::from).expect("non-unicode filename");
|
||||||
let (bang, data) = crate::bang::slice_shebang(read(path.as_path())?.as_slice());
|
let (bang, data) = crate::bang::slice_shebang(read(path.as_path())?.as_slice());
|
||||||
|
|
@ -161,8 +202,8 @@ impl Dll {
|
||||||
let text = &data[start..start+size];
|
let text = &data[start..start+size];
|
||||||
let mut calls_by_source = Default::default();
|
let mut calls_by_source = Default::default();
|
||||||
let mut calls_by_target = Default::default();
|
let mut calls_by_target = Default::default();
|
||||||
let mut deps_by_library = Default::default();
|
let mut deps_by_library = Default::default();
|
||||||
let mut deps_by_address = Default::default();
|
let mut deps_by_address = Default::default();
|
||||||
let (modules_count, methods_count) = Self::deps(
|
let (modules_count, methods_count) = Self::deps(
|
||||||
&pe,
|
&pe,
|
||||||
&mut deps_by_library,
|
&mut deps_by_library,
|
||||||
|
|
@ -177,19 +218,23 @@ impl Dll {
|
||||||
Some(&deps_by_address),
|
Some(&deps_by_address),
|
||||||
&mut calls_by_source,
|
&mut calls_by_source,
|
||||||
&mut calls_by_target,
|
&mut calls_by_target,
|
||||||
true
|
false
|
||||||
)?;
|
)?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
path: path.clone(),
|
path: path.clone(),
|
||||||
bang,
|
bang,
|
||||||
pe,
|
|
||||||
code: Arc::from(text),
|
code: Arc::from(text),
|
||||||
code_start: start as u32,
|
code_base: match pe.get_valid_nt_headers()? {
|
||||||
|
NTHeaders::NTHeaders32(h32) => panic!("32 bit headers"),
|
||||||
|
NTHeaders::NTHeaders64(h64) => h64.optional_header.base_of_code.0,
|
||||||
|
},
|
||||||
deps_by_library: deps_by_library.clone(),
|
deps_by_library: deps_by_library.clone(),
|
||||||
deps_by_address: deps_by_address.clone(),
|
deps_by_address: deps_by_address.clone(),
|
||||||
calls_by_source,
|
calls_by_source,
|
||||||
calls_by_target,
|
calls_by_target,
|
||||||
|
exports: Default::default(),
|
||||||
|
pe,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn deps (
|
fn deps (
|
||||||
|
|
@ -243,9 +288,9 @@ impl Dll {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!(" (deps-modules {modules})");
|
|
||||||
println!(" (deps-methods {methods})");
|
|
||||||
if verbose {
|
if verbose {
|
||||||
|
println!(" (deps-modules {modules})");
|
||||||
|
println!(" (deps-methods {methods})");
|
||||||
for (module, methods) in deps_by_library.iter() {
|
for (module, methods) in deps_by_library.iter() {
|
||||||
print!(" ({module}");
|
print!(" ({module}");
|
||||||
for (method, addr) in methods.iter() {
|
for (method, addr) in methods.iter() {
|
||||||
|
|
@ -256,6 +301,16 @@ impl Dll {
|
||||||
}
|
}
|
||||||
Ok((modules, methods))
|
Ok((modules, methods))
|
||||||
}
|
}
|
||||||
|
fn dep_name (deps: Option<&BTreeMap<u32, (Arc<str>, Arc<str>)>>, target: u32)
|
||||||
|
-> (Option<Arc<str>>, Option<Arc<str>>, Arc<str>)
|
||||||
|
{
|
||||||
|
let module = deps.and_then(|deps|deps.get(&target)).map(|dep|dep.0.clone());
|
||||||
|
let method = deps.and_then(|deps|deps.get(&target)).map(|dep|dep.1.clone());
|
||||||
|
let external = format!("{}::{}",
|
||||||
|
module.as_ref().map(|x|x.as_ref()).unwrap_or("???"),
|
||||||
|
method.as_ref().map(|x|x.as_ref()).unwrap_or("???"));
|
||||||
|
(module, method, external.into())
|
||||||
|
}
|
||||||
fn calls (
|
fn calls (
|
||||||
name: &Arc<str>,
|
name: &Arc<str>,
|
||||||
pe: &VecPE,
|
pe: &VecPE,
|
||||||
|
|
@ -266,7 +321,7 @@ impl Dll {
|
||||||
calls_by_target: &mut BTreeMap<u32, Vec<Arc<Call>>>,
|
calls_by_target: &mut BTreeMap<u32, Vec<Arc<Call>>>,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
) -> Usually<usize> {
|
) -> Usually<usize> {
|
||||||
let mut decoder = iced_x86::Decoder::with_ip(64, data, 0x1000, 0);
|
let mut decoder = Decoder::with_ip(64, data, 0x1000, 0);
|
||||||
let mut calls = 0;
|
let mut calls = 0;
|
||||||
while decoder.can_decode() {
|
while decoder.can_decode() {
|
||||||
if let Some(call) = Self::call(name, pe, start, data, deps, &mut decoder, false)? {
|
if let Some(call) = Self::call(name, pe, start, data, deps, &mut decoder, false)? {
|
||||||
|
|
@ -278,8 +333,8 @@ impl Dll {
|
||||||
calls_by_target.get_mut(&call.target).unwrap().push(call);
|
calls_by_target.get_mut(&call.target).unwrap().push(call);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!(" (call-sites {calls})");
|
|
||||||
if verbose {
|
if verbose {
|
||||||
|
println!(" (call-sites {calls})");
|
||||||
for (target, sites) in calls_by_target.iter() {
|
for (target, sites) in calls_by_target.iter() {
|
||||||
let (_, _, external) = Self::dep_name(deps, *target);
|
let (_, _, external) = Self::dep_name(deps, *target);
|
||||||
println!(" ({:>5}x call 0x{target:08x} {external})", sites.len());
|
println!(" ({:>5}x call 0x{target:08x} {external})", sites.len());
|
||||||
|
|
@ -292,32 +347,22 @@ impl Dll {
|
||||||
}
|
}
|
||||||
Ok(calls)
|
Ok(calls)
|
||||||
}
|
}
|
||||||
fn dep_name (deps: Option<&BTreeMap<u32, (Arc<str>, Arc<str>)>>, target: u32)
|
|
||||||
-> (Option<Arc<str>>, Option<Arc<str>>, Arc<str>)
|
|
||||||
{
|
|
||||||
let module = deps.and_then(|deps|deps.get(&target)).map(|dep|dep.0.clone());
|
|
||||||
let method = deps.and_then(|deps|deps.get(&target)).map(|dep|dep.1.clone());
|
|
||||||
let external = format!("{}::{}",
|
|
||||||
module.as_ref().map(|x|x.as_ref()).unwrap_or("???"),
|
|
||||||
method.as_ref().map(|x|x.as_ref()).unwrap_or("???"));
|
|
||||||
(module, method, external.into())
|
|
||||||
}
|
|
||||||
fn call (
|
fn call (
|
||||||
name: &Arc<str>,
|
name: &Arc<str>,
|
||||||
pe: &VecPE,
|
pe: &VecPE,
|
||||||
start: usize,
|
start: usize,
|
||||||
data: &[u8],
|
data: &[u8],
|
||||||
deps: Option<&BTreeMap<u32, (Arc<str>, Arc<str>)>>,
|
deps: Option<&BTreeMap<u32, (Arc<str>, Arc<str>)>>,
|
||||||
decoder: &mut iced_x86::Decoder,
|
decoder: &mut Decoder,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
) -> Usually<Option<Arc<Call>>> {
|
) -> Usually<Option<Arc<Call>>> {
|
||||||
let position = decoder.position();
|
let position = decoder.position();
|
||||||
let instruction = decoder.decode();
|
let instruction = decoder.decode();
|
||||||
let opcodes = &data[position..position+instruction.len()];
|
let opcodes = &data[position..position+instruction.len()];
|
||||||
if Self::matches(&instruction) && !Self::skip(opcodes) {
|
if Call::matches(&instruction) && !Call::skip(opcodes) {
|
||||||
let offset = (position + start) as u32;
|
let offset = (position + start) as u32;
|
||||||
let offset_rva = pe.offset_to_rva(Offset(offset))?.0;
|
let offset_rva = pe.offset_to_rva(Offset(offset))?.0;
|
||||||
if let Some(target) = Self::target(opcodes, offset_rva) {
|
if let Some(target) = Call::target(opcodes, offset_rva) {
|
||||||
let (module, method, external) = Self::dep_name(deps, target);
|
let (module, method, external) = Self::dep_name(deps, target);
|
||||||
if verbose {
|
if verbose {
|
||||||
let external = format!("{}::{}",
|
let external = format!("{}::{}",
|
||||||
|
|
@ -338,10 +383,13 @@ impl Dll {
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
fn matches (instruction: &iced_x86::Instruction) -> bool {
|
}
|
||||||
instruction.op0_kind() == iced_x86::OpKind::Memory && (
|
|
||||||
instruction.flow_control() == iced_x86::FlowControl::IndirectBranch ||
|
impl Call {
|
||||||
instruction.flow_control() == iced_x86::FlowControl::IndirectCall
|
fn matches (instruction: &Instruction) -> bool {
|
||||||
|
instruction.op0_kind() == OpKind::Memory && (
|
||||||
|
instruction.flow_control() == FlowControl::IndirectBranch ||
|
||||||
|
instruction.flow_control() == FlowControl::IndirectCall
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
fn skip (opcodes: &[u8]) -> bool {
|
fn skip (opcodes: &[u8]) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,13 @@ pub(crate) use ::object::endian::LittleEndian;
|
||||||
pub(crate) use ::object::write::elf::Writer as ElfWriter;
|
pub(crate) use ::object::write::elf::Writer as ElfWriter;
|
||||||
pub(crate) use ::pretty_hex::*;
|
pub(crate) use ::pretty_hex::*;
|
||||||
pub(crate) use ::exe::{Buffer, PE, VecPE, PtrPE, types::*, headers::*};
|
pub(crate) use ::exe::{Buffer, PE, VecPE, PtrPE, types::*, headers::*};
|
||||||
|
pub(crate) use ::iced_x86::{Encoder, Decoder, DecoderOptions, Instruction, OpKind, FlowControl};
|
||||||
pub(crate) type Usually<T> = Result<T, Box<dyn std::error::Error>>;
|
pub(crate) type Usually<T> = Result<T, Box<dyn std::error::Error>>;
|
||||||
pub const RESET: &str = "\u{001b}[0m";
|
pub const RESET: &str = "\u{001b}[0m";
|
||||||
pub const BOLD: &str = "\u{001b}[1m";
|
pub const BOLD: &str = "\u{001b}[1m";
|
||||||
|
pub enum Verbosity {
|
||||||
|
Silent,
|
||||||
|
Terse,
|
||||||
|
Brief,
|
||||||
|
Verbose,
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue