mirror of
https://codeberg.org/unspeaker/vestal.git
synced 2025-12-06 08:36:41 +01:00
sort of map of what needs to be linked
This commit is contained in:
parent
8a404c3bd5
commit
5b2a785829
3 changed files with 188 additions and 46 deletions
|
|
@ -3,64 +3,147 @@ use crate::*;
|
||||||
impl Module {
|
impl Module {
|
||||||
/// Collect all calls that point to imports.
|
/// Collect all calls that point to imports.
|
||||||
pub fn load_call_sites (self: Arc<Self>, recurse: bool) -> Usually<Arc<Self>> {
|
pub fn load_call_sites (self: Arc<Self>, recurse: bool) -> Usually<Arc<Self>> {
|
||||||
if self.verbose {
|
self.load_call_sites_slice(0, recurse.then_some(0))?;
|
||||||
println!(" {DIM}(load-call-sites){RESET}");
|
Ok(self)
|
||||||
}
|
}
|
||||||
let mut decoder = Decoder::with_ip(64, self.code.as_ref(), self.code_base as u64, 0);
|
/// Collect all calls that point to imports, starting from an address.
|
||||||
let mut targets: BTreeMap<u32, Vec<Arc<CallSite>>> = Default::default();
|
fn load_call_sites_slice (
|
||||||
|
self: &Arc<Self>,
|
||||||
|
start: usize,
|
||||||
|
recurse: Option<usize>,
|
||||||
|
) -> Usually<()> {
|
||||||
|
let group = false;
|
||||||
|
//if self.verbose {
|
||||||
|
//println!(" {DIM}(load-call-sites {} {} {} {RESET}",
|
||||||
|
//fmt_num(start),
|
||||||
|
//recurse.unwrap_or(0),
|
||||||
|
//self.name);
|
||||||
|
//}
|
||||||
|
let code = &self.code.as_ref()[start..];
|
||||||
|
//println!("{:x?}", &code[..64]);
|
||||||
|
let rip = self.code_base as u64 + start as u64;
|
||||||
|
let mut decoder = Decoder::with_ip(64, code, rip, 0);
|
||||||
while decoder.can_decode() {
|
while decoder.can_decode() {
|
||||||
let position = decoder.position();
|
let position = decoder.position();
|
||||||
let instruction = decoder.decode();
|
let instruction = decoder.decode();
|
||||||
let opcodes = &self.code[position..position+instruction.len()];
|
let opcodes = &code[position..position+instruction.len()];
|
||||||
if CallSite::matches(&instruction) && !CallSite::skip(opcodes) {
|
let address = self.code_base + start as u32 + position as u32;
|
||||||
let offset = (position + self.code_start) as u32;
|
//println!("{:15} {} {instruction} {}", self.name, fmt_num(address as usize), fmt_bytes(opcodes));
|
||||||
let source = self.pe.offset_to_rva(Offset((position + self.code_start) as u32))?.0;
|
// Ascend on RET
|
||||||
let target = CallSite::target(source, opcodes).unwrap_or(0);
|
if let Some(depth) = recurse {
|
||||||
let import = self.imports.read().unwrap().get(&target).cloned();
|
if depth > 0 && opcodes == &[0xc3] {
|
||||||
let call_site = Arc::new(CallSite {
|
break
|
||||||
caller: self.clone(),
|
}
|
||||||
source,
|
}
|
||||||
offset,
|
// Descend on CALL/JMP
|
||||||
target,
|
if CallSite::matches(&instruction) && !CallSite::skip(opcodes) {
|
||||||
length: opcodes.len(),
|
let call_site = self.call_site(start + position, opcodes)?;
|
||||||
method: import.clone().map(|x|x.1),
|
if !self.targets.read().unwrap().contains_key(&call_site.target) {
|
||||||
module: if let Some(import) = import {
|
Log::call_site(self.verbose && !group, &call_site, Some(opcodes), recurse);
|
||||||
self.dependencies.read().unwrap().get(&import.0).cloned()
|
}
|
||||||
} else {
|
self.load_call_site(&call_site);
|
||||||
None
|
if let Some(depth) = recurse {
|
||||||
}
|
self.load_call_site_recurse(&call_site, depth)?;
|
||||||
});
|
|
||||||
call_site.show(Some(opcodes));
|
|
||||||
if !targets.contains_key(&call_site.target) {
|
|
||||||
targets.insert(call_site.target, vec![]);
|
|
||||||
}
|
}
|
||||||
targets.get_mut(&call_site.target).unwrap().push(call_site);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(self)
|
//Log::call_sites(self.verbose && group, &self.name, &self.targets.read().unwrap(), recurse);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
/// Annotate a given instruction in the code section as a [CallSite].
|
||||||
fn parse_link (&self, link: &Arc<CallSite>) -> Option<u32> {
|
fn call_site (self: &Arc<Self>, position: usize, opcodes: &[u8]) -> Usually<Arc<CallSite>> {
|
||||||
None
|
let group = false;
|
||||||
//Some(*self.deps_by_library
|
let offset = (position + self.code_start) as u32;
|
||||||
//.get(link.module.as_ref()?)?
|
let source = self.pe.offset_to_rva(Offset((position + self.code_start) as u32))?.0;
|
||||||
//.get(link.method.as_ref()?)?)
|
let target = CallSite::target(source, opcodes).unwrap_or(0);
|
||||||
|
let import = self.imports.read().unwrap().get(&target).cloned();
|
||||||
|
let call_site = Arc::new(CallSite {
|
||||||
|
caller: self.clone(),
|
||||||
|
source,
|
||||||
|
offset,
|
||||||
|
target,
|
||||||
|
length: opcodes.len(),
|
||||||
|
method: import.clone().map(|x|x.1),
|
||||||
|
module: if let Some(import) = import {
|
||||||
|
self.dependencies.read().unwrap().get(&import.0).cloned()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Ok(call_site)
|
||||||
}
|
}
|
||||||
|
/// Add a [CallSite] under the appropriate target
|
||||||
pub fn resolve_forward (&self, rva: &RVA) -> Usually<Option<(Arc<str>, Arc<str>)>> {
|
fn load_call_site (&self, call_site: &Arc<CallSite>) {
|
||||||
let mut addr = (rva.0 - self.code_base) as usize;
|
let mut targets = self.targets.write().unwrap();
|
||||||
|
if !targets.contains_key(&call_site.target) {
|
||||||
|
targets.insert(call_site.target, vec![]);
|
||||||
|
}
|
||||||
|
targets.get_mut(&call_site.target).unwrap().push(call_site.clone());
|
||||||
|
}
|
||||||
|
/// Follow the call site and resolve the next call site found in the dependency.
|
||||||
|
fn load_call_site_recurse (self: &Arc<Self>, call_site: &Arc<CallSite>, depth: usize) -> Usually<()> {
|
||||||
|
let CallSite { module, method, .. } = call_site.as_ref();
|
||||||
|
if let (Some(module), Some(method)) = (module, method) {
|
||||||
|
if let Some(method) = module.exports.read().unwrap().get(method) {
|
||||||
|
module.load_call_site_recurse_export(method, depth)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
/// Follow a function call thunk.
|
||||||
|
fn load_call_site_recurse_export (self: &Arc<Self>, method: &ThunkData, depth: usize) -> Usually<()> {
|
||||||
|
match method {
|
||||||
|
ThunkData::Function(rva) => {
|
||||||
|
self.load_call_site_recurse_function(rva, depth + 1)?;
|
||||||
|
},
|
||||||
|
ThunkData::ForwarderString(rva) => {
|
||||||
|
self.load_call_site_recurse_forward(rva, depth + 1)?;
|
||||||
|
},
|
||||||
|
x => {
|
||||||
|
unimplemented!("{x:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
/// Follow a function call site.
|
||||||
|
fn load_call_site_recurse_function (self: &Arc<Self>, rva: &RVA, depth: usize) -> Usually<()> {
|
||||||
|
let index = (rva.0 - self.code_base) as usize;
|
||||||
|
let slice = &self.code[index..index+64];
|
||||||
|
self.load_call_sites_slice(index, Some(depth))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
/// Follow a forwarded call site.
|
||||||
|
fn load_call_site_recurse_forward (self: &Arc<Self>, rva: &RVA, depth: usize) -> Usually<()> {
|
||||||
|
if let Some((mut module_name, method_name)) = self.resolve_forward(rva)? {
|
||||||
|
let mut name = module_name.to_lowercase();
|
||||||
|
if !name.ends_with(".dll") {
|
||||||
|
name = format!("{name}.dll");
|
||||||
|
}
|
||||||
|
if let Some(module) = self.dependencies.read().unwrap().get(name.as_str()) {
|
||||||
|
if let Some(method) = module.exports.read().unwrap().get(&method_name) {
|
||||||
|
module.load_call_site_recurse_export(method, depth)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//panic!("{} {name}::{method_name}", self.name);
|
||||||
|
} else {
|
||||||
|
panic!("unresolved fwd {rva:x?} {}", self.read_forward(rva)?);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn read_forward (&self, rva: &RVA) -> Usually<Arc<str>> {
|
||||||
|
let mut address = self.pe.rva_to_offset(*rva)?.0 as usize;
|
||||||
let mut forward = vec![];
|
let mut forward = vec![];
|
||||||
while let Some(c) = self.pe.as_slice().get(addr) {
|
while let Some(c) = self.pe.as_slice().get(address) {
|
||||||
if *c == 0x00 {
|
if *c == 0x00 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
forward.push(*c);
|
forward.push(*c);
|
||||||
addr += 1;
|
address += 1;
|
||||||
}
|
}
|
||||||
Ok(String::from_utf8(forward)?
|
Ok(String::from_utf8(forward)?.as_str().into())
|
||||||
.as_str()
|
}
|
||||||
.split_once(".")
|
pub fn resolve_forward (&self, rva: &RVA) -> Usually<Option<(Arc<str>, Arc<str>)>> {
|
||||||
.map(|(x, y)|(x.into(), y.into())))
|
Ok(self.read_forward(rva)?.split_once(".").map(|(x, y)|(x.into(), y.into())))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,8 @@ pub struct Module {
|
||||||
pub dependencies: RwLock<BTreeMap<Arc<str>, Arc<Self>>>,
|
pub dependencies: RwLock<BTreeMap<Arc<str>, Arc<Self>>>,
|
||||||
/// Call targets to methods from imported modules.
|
/// Call targets to methods from imported modules.
|
||||||
pub imports: RwLock<BTreeMap<u32, (Arc<str>, Arc<str>)>>,
|
pub imports: RwLock<BTreeMap<u32, (Arc<str>, Arc<str>)>>,
|
||||||
|
/// Mapping of call target to its invocations.
|
||||||
|
pub targets: RwLock<BTreeMap<u32, Vec<Arc<CallSite>>>>,
|
||||||
/// Addresses of exported methods by name
|
/// Addresses of exported methods by name
|
||||||
pub exports: RwLock<BTreeMap<Arc<str>, ThunkData>>,
|
pub exports: RwLock<BTreeMap<Arc<str>, ThunkData>>,
|
||||||
/// Locations in `.text` section that need to be patched
|
/// Locations in `.text` section that need to be patched
|
||||||
|
|
@ -106,6 +108,7 @@ impl Module {
|
||||||
search_paths: Default::default(),
|
search_paths: Default::default(),
|
||||||
dependencies: Default::default(),
|
dependencies: Default::default(),
|
||||||
imports: Default::default(),
|
imports: Default::default(),
|
||||||
|
targets: Default::default(),
|
||||||
exports: Default::default(),
|
exports: Default::default(),
|
||||||
call_sites: Default::default(),
|
call_sites: Default::default(),
|
||||||
pe,
|
pe,
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,44 @@ impl Log {
|
||||||
println!("(found {} {:?})", name.as_ref(), path.as_ref());
|
println!("(found {} {:?})", name.as_ref(), path.as_ref());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn call_site (
|
||||||
|
show: bool,
|
||||||
|
call: &CallSite,
|
||||||
|
opcodes: Option<&[u8]>,
|
||||||
|
depth: Option<usize>,
|
||||||
|
) {
|
||||||
|
if show {
|
||||||
|
if let Some(depth) = depth {
|
||||||
|
for i in 0..depth {
|
||||||
|
print!(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
call.show(opcodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn call_sites (
|
||||||
|
show: bool,
|
||||||
|
name: &impl AsRef<str>,
|
||||||
|
targets: &BTreeMap<u32, Vec<Arc<CallSite>>>,
|
||||||
|
depth: Option<usize>
|
||||||
|
) {
|
||||||
|
for (target, call_sites) in targets.iter() {
|
||||||
|
if let Some(depth) = depth {
|
||||||
|
for i in 0..depth {
|
||||||
|
print!(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//println!(" (call {:15} {})", name.as_ref(), fmt_num(*target as usize));
|
||||||
|
for call_site in call_sites.iter() {
|
||||||
|
if let Some(depth) = depth {
|
||||||
|
for i in 0..depth {
|
||||||
|
print!(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
call_site.show(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Show {
|
impl Show {
|
||||||
|
|
@ -72,6 +110,24 @@ impl std::fmt::Debug for Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Module {
|
impl Module {
|
||||||
|
pub fn show_layout (&self) {
|
||||||
|
println!("\n█ = 64k");
|
||||||
|
for module in self.namespace.read().unwrap().values() {
|
||||||
|
print!("\n{} ", module.name);
|
||||||
|
let page_size = 256;
|
||||||
|
for i in 0..(module.code_start + module.code_size) / page_size {
|
||||||
|
if i % 64 == 0 {
|
||||||
|
print!("\n");
|
||||||
|
}
|
||||||
|
if i * page_size < module.code_start {
|
||||||
|
print!("░");
|
||||||
|
} else {
|
||||||
|
print!("▒");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print!("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn show_instruction (&self, addr: usize) {
|
pub fn show_instruction (&self, addr: usize) {
|
||||||
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();
|
||||||
|
|
@ -132,9 +188,9 @@ impl Module {
|
||||||
impl CallSite {
|
impl CallSite {
|
||||||
pub fn show (&self, opcodes: Option<&[u8]>) {
|
pub fn show (&self, opcodes: Option<&[u8]>) {
|
||||||
let label = self.caller.imports.read().unwrap().get(&self.target)
|
let label = self.caller.imports.read().unwrap().get(&self.target)
|
||||||
.map(|(module, method)|format!("{module}::{method}"))
|
.map(|(module, method)|format!("{GREEN}{module}::{method}{RESET}"))
|
||||||
.unwrap_or_else(||format!("{RED}unresolved{RESET}"));
|
.unwrap_or_else(||format!("{RED}unresolved{RESET}"));
|
||||||
println!(" ╰-> (call {} {} {} {DIM}{:20}{RESET} {} {BOLD}{}{RESET})",
|
println!(" ╰-> (call {:15} {} {} {DIM}{:20}{RESET} {} {BOLD}{}{RESET})",
|
||||||
&self.caller.name,
|
&self.caller.name,
|
||||||
fmt_num(self.offset as usize),
|
fmt_num(self.offset as usize),
|
||||||
fmt_num(self.source as usize),
|
fmt_num(self.source as usize),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue