use std::error::Error; use libloading::os::unix::{Library, Symbol}; use retour::Function; use tracing::warn; use crate::tricks::find_argv0; pub fn find_symbol(name: &str) -> Result, Box> { // try to find it among exported symbols let this = Library::this(); // TODO don't reopen it every time let sym : Result, libloading::Error> = unsafe { this.get(name.as_bytes()) }; if let Ok(s) = sym { return Ok(Some(*s)); } // try to read it from executable's elf match find_argv0() { None => warn!("could not find argv0 for process"), Some(exec) => match procmaps::map_addr_path(&exec)? { None => warn!("could not find base addr of process image"), Some((base, path)) => match exec::offset_in_elf(&path, &name)? { None => warn!("could not locate requested symbol in ELF"), Some(offset) => { let addr : *const () = (base + offset) as *const (); return Ok(Some(unsafe { Function::from_ptr(addr) } )); } } } } Ok(None) } pub mod exec { use std::path::Path; use elf::{ParseError, ElfBytes, endian::AnyEndian}; use tracing::{warn, debug}; pub fn offset_in_elf(path: &Path, symbol: &str) -> Result, ParseError> { let exec_data = std::fs::read(path)?; let headers = ElfBytes::::minimal_parse(&exec_data)?; let common = headers.find_common_data()?; // first try with hash table match common.sysv_hash { None => warn!("missing symbols hash table in ELF"), Some(sysv_hash) => match common.dynsyms { None => warn!("missing dynamic symbols in ELF"), Some(dynsyms) => match common.dynsyms_strs { None => warn!("missing string tab for dynamic symbols in ELF"), Some(dynsyms_strs) => match sysv_hash.find(symbol.as_bytes(), &dynsyms, &dynsyms_strs)? { None => debug!("could not find symbol {} in ELF hashmap", symbol), Some((_id, sym)) => return Ok(Some(sym.st_value as usize)), }, }, }, } // fall back to iterating symbols table match common.symtab { None => warn!("missing symbols table in ELF"), Some(symtab) => match common.symtab_strs { None => warn!("missing names for symbols in ELF"), Some(symtab_strs) => { for sym in symtab { let name = symtab_strs.get(sym.st_name as usize)?; if name == symbol { return Ok(Some(sym.st_value as usize)); } } debug!("no symbol matched '{}'", symbol); } } } Ok(None) } } pub mod procmaps { use std::path::PathBuf; use proc_maps::get_process_maps; use tracing::{debug, warn}; use crate::tricks::fmt_path; pub fn map_addr_path(name: &str) -> std::io::Result> { let proc_maps = get_process_maps(std::process::id() as i32)?; for map in proc_maps { debug!("map > 0x{:08X} {} [{:x}] - {} [{}]", map.start(), map.flags, map.offset, map.inode, fmt_path(map.filename())); if map.is_exec() { if let Some(path) = map.filename() { if path.ends_with(name) { return Ok(Some((map.start() - map.offset, map.filename().unwrap().to_path_buf()))); } } } } warn!("could not find address of '{}'", name); Ok(None) } }