flatten and cleanup

This commit is contained in:
🪞👃🪞 2025-02-22 17:24:34 +02:00
parent 459f6c643c
commit ed01876426
2 changed files with 311 additions and 310 deletions

View file

@ -2,7 +2,7 @@
mod util;
mod bang;
//mod load;
//mod show;
mod show;
//mod link;
pub(crate) use self::util::*;
@ -29,16 +29,6 @@ fn main () -> Usually<()> {
let name = rebuilder.load(&path, true)?;
let main = rebuilder.dlls.get(&name).unwrap();
rebuilder.resolve_calls(&main, true, true)?;
//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() {
//let instruction = decoder.decode();
//if Dll::matches(&instruction) {
//println!("{instruction}");
//}
//}
Ok(())
}
@ -128,7 +118,7 @@ impl Rebuilder {
dll.print_hex(addr, 1);
if recurse {
let mut decoder = Decoder::with_ip(
64, &dll.code[addr..], 0, DecoderOptions::NONE
64, &dll.code[addr..], 0x1000, DecoderOptions::NONE
);
while decoder.can_decode() {
let position = decoder.position();
@ -200,21 +190,21 @@ impl Dll {
let mut calls_by_target = Default::default();
let mut deps_by_library = Default::default();
let mut deps_by_address = Default::default();
let exports = Self::exports(&pe).unwrap_or_default();
let (modules_count, methods_count) = Self::deps(
&pe,
let exports = collect_exports(&pe).unwrap_or_default();
let (modules_count, methods_count) = collect_deps(
&mut deps_by_library,
&mut deps_by_address,
&pe,
false
)?;
let calls = Call::calls(
let calls = collect_calls(
&mut calls_by_source,
&mut calls_by_target,
Some(&deps_by_address),
&name,
&pe,
start,
text,
Some(&deps_by_address),
&mut calls_by_source,
&mut calls_by_target,
false
)?;
Ok(Self {
@ -234,10 +224,15 @@ impl Dll {
pe,
})
}
fn deps (
pe: &VecPE,
fn parse_call (&self, call: &Arc<Call>) -> Option<u32> {
self.deps_by_library.get(call.module.as_ref()?)?.get(call.method.as_ref()?).map(|x|*x)
}
}
fn collect_deps (
deps_by_library: &mut BTreeMap<Arc<str>, BTreeMap<Arc<str>, u32>>,
deps_by_address: &mut BTreeMap<u32, (Arc<str>, Arc<str>)>,
pe: &VecPE,
verbose: bool,
) -> Usually<(usize, usize)> {
let directory = ImportDirectory::parse(pe)?;
@ -298,45 +293,100 @@ impl Dll {
}
Ok((modules, methods))
}
fn exports (pe: &VecPE) -> Usually<BTreeMap<Arc<str>, ThunkData>> {
fn collect_exports (
pe: &VecPE
) -> Usually<BTreeMap<Arc<str>, ThunkData>> {
Ok(ImageExportDirectory::parse(pe)?
.get_export_map(pe)?
.into_iter()
.map(|(k, v)|(k.into(), v))
.collect())
}
fn parse_call (&self, call: &Arc<Call>) -> Option<u32> {
self.deps_by_library.get(call.module.as_ref()?)?.get(call.method.as_ref()?).map(|x|*x)
fn collect_calls (
calls_by_source: &mut BTreeMap<u32, Arc<Call>>,
calls_by_target: &mut BTreeMap<u32, Vec<Arc<Call>>>,
deps: Option<&BTreeMap<u32, (Arc<str>, Arc<str>)>>,
name: &Arc<str>,
pe: &VecPE,
start: usize,
data: &[u8],
verbose: bool,
) -> Usually<usize> {
let mut decoder = Decoder::with_ip(64, data, 0x1000, 0);
let mut calls = 0;
while decoder.can_decode() {
if let Some(call) = collect_call(name, pe, start, data, deps, &mut decoder, false)? {
calls += 1;
calls_by_source.insert(call.source, call.clone());
if !calls_by_target.contains_key(&call.target) {
calls_by_target.insert(call.target, Default::default());
}
fn print_call (&self, addr: usize, module: Option<&Arc<str>>, method: Option<&Arc<str>>) {
let mut decoder = Decoder::with_ip(64, &self.code[addr..], 0, DecoderOptions::NONE);
calls_by_target.get_mut(&call.target).unwrap().push(call);
}
}
if verbose {
println!(" (call-sites {calls})");
for (target, sites) in calls_by_target.iter() {
let (_, _, external) = dep_name(deps, *target);
println!(" ({:>5}x call 0x{target:08x} {external})", sites.len());
//.map(|site|format!("0x{:08x}", site.offset))
//.collect::<Vec<_>>());
//println!(" (call 0x{target:08x} {external}\n {:?})", sites.iter()
//.map(|site|format!("0x{:08x}", site.offset))
//.collect::<Vec<_>>());
}
}
Ok(calls)
}
fn collect_call (
name: &Arc<str>,
pe: &VecPE,
start: usize,
data: &[u8],
deps: Option<&BTreeMap<u32, (Arc<str>, Arc<str>)>>,
decoder: &mut Decoder,
verbose: bool,
) -> Usually<Option<Arc<Call>>> {
let position = decoder.position();
let instruction = decoder.decode();
let opcodes = &self.code[addr..addr+instruction.len()].iter()
.map(|x|format!("{x:02x}"))
.join(" ");
println!("{BOLD}0x{addr:>08x}{RESET} {:20} {DIM}{opcodes}{RESET} {BOLD}{instruction}{RESET} {module:?} {method:?}", &self.name);
let opcodes = &data[position..position+instruction.len()];
if Call::matches(&instruction) && !Call::skip(opcodes) {
let offset = (position + start) as u32;
let offset_rva = pe.offset_to_rva(Offset(offset))?.0;
if let Some(target) = Call::target(opcodes, offset_rva) {
let (module, method, external) = dep_name(deps, target);
if verbose {
let external = format!("{}::{}",
module.as_ref().map(|x|x.as_ref()).unwrap_or("???"),
method.as_ref().map(|x|x.as_ref()).unwrap_or("???"));
println!(" ({BOLD}0x{:08x}{RESET} 0x{:08x} {BOLD}{:30}{RESET} 0x{:x} {}",
offset, offset_rva, instruction, target, external);
}
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 {
display_offset: self.code_base as usize + b, ..cfg
}));
if n > 0 {
println!("{DIM}{:?}{RESET}", &self.code[c..d].hex_conf(HexConfig {
display_offset: self.code_base as usize + c, ..cfg
}));
return Ok(Some(Arc::new(Call {
offset: offset,
source: offset_rva,
length: opcodes.len(),
target,
module,
method,
})))
}
}
Ok(None)
}
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())
}
#[derive(Debug)]
@ -409,86 +459,4 @@ impl Call {
}
None
}
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 (
name: &Arc<str>,
pe: &VecPE,
start: usize,
data: &[u8],
deps: Option<&BTreeMap<u32, (Arc<str>, Arc<str>)>>,
calls_by_source: &mut BTreeMap<u32, Arc<Call>>,
calls_by_target: &mut BTreeMap<u32, Vec<Arc<Call>>>,
verbose: bool,
) -> Usually<usize> {
let mut decoder = Decoder::with_ip(64, data, 0x1000, 0);
let mut calls = 0;
while decoder.can_decode() {
if let Some(call) = Self::call(name, pe, start, data, deps, &mut decoder, false)? {
calls += 1;
calls_by_source.insert(call.source, call.clone());
if !calls_by_target.contains_key(&call.target) {
calls_by_target.insert(call.target, Default::default());
}
calls_by_target.get_mut(&call.target).unwrap().push(call);
}
}
if verbose {
println!(" (call-sites {calls})");
for (target, sites) in calls_by_target.iter() {
let (_, _, external) = Self::dep_name(deps, *target);
println!(" ({:>5}x call 0x{target:08x} {external})", sites.len());
//.map(|site|format!("0x{:08x}", site.offset))
//.collect::<Vec<_>>());
//println!(" (call 0x{target:08x} {external}\n {:?})", sites.iter()
//.map(|site|format!("0x{:08x}", site.offset))
//.collect::<Vec<_>>());
}
}
Ok(calls)
}
fn call (
name: &Arc<str>,
pe: &VecPE,
start: usize,
data: &[u8],
deps: Option<&BTreeMap<u32, (Arc<str>, Arc<str>)>>,
decoder: &mut Decoder,
verbose: bool,
) -> Usually<Option<Arc<Call>>> {
let position = decoder.position();
let instruction = decoder.decode();
let opcodes = &data[position..position+instruction.len()];
if Call::matches(&instruction) && !Call::skip(opcodes) {
let offset = (position + start) as u32;
let offset_rva = pe.offset_to_rva(Offset(offset))?.0;
if let Some(target) = Call::target(opcodes, offset_rva) {
let (module, method, external) = Self::dep_name(deps, target);
if verbose {
let external = format!("{}::{}",
module.as_ref().map(|x|x.as_ref()).unwrap_or("???"),
method.as_ref().map(|x|x.as_ref()).unwrap_or("???"));
println!(" ({BOLD}0x{:08x}{RESET} 0x{:08x} {BOLD}{:30}{RESET} 0x{:x} {}",
offset, offset_rva, instruction, target, external);
}
return Ok(Some(Arc::new(Call {
offset: offset,
source: offset_rva,
length: opcodes.len(),
target,
module,
method,
})))
}
}
Ok(None)
}
}

View file

@ -1,116 +1,149 @@
use crate::*;
impl Vestal {
pub fn show_addr_to_import (&self) {
for (addr, (dll, export)) in self.addr_to_import.iter() {
println!("{BOLD}0x{addr:>08x}{RESET} {dll:>20} {export:<40}");
}
}
pub fn show_vst_entrypoint (&self, path: &PathBuf) {
//let exports = self.path_to_exports.get(path).expect("no exports");
//for export in exports.iter() {
//if export.name_string() == Some("VSTPluginMain".to_string()) {
//println!("{export:?}");
//println!("{}", export.name_string().unwrap());
//let addr = (export.addr() as usize);
//println!();
//println!();
//return Ok(())
//}
//}
//panic!("no main");
//println!("{:#?}", &self.addr_to_import);
}
pub fn show_dll (&self, path: &PathBuf) -> Usually<()> {
let dll = self.path_to_pe.get(path).expect("no such library");
let ep_rva = dll.get_entrypoint()?;
let ep_off = dll.rva_to_offset(ep_rva)?;
println!("\n({:p} 0x{:x?} {:x?} {:x?}\n {path:?})",
dll.as_ptr(),
dll.get_image_base()?,
ep_rva,
ep_off);
Ok(())
}
pub fn show_calls (&self, path: &PathBuf, verbose: bool) -> Usually<()> {
let mut calls = 0;
let dll = self.path_to_pe.get(path).expect("no such library");
let buf = dll.get_buffer();
let section = dll.get_section_by_name(".text")?;
let section_ptr = section.pointer_to_raw_data.0 as usize;
let section_len = section.size_of_raw_data as usize;
let section_data = &buf[section_ptr..section_ptr+section_len];
let mut decoder = iced_x86::Decoder::with_ip(64, section_data, 0x1000, 0);
while decoder.can_decode() {
let position = decoder.position();
impl Dll {
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 instruction = decoder.decode();
let opcodes = &section_data[position..position+instruction.len()];
//println!("0x{position:08x} {opcodes:32} {instruction}");
if (instruction.flow_control() == iced_x86::FlowControl::IndirectBranch
|| instruction.flow_control() == iced_x86::FlowControl::IndirectCall)
&& instruction.op0_kind() == iced_x86::OpKind::Memory {
match opcodes[0] {
0xff => match opcodes[1] {
0x10 | 0x12 | 0x13 |
0x50 | 0x51 | 0x52 | 0x53 | 0x54 | 0x55 | 0x56 | 0x57 |
0x60 | 0x90 | 0x92 | 0x93 | 0x94 | 0x97 => continue,
_ => {},
},
0x41 | 0x42 | 0x43 | 0x49 => match opcodes[1] {
0xff => continue,
_ => {},
},
0x48 => match opcodes[2] {
0x20 | 0x60 | 0x62 | 0xa0 | 0xa2 => continue,
_ => {},
},
_ => {}
let opcodes = &self.code[addr..addr+instruction.len()].iter()
.map(|x|format!("{x:02x}"))
.join(" ");
println!("{BOLD}0x{addr:>08x}{RESET} {:20} {DIM}{opcodes}{RESET} {BOLD}{instruction}{RESET} {module:?} {method:?}", &self.name);
}
let offset = (position + section_ptr) as u32;
let offset_rva = dll.offset_to_rva(Offset(offset))?.0;
let call_target = match opcodes[0] {
0xff => match opcodes[1] {
0x15 | 0x25 =>
offset_rva + opcodes.len() as u32 + u32::from_le_bytes([
opcodes[2],
opcodes[3],
opcodes[4],
opcodes[5]
]),
_ => 0x0
},
0x48 => match opcodes[1] {
0xff => match opcodes[2] {
0x15 | 0x25 =>
offset_rva + opcodes.len() as u32 + u32::from_le_bytes([
opcodes[3],
opcodes[4],
opcodes[5],
opcodes[6]
]),
_ => 0x0
},
_ => 0x0
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
}));
}
_ => 0x0
};
let unknown = (String::from("unknown"), String::from("unknown"));
let external = format!("{}::{}",
self.addr_to_import.get(&call_target).unwrap_or(&unknown).0,
self.addr_to_import.get(&call_target).unwrap_or(&unknown).1);
let dependent = path.file_name().unwrap();
if verbose {
println!(" ({BOLD}{external}{RESET}\n Offset(0x{:08x}) RVA(R=0x{:08x})\n {:25} {:40} 0x{:08x}",
offset,
offset_rva,
opcodes.iter().map(|x|format!("{x:>02x}")).collect::<Vec<_>>().join(" "),
instruction,
call_target,
);
}
calls += 1;
println!("{BOLD}{:?}{RESET}", &self.code[b..c].hex_conf(HexConfig {
display_offset: self.code_base as usize + b, ..cfg
}));
if n > 0 {
println!("{DIM}{:?}{RESET}", &self.code[c..d].hex_conf(HexConfig {
display_offset: self.code_base as usize + c, ..cfg
}));
}
}
println!(" (calls {calls})");
Ok(())
}
}
//impl Vestal {
//pub fn show_addr_to_import (&self) {
//for (addr, (dll, export)) in self.addr_to_import.iter() {
//println!("{BOLD}0x{addr:>08x}{RESET} {dll:>20} {export:<40}");
//}
//}
//pub fn show_vst_entrypoint (&self, path: &PathBuf) {
////let exports = self.path_to_exports.get(path).expect("no exports");
////for export in exports.iter() {
////if export.name_string() == Some("VSTPluginMain".to_string()) {
////println!("{export:?}");
////println!("{}", export.name_string().unwrap());
////let addr = (export.addr() as usize);
////println!();
////println!();
////return Ok(())
////}
////}
////panic!("no main");
////println!("{:#?}", &self.addr_to_import);
//}
//pub fn show_dll (&self, path: &PathBuf) -> Usually<()> {
//let dll = self.path_to_pe.get(path).expect("no such library");
//let ep_rva = dll.get_entrypoint()?;
//let ep_off = dll.rva_to_offset(ep_rva)?;
//println!("\n({:p} 0x{:x?} {:x?} {:x?}\n {path:?})",
//dll.as_ptr(),
//dll.get_image_base()?,
//ep_rva,
//ep_off);
//Ok(())
//}
//pub fn show_calls (&self, path: &PathBuf, verbose: bool) -> Usually<()> {
//let mut calls = 0;
//let dll = self.path_to_pe.get(path).expect("no such library");
//let buf = dll.get_buffer();
//let section = dll.get_section_by_name(".text")?;
//let section_ptr = section.pointer_to_raw_data.0 as usize;
//let section_len = section.size_of_raw_data as usize;
//let section_data = &buf[section_ptr..section_ptr+section_len];
//let mut decoder = iced_x86::Decoder::with_ip(64, section_data, 0x1000, 0);
//while decoder.can_decode() {
//let position = decoder.position();
//let instruction = decoder.decode();
//let opcodes = &section_data[position..position+instruction.len()];
////println!("0x{position:08x} {opcodes:32} {instruction}");
//if (instruction.flow_control() == iced_x86::FlowControl::IndirectBranch
//|| instruction.flow_control() == iced_x86::FlowControl::IndirectCall)
//&& instruction.op0_kind() == iced_x86::OpKind::Memory {
//match opcodes[0] {
//0xff => match opcodes[1] {
//0x10 | 0x12 | 0x13 |
//0x50 | 0x51 | 0x52 | 0x53 | 0x54 | 0x55 | 0x56 | 0x57 |
//0x60 | 0x90 | 0x92 | 0x93 | 0x94 | 0x97 => continue,
//_ => {},
//},
//0x41 | 0x42 | 0x43 | 0x49 => match opcodes[1] {
//0xff => continue,
//_ => {},
//},
//0x48 => match opcodes[2] {
//0x20 | 0x60 | 0x62 | 0xa0 | 0xa2 => continue,
//_ => {},
//},
//_ => {}
//}
//let offset = (position + section_ptr) as u32;
//let offset_rva = dll.offset_to_rva(Offset(offset))?.0;
//let call_target = match opcodes[0] {
//0xff => match opcodes[1] {
//0x15 | 0x25 =>
//offset_rva + opcodes.len() as u32 + u32::from_le_bytes([
//opcodes[2],
//opcodes[3],
//opcodes[4],
//opcodes[5]
//]),
//_ => 0x0
//},
//0x48 => match opcodes[1] {
//0xff => match opcodes[2] {
//0x15 | 0x25 =>
//offset_rva + opcodes.len() as u32 + u32::from_le_bytes([
//opcodes[3],
//opcodes[4],
//opcodes[5],
//opcodes[6]
//]),
//_ => 0x0
//},
//_ => 0x0
//}
//_ => 0x0
//};
//let unknown = (String::from("unknown"), String::from("unknown"));
//let external = format!("{}::{}",
//self.addr_to_import.get(&call_target).unwrap_or(&unknown).0,
//self.addr_to_import.get(&call_target).unwrap_or(&unknown).1);
//let dependent = path.file_name().unwrap();
//if verbose {
//println!(" ({BOLD}{external}{RESET}\n Offset(0x{:08x}) RVA(R=0x{:08x})\n {:25} {:40} 0x{:08x}",
//offset,
//offset_rva,
//opcodes.iter().map(|x|format!("{x:>02x}")).collect::<Vec<_>>().join(" "),
//instruction,
//call_target,
//);
//}
//calls += 1;
//}
//}
//println!(" (calls {calls})");
//Ok(())
//}
//}