mirror of
https://codeberg.org/unspeaker/vestal.git
synced 2025-12-06 10:46:42 +01:00
wip: baby's first (hex) dump
This commit is contained in:
parent
ed01876426
commit
099d0f3e92
2 changed files with 107 additions and 52 deletions
|
|
@ -10,11 +10,12 @@ fn main () -> Usually<()> {
|
||||||
use clap::{arg, command, value_parser, ArgAction, Command};
|
use clap::{arg, command, value_parser, ArgAction, Command};
|
||||||
let matches = command!()
|
let matches = command!()
|
||||||
.arg(arg!([path] "Path to VST DLL").value_parser(value_parser!(PathBuf)))
|
.arg(arg!([path] "Path to VST DLL").value_parser(value_parser!(PathBuf)))
|
||||||
.arg(arg!(-i --inspect "Show info, don't run").required(false))
|
|
||||||
//.arg(arg!(-s --stub <VALUE> "Provide a stub import").required(false))
|
//.arg(arg!(-s --stub <VALUE> "Provide a stub import").required(false))
|
||||||
|
.arg(arg!(-i --inspect "Show info, don't run").required(false))
|
||||||
|
.arg(arg!(-v --verbose "Show a lot of info").required(false))
|
||||||
.get_matches();
|
.get_matches();
|
||||||
let path = matches.get_one::<PathBuf>("path")
|
let path = matches.get_one::<PathBuf>("path").unwrap_or_else(||panic!("pass path to VST DLL"));
|
||||||
.unwrap_or_else(||panic!("Pass a path to a VST DLL."));
|
let verbose = *(matches.get_one::<bool>("verbose").unwrap_or(&false));
|
||||||
let mut rebuilder = Rebuilder::new(&[
|
let mut rebuilder = Rebuilder::new(&[
|
||||||
std::env::current_dir()?,
|
std::env::current_dir()?,
|
||||||
canonicalize(path.clone().parent().expect("invalid parent path"))?,
|
canonicalize(path.clone().parent().expect("invalid parent path"))?,
|
||||||
|
|
@ -26,9 +27,9 @@ 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 name = rebuilder.load(&path, true)?;
|
let name = rebuilder.load(&path, true, verbose)?;
|
||||||
let main = rebuilder.dlls.get(&name).unwrap();
|
let main = rebuilder.dlls.get(&name).unwrap();
|
||||||
rebuilder.resolve_calls(&main, true, true)?;
|
rebuilder.resolve_calls(&main, true, verbose)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,7 +50,7 @@ impl Rebuilder {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn load (&mut self, path: &impl AsRef<Path>, recurse: bool) -> Usually<Arc<str>> {
|
fn load (&mut self, path: &impl AsRef<Path>, recurse: bool, verbose: bool) -> Usually<Arc<str>> {
|
||||||
let path: Arc<PathBuf> = Arc::from(PathBuf::from(path.as_ref()));
|
let path: Arc<PathBuf> = Arc::from(PathBuf::from(path.as_ref()));
|
||||||
if self.visited.contains(&path) {
|
if self.visited.contains(&path) {
|
||||||
let name = path.file_name().expect("no file name");
|
let name = path.file_name().expect("no file name");
|
||||||
|
|
@ -57,12 +58,12 @@ impl Rebuilder {
|
||||||
return Ok(name)
|
return Ok(name)
|
||||||
}
|
}
|
||||||
self.visited.insert(path.clone());
|
self.visited.insert(path.clone());
|
||||||
let dll = Arc::new(Dll::new(&Arc::new(PathBuf::from(path.as_ref())), false)?);
|
let dll = Arc::new(Dll::new(&Arc::new(PathBuf::from(path.as_ref())), verbose)?);
|
||||||
self.dlls.insert(dll.name.clone(), dll.clone());
|
self.dlls.insert(dll.name.clone(), dll.clone());
|
||||||
if recurse {
|
if recurse {
|
||||||
for dep in dll.deps_by_library.keys() {
|
for dep in dll.deps_by_library.keys() {
|
||||||
if let Some(dep) = self.find(dep, false)? {
|
if let Some(dep) = self.find(dep, verbose)? {
|
||||||
self.load(&dep, recurse)?;
|
self.load(&dep, recurse, verbose)?;
|
||||||
} else {
|
} else {
|
||||||
panic!("not found: {dep:?}");
|
panic!("not found: {dep:?}");
|
||||||
}
|
}
|
||||||
|
|
@ -97,43 +98,45 @@ impl Rebuilder {
|
||||||
&self, dll: &Dll, addr: u32, call: &Arc<Call>, recurse: bool, verbose: bool,
|
&self, dll: &Dll, addr: u32, call: &Arc<Call>, recurse: bool, verbose: bool,
|
||||||
) -> Usually<()> {
|
) -> Usually<()> {
|
||||||
let addr = (addr - dll.code_base) as usize;
|
let addr = (addr - dll.code_base) as usize;
|
||||||
if verbose {
|
//if verbose {
|
||||||
println!("--------------------------------");
|
println!("--------------------------------");
|
||||||
dll.print_call(addr, call.module.as_ref(), call.method.as_ref());
|
dll.print_call(addr, call.module.as_ref(), call.method.as_ref());
|
||||||
dll.print_hex(addr, 1);
|
dll.print_hex(addr, 1);
|
||||||
}
|
//}
|
||||||
if let Some(method) = dll.parse_call(call) {
|
if let Some(method) = dll.parse_call(call) {
|
||||||
let module_name = call.module.as_ref().unwrap();
|
let module_name = call.module.as_ref().unwrap();
|
||||||
let method_name = call.method.as_ref().unwrap();
|
let method_name = call.method.as_ref().unwrap();
|
||||||
println!("0x{method:>08x} {module_name:20} {method_name}");
|
println!("{BOLD}0x{method:>08x}{RESET} {module_name:20} {method_name}");
|
||||||
if let Some(path) = self.find(module_name, false)? {
|
if let Some(path) = self.find(module_name, false)? {
|
||||||
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");
|
||||||
if let Some(dll) = self.dlls.get(&name) {
|
if let Some(dll) = self.dlls.get(&name) {
|
||||||
if let Some(thunk) = dll.exports.get(method_name) {
|
if let Some(thunk) = dll.exports.get(method_name) {
|
||||||
if let ThunkData::Function(rva) = thunk {
|
if let ThunkData::Function(rva) = thunk {
|
||||||
println!("# found {:?}::{} at 0x{:>08x}", &dll.name, method_name, rva.0);
|
|
||||||
let addr = (rva.0 - dll.code_base) as usize;
|
let addr = (rva.0 - dll.code_base) as usize;
|
||||||
dll.print_call(addr, None, None);
|
println!(" ⋮ ");
|
||||||
|
println!("{BOLD}0x{:>08x}{RESET} {}::{} found", rva.0, &dll.name, method_name);
|
||||||
dll.print_hex(addr, 1);
|
dll.print_hex(addr, 1);
|
||||||
if recurse {
|
if recurse {
|
||||||
let mut decoder = Decoder::with_ip(
|
let mut decoder = Decoder::with_ip(
|
||||||
64, &dll.code[addr..], 0x1000, DecoderOptions::NONE
|
64, &dll.code[addr..], 0, DecoderOptions::NONE
|
||||||
);
|
);
|
||||||
while decoder.can_decode() {
|
while decoder.can_decode() {
|
||||||
let position = decoder.position();
|
let position = decoder.position();
|
||||||
let instruction = decoder.decode();
|
let instruction = decoder.decode();
|
||||||
//println!("...");
|
|
||||||
dll.print_call(addr + position, None, None);
|
dll.print_call(addr + position, None, None);
|
||||||
//dll.print_hex(addr, 0);
|
let opcodes = &dll.code[position..position+instruction.len()];
|
||||||
|
if Call::matches(&instruction) && !Call::skip(opcodes) {
|
||||||
|
let start = 0x0;
|
||||||
|
let offset = (position + start) as u32;
|
||||||
|
let offset_rva = dll.pe.offset_to_rva(Offset(offset))?.0 as u32;
|
||||||
|
println!(" └--> {:?}", Call::target(opcodes, 0));
|
||||||
|
}
|
||||||
|
if dll.code[addr + position] == 0xc3 {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//println!("{:?}", &dll.code[addr..addr + 128].hex_dump());
|
|
||||||
//if let Some(path) = self.find(&dll.name, true)? {
|
|
||||||
//println!("# at {path:?}");
|
|
||||||
//} else {
|
|
||||||
//panic!("# not found {:?}", &dll.name);
|
|
||||||
//}
|
|
||||||
} else {
|
} else {
|
||||||
panic!("# unsupported {thunk:?}");
|
panic!("# unsupported {thunk:?}");
|
||||||
}
|
}
|
||||||
|
|
@ -190,12 +193,15 @@ impl Dll {
|
||||||
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 exports = collect_exports(&pe).unwrap_or_default();
|
let exports = collect_exports(
|
||||||
|
&pe,
|
||||||
|
verbose
|
||||||
|
).unwrap_or_default();
|
||||||
let (modules_count, methods_count) = collect_deps(
|
let (modules_count, methods_count) = collect_deps(
|
||||||
&mut deps_by_library,
|
&mut deps_by_library,
|
||||||
&mut deps_by_address,
|
&mut deps_by_address,
|
||||||
&pe,
|
&pe,
|
||||||
false
|
verbose,
|
||||||
)?;
|
)?;
|
||||||
let calls = collect_calls(
|
let calls = collect_calls(
|
||||||
&mut calls_by_source,
|
&mut calls_by_source,
|
||||||
|
|
@ -205,7 +211,7 @@ impl Dll {
|
||||||
&pe,
|
&pe,
|
||||||
start,
|
start,
|
||||||
text,
|
text,
|
||||||
false
|
verbose
|
||||||
)?;
|
)?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
|
|
@ -276,7 +282,7 @@ fn collect_deps (
|
||||||
deps_by_address.insert(call_via, (module_name.clone(), method.clone()));
|
deps_by_address.insert(call_via, (module_name.clone(), method.clone()));
|
||||||
methods += 1;
|
methods += 1;
|
||||||
if verbose {
|
if verbose {
|
||||||
println!(" ({index:5} 0x{call_via:08x} {module_name:>20} {method})");
|
println!(" (import 0x{call_via:08x} {module_name}::{method})");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -295,7 +301,8 @@ fn collect_deps (
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_exports (
|
fn collect_exports (
|
||||||
pe: &VecPE
|
pe: &VecPE,
|
||||||
|
_verbose: bool,
|
||||||
) -> Usually<BTreeMap<Arc<str>, ThunkData>> {
|
) -> Usually<BTreeMap<Arc<str>, ThunkData>> {
|
||||||
Ok(ImageExportDirectory::parse(pe)?
|
Ok(ImageExportDirectory::parse(pe)?
|
||||||
.get_export_map(pe)?
|
.get_export_map(pe)?
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,82 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
impl Dll {
|
impl Dll {
|
||||||
pub fn print_call (&self, addr: usize, module: Option<&Arc<str>>, method: Option<&Arc<str>>) {
|
pub fn print_call (&self, addr: usize, module: Option<&Arc<str>>, method: Option<&Arc<str>>) {
|
||||||
let mut decoder = Decoder::with_ip(64, &self.code[addr..], 0x1000, DecoderOptions::NONE);
|
let mut decoder = Decoder::with_ip(64, &self.code[addr..], 0x1000, DecoderOptions::NONE);
|
||||||
let instruction = decoder.decode();
|
let instruction = decoder.decode();
|
||||||
let opcodes = &self.code[addr..addr+instruction.len()].iter()
|
let is_stack = instruction.is_stack_instruction();
|
||||||
.map(|x|format!("{x:02x}"))
|
let is_link = Call::matches(&instruction);
|
||||||
.join(" ");
|
let style = if is_stack || is_link { BOLD } else { DIM };
|
||||||
println!("{BOLD}0x{addr:>08x}{RESET} {:20} {DIM}{opcodes}{RESET} {BOLD}{instruction}{RESET} {module:?} {method:?}", &self.name);
|
print!("{style}0x{addr:>08x} {:20}{RESET}", &self.name);
|
||||||
|
print!(" {style}{:26}{RESET}", Self::fmt_bytes(&self.code[addr..addr+instruction.len()]));
|
||||||
|
println!(" {style}{instruction}{RESET}");
|
||||||
|
if module.is_some() || method.is_some() {
|
||||||
|
println!(" └--> {}{}{}",
|
||||||
|
module.map(|x|x.as_ref()).unwrap_or(&""),
|
||||||
|
if module.is_some() && method.is_some() { "::" } else { "" },
|
||||||
|
method.map(|x|x.as_ref()).unwrap_or(&""));
|
||||||
}
|
}
|
||||||
pub fn print_hex (&self, addr: usize, n: usize) {
|
|
||||||
let cfg = HexConfig {title: false, width: 16, group: 4, chunk: 1, ..HexConfig::default()};
|
|
||||||
let snap = |x|(x/16)*16;
|
|
||||||
let a = snap(addr - 16*n);
|
|
||||||
let b = addr;
|
|
||||||
let c = snap(addr + 16);
|
|
||||||
let d = snap(c + 16*n);
|
|
||||||
if n > 0 {
|
|
||||||
println!("{DIM}{:?}{RESET}", &self.code[a..b].hex_conf(HexConfig {
|
|
||||||
display_offset: self.code_base as usize + a, ..cfg
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
println!("{BOLD}{:?}{RESET}", &self.code[b..c].hex_conf(HexConfig {
|
pub fn print_hex (&self, addr: usize, context: usize) {
|
||||||
display_offset: self.code_base as usize + b, ..cfg
|
let base = self.code_base as usize;
|
||||||
}));
|
let line = 16;
|
||||||
if n > 0 {
|
let group = 2;
|
||||||
println!("{DIM}{:?}{RESET}", &self.code[c..d].hex_conf(HexConfig {
|
let snap = |x|(x/line)*line;
|
||||||
display_offset: self.code_base as usize + c, ..cfg
|
let byte_start = snap(addr).saturating_sub(context*line);
|
||||||
}));
|
let byte_end = snap(addr) + (context + 1) * line;
|
||||||
|
let mut output = String::new();
|
||||||
|
for (index, byte) in (byte_start..byte_end).enumerate() {
|
||||||
|
write!(&mut output, "{DIM}");
|
||||||
|
if byte % line == 0 {
|
||||||
|
if (byte >= snap(addr)) && (byte < snap(addr) + line) {
|
||||||
|
write!(&mut output, "{RESET}{BOLD}");
|
||||||
}
|
}
|
||||||
|
write!(&mut output, " {byte:08x}");
|
||||||
|
}
|
||||||
|
write!(&mut output, "{DIM}");
|
||||||
|
if byte % group == 0 {
|
||||||
|
write!(&mut output, " ");
|
||||||
|
}
|
||||||
|
if (byte >= addr) && (byte < addr + 8) {
|
||||||
|
write!(&mut output, "{RESET}{BOLD}");
|
||||||
|
}
|
||||||
|
write!(&mut output, "{:02x}", self.code[byte]);
|
||||||
|
if byte % line == line - 1 {
|
||||||
|
write!(&mut output, "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("{output}");
|
||||||
|
//let cfg = HexConfig {title: false, width: 16, group: 4, chunk: 1, ..HexConfig::default()};
|
||||||
|
//if n > 0 {
|
||||||
|
//println!(" {DIM}{}{RESET}", Self::fmt_hex_line(a, &self.code[a..b]));
|
||||||
|
//}
|
||||||
|
//println!(" {BOLD}{}{RESET}", Self::fmt_hex_line(b, &self.code[b..c]));
|
||||||
|
//if n > 0 {
|
||||||
|
//println!(" {DIM}{}{RESET}", Self::fmt_hex_line(c, &self.code[c..d]));
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
//pub fn fmt_hex_block (data: &[u8], addr: usize, length: usize, context: usize) -> Arc<str> {
|
||||||
|
//let start = addr;
|
||||||
|
//let end = addr + length;
|
||||||
|
//let line = 16;
|
||||||
|
//let snap = |x|(x/line)*line;
|
||||||
|
//let addr = snap(addr);
|
||||||
|
//let prev = addr - line * context;
|
||||||
|
//let next = addr + line * (context + 1);
|
||||||
|
//if n > 0 {
|
||||||
|
//println!(" {DIM}{}{RESET}", Self::fmt_hex_line(prev, &self.code[prev..addr]));
|
||||||
|
//}
|
||||||
|
//println!(" {BOLD}{}{RESET}", Self::fmt_hex_line(b, &self.code[addr..addr+length]));
|
||||||
|
//if n > 0 {
|
||||||
|
//println!(" {DIM}{}{RESET}", Self::fmt_hex_line(c, &self.code[addr+length..next]));
|
||||||
|
//}
|
||||||
|
//}
|
||||||
|
pub fn fmt_hex_line (addr: usize, data: &[u8]) -> Arc<str> {
|
||||||
|
format!("{:>08x} {}", addr, Self::fmt_bytes(data)).into()
|
||||||
|
}
|
||||||
|
pub fn fmt_bytes (bytes: &[u8]) -> Arc<str> {
|
||||||
|
bytes.iter().map(|x|format!("{x:02x}")).join(" ").into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue