diff options
Diffstat (limited to 'rust/examples/pdb-ng/src/symbol_parser.rs')
| -rw-r--r-- | rust/examples/pdb-ng/src/symbol_parser.rs | 2061 |
1 files changed, 0 insertions, 2061 deletions
diff --git a/rust/examples/pdb-ng/src/symbol_parser.rs b/rust/examples/pdb-ng/src/symbol_parser.rs deleted file mode 100644 index 7a90c4ab..00000000 --- a/rust/examples/pdb-ng/src/symbol_parser.rs +++ /dev/null @@ -1,2061 +0,0 @@ -// Copyright 2022-2024 Vector 35 Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::collections::{BTreeMap, HashMap, HashSet}; -use std::mem; -use std::sync::OnceLock; - -use anyhow::{anyhow, Result}; -use itertools::Itertools; -use pdb::register::Register::{AMD64, X86}; -use pdb::register::{AMD64Register, X86Register}; -use pdb::Error::UnimplementedSymbolKind; -use pdb::{ - AnnotationReferenceSymbol, BasePointerRelativeSymbol, BlockSymbol, BuildInfoSymbol, - CallSiteInfoSymbol, CompileFlagsSymbol, ConstantSymbol, DataReferenceSymbol, DataSymbol, - DefRangeFramePointerRelativeFullScopeSymbol, DefRangeFramePointerRelativeSymbol, - DefRangeRegisterRelativeSymbol, DefRangeRegisterSymbol, DefRangeSubFieldRegisterSymbol, - DefRangeSubFieldSymbol, DefRangeSymbol, ExportSymbol, FallibleIterator, FrameProcedureSymbol, - InlineSiteSymbol, LabelSymbol, LocalSymbol, MultiRegisterVariableSymbol, ObjNameSymbol, - ProcedureReferenceSymbol, ProcedureSymbol, PublicSymbol, RegisterRelativeSymbol, - RegisterVariableSymbol, Rva, SeparatedCodeSymbol, Source, Symbol, SymbolData, SymbolIndex, - SymbolIter, ThreadStorageSymbol, ThunkSymbol, TrampolineSymbol, TypeIndex, - UserDefinedTypeSymbol, UsingNamespaceSymbol, -}; - -use binaryninja::architecture::{Architecture, ArchitectureExt, Register}; -use binaryninja::binaryview::BinaryViewBase; -use binaryninja::demangle::demangle_ms; -use binaryninja::rc::Ref; -use binaryninja::types::{ - max_confidence, min_confidence, Conf, ConfMergable, FunctionParameter, QualifiedName, - StructureBuilder, Type, TypeClass, Variable, VariableSourceType, -}; - -use crate::PDBParserInstance; - -const DEMANGLE_CONFIDENCE: u8 = 32; - -/// Parsed Data Symbol like globals, etc -#[derive(Debug, Clone)] -pub struct SymbolNames { - pub raw_name: String, - pub short_name: Option<String>, - pub full_name: Option<String>, -} - -/// Parsed Data Symbol like globals, etc -#[derive(Debug, Clone)] -pub struct ParsedDataSymbol { - /// If the symbol comes from the public symbol list (lower quality) - pub is_public: bool, - /// Absolute address in bv - pub address: u64, - /// Symbol name - pub name: SymbolNames, - /// Type if known - pub type_: Option<Conf<Ref<Type>>>, -} - -/// Parsed functions and function-y symbols -#[derive(Debug, Clone)] -pub struct ParsedProcedure { - /// If the symbol comes from the public symbol list (lower quality) - pub is_public: bool, - /// Absolute address in bv - pub address: u64, - /// Symbol name - pub name: SymbolNames, - /// Function type if known - pub type_: Option<Conf<Ref<Type>>>, - /// List of local variables (TODO: use these) - pub locals: Vec<ParsedVariable>, -} - -/// Structure with some information about a procedure -#[derive(Debug, Clone)] -pub struct ParsedProcedureInfo { - /// Known parameters for the procedure - pub params: Vec<ParsedVariable>, - /// Known local variables for the procedure - pub locals: Vec<ParsedVariable>, -} - -/// One parsed variable / parameter -#[derive(Debug, Clone)] -pub struct ParsedVariable { - /// Variable name - pub name: String, - /// Variable type if known - pub type_: Option<Conf<Ref<Type>>>, - /// Location(s) where the variable is stored. PDB lets you store a variable in multiple locations - /// despite binja not really understanding that. Length is probably never zero - pub storage: Vec<ParsedLocation>, - /// Do we think this is a parameter - pub is_param: bool, -} - -#[derive(Debug, Copy, Clone)] -pub struct ParsedLocation { - /// Location information - pub location: Variable, - /// Is the storage location relative to the base pointer? See [ParsedProcedureInfo.frame_offset] - pub base_relative: bool, - /// Is the storage location relative to the stack pointer? - pub stack_relative: bool, -} - -/// Big enum of all the types of symbols we know how to parse -#[derive(Debug, Clone)] -pub enum ParsedSymbol { - /// Parsed Data Symbol like globals, etc - Data(ParsedDataSymbol), - /// Parsed functions and function-y symbols - Procedure(ParsedProcedure), - /// Structure with some information about a procedure - ProcedureInfo(ParsedProcedureInfo), - /// One parsed variable / parameter - LocalVariable(ParsedVariable), - /// Location of a local variable - Location(ParsedLocation), -} - -/// This is all done in the parser instance namespace because the lifetimes are impossible to -/// wrangle otherwise. -impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { - pub fn parse_symbols( - &mut self, - progress: Box<dyn Fn(usize, usize) -> Result<()> + '_>, - ) -> Result<(Vec<ParsedSymbol>, Vec<ParsedSymbol>)> { - let mut module_count = 0usize; - let dbg = self.pdb.debug_information()?; - let mut modules = dbg.modules()?; - while let Some(_module) = modules.next()? { - module_count += 1; - } - - let global_symbols = self.pdb.global_symbols()?; - let symbols = global_symbols.iter(); - let parsed = self.parse_mod_symbols(symbols)?; - for sym in parsed { - match &sym { - ParsedSymbol::Data(ParsedDataSymbol { - name: SymbolNames { raw_name, .. }, - .. - }) - | ParsedSymbol::Procedure(ParsedProcedure { - name: SymbolNames { raw_name, .. }, - .. - }) => { - self.parsed_symbols_by_name - .insert(raw_name.clone(), self.parsed_symbols.len()); - } - _ => {} - } - self.parsed_symbols.push(sym); - } - - (progress)(1, module_count + 1)?; - - let dbg = self.pdb.debug_information()?; - let mut modules = dbg.modules()?; - let mut i = 0; - while let Some(module) = modules.next()? { - i += 1; - (progress)(i + 1, module_count + 1)?; - - self.log(|| { - format!( - "Module {} {}", - module.module_name(), - module.object_file_name() - ) - }); - if let Some(module_info) = self.pdb.module_info(&module)? { - let symbols = module_info.symbols()?; - let parsed = self.parse_mod_symbols(symbols)?; - for sym in parsed { - match &sym { - ParsedSymbol::Data(ParsedDataSymbol { - name: SymbolNames { raw_name, .. }, - .. - }) - | ParsedSymbol::Procedure(ParsedProcedure { - name: SymbolNames { raw_name, .. }, - .. - }) => { - self.parsed_symbols_by_name - .insert(raw_name.clone(), self.parsed_symbols.len()); - } - _ => {} - } - self.parsed_symbols.push(sym); - } - } - } - - let use_public = - self.settings - .get_bool("pdb.features.loadGlobalSymbols", Some(self.bv), None); - - let mut best_symbols = HashMap::<String, &ParsedSymbol>::new(); - for sym in &self.parsed_symbols { - match sym { - ParsedSymbol::Data(ParsedDataSymbol { - is_public, - address, - name: - SymbolNames { - raw_name, - full_name, - .. - }, - type_, - .. - }) => { - if *is_public && !use_public { - continue; - } - - let this_confidence = match type_ { - Some(Conf { confidence, .. }) => *confidence, - _ => min_confidence(), - }; - let (new_better, old_exists) = match best_symbols.get(raw_name) { - Some(ParsedSymbol::Data(ParsedDataSymbol { - type_: - Some(Conf { - confidence: old_conf, - .. - }), - .. - })) => (this_confidence > *old_conf, true), - Some(ParsedSymbol::Data(ParsedDataSymbol { type_: None, .. })) => { - (true, true) - } - Some(..) => (false, true), - _ => (true, false), - }; - if new_better { - self.log(|| { - format!( - "New best symbol (at 0x{:x}) for `{}` / `{}`: {:?}", - *address, - raw_name, - full_name.as_ref().unwrap_or(raw_name), - sym - ) - }); - if old_exists { - self.log(|| format!("Clobbering old definition")); - } - best_symbols.insert(raw_name.clone(), sym); - } - } - _ => {} - } - } - - let mut best_functions = HashMap::<String, &ParsedSymbol>::new(); - for sym in &self.parsed_symbols { - match sym { - ParsedSymbol::Procedure(ParsedProcedure { - is_public, - address, - name: - SymbolNames { - raw_name, - full_name, - .. - }, - type_, - .. - }) => { - if *is_public && !use_public { - continue; - } - - let this_confidence = match type_ { - Some(Conf { confidence, .. }) => *confidence, - _ => min_confidence(), - }; - let (new_better, old_exists) = match best_functions.get(raw_name) { - Some(ParsedSymbol::Procedure(ParsedProcedure { - type_: - Some(Conf { - confidence: old_conf, - .. - }), - .. - })) => (this_confidence > *old_conf, true), - Some(ParsedSymbol::Procedure(ParsedProcedure { type_: None, .. })) => { - (true, true) - } - Some(..) => (false, true), - _ => (true, false), - }; - if new_better { - self.log(|| { - format!( - "New best function (at 0x{:x}) for `{}` / `{}`: {:?}", - *address, - raw_name, - full_name.as_ref().unwrap_or(raw_name), - sym - ) - }); - if old_exists { - self.log(|| format!("Clobbering old definition")); - } - best_functions.insert(raw_name.clone(), sym); - } - } - _ => {} - } - } - - Ok(( - best_symbols - .into_iter() - .map(|(_, sym)| sym.clone()) - .sorted_by_key(|sym| match sym { - ParsedSymbol::Data(ParsedDataSymbol { - type_, is_public, .. - }) => type_ - .as_ref() - .map(|ty| { - if *is_public { - ty.confidence / 2 - } else { - ty.confidence - } - }) - .unwrap_or(0), - ParsedSymbol::Procedure(ParsedProcedure { - type_, is_public, .. - }) => type_ - .as_ref() - .map(|ty| { - if *is_public { - ty.confidence / 2 - } else { - ty.confidence - } - }) - .unwrap_or(0), - _ => 0, - }) - .collect::<Vec<_>>(), - best_functions - .into_iter() - .map(|(_, func)| func.clone()) - .sorted_by_key(|sym| match sym { - ParsedSymbol::Data(ParsedDataSymbol { - type_, is_public, .. - }) => type_ - .as_ref() - .map(|ty| { - if *is_public { - ty.confidence / 2 - } else { - ty.confidence - } - }) - .unwrap_or(0), - ParsedSymbol::Procedure(ParsedProcedure { - type_, is_public, .. - }) => type_ - .as_ref() - .map(|ty| { - if *is_public { - ty.confidence / 2 - } else { - ty.confidence - } - }) - .unwrap_or(0), - _ => 0, - }) - .collect::<Vec<_>>(), - )) - } - - /// Parse all the symbols in a module, via the given SymbolIter - pub fn parse_mod_symbols(&mut self, mut symbols: SymbolIter) -> Result<Vec<ParsedSymbol>> { - // Collect tree structure first - let mut first = None; - let mut last_local = None; - let mut top_level_syms = vec![]; - let mut thunk_syms = vec![]; - let mut unparsed_syms = BTreeMap::new(); - while let Some(sym) = symbols.next()? { - if first.is_none() { - first = Some(sym.index()); - } - unparsed_syms.insert(sym.index(), sym); - - let p = sym.parse(); - self.log(|| format!("Parsed: {:x?}", p)); - - // It's some sort of weird tree structure where SOME symbols have "end" indices - // and anything between them and that index is a child symbol - // Sometimes there are "end scope" symbols at those end indices but like, sometimes - // there aren't? Which makes that entire system seem pointless (or I'm just missing - // something and it makes sense to _someone_) - if let Some(&(start, _end)) = self.symbol_stack.last() { - self.add_symbol_child(start, sym.index()); - } else { - // Place thunk symbols in their own list at the end, so they can reference - // other symbols parsed in the module - match &p { - Ok(SymbolData::Thunk(_)) => { - thunk_syms.push(sym.index()); - } - _ => { - top_level_syms.push(sym.index()); - } - } - } - let mut popped = false; - while let Some(&(_start, end)) = self.symbol_stack.last() { - if sym.index().0 >= end.0 { - let _ = self.symbol_stack.pop(); - popped = true; - } else { - break; - } - } - - // These aren't actually used for parsing (I don't trust them) but we can include a little - // debug error check here and see if it's ever actually wrong - match p { - Ok(SymbolData::ScopeEnd) | Ok(SymbolData::InlineSiteEnd) if popped => {} - Ok(SymbolData::ScopeEnd) | Ok(SymbolData::InlineSiteEnd) if !popped => { - self.log(|| format!("Did not pop at a scope end??? WTF??")); - } - _ if popped => { - self.log(|| format!("Popped but not at a scope end??? WTF??")); - } - _ => {} - } - - // Push new scopes on the stack to build the tree - match p { - Ok(SymbolData::Procedure(data)) => { - self.symbol_stack.push((sym.index(), data.end)); - } - Ok(SymbolData::InlineSite(data)) => { - self.symbol_stack.push((sym.index(), data.end)); - } - Ok(SymbolData::Block(data)) => { - self.symbol_stack.push((sym.index(), data.end)); - } - Ok(SymbolData::Thunk(data)) => { - self.symbol_stack.push((sym.index(), data.end)); - } - Ok(SymbolData::SeparatedCode(data)) => { - self.symbol_stack.push((sym.index(), data.end)); - } - Ok(SymbolData::FrameProcedure(..)) => { - if let Some(&(_, proc_end)) = self.symbol_stack.last() { - self.symbol_stack.push((sym.index(), proc_end)); - } - } - Ok(SymbolData::Local(..)) => { - last_local = Some(sym.index()); - } - Ok(SymbolData::DefRange(..)) - | Ok(SymbolData::DefRangeSubField(..)) - | Ok(SymbolData::DefRangeRegister(..)) - | Ok(SymbolData::DefRangeFramePointerRelative(..)) - | Ok(SymbolData::DefRangeFramePointerRelativeFullScope(..)) - | Ok(SymbolData::DefRangeSubFieldRegister(..)) - | Ok(SymbolData::DefRangeRegisterRelative(..)) => { - // I'd like to retract my previous statement that someone could possibly - // understand this: - // These symbol types impact the previous symbol, if it was a local - // BUT ALSO!! PART III REVENGE OF THE SYM-TH: You can have more than one of - // these and they all (?? it's undocumented) apply to the last local, PROBABLY - if let Some(last) = last_local { - self.add_symbol_child(last, sym.index()); - } else { - self.log(|| format!("Found def range with no last local: {:?}", p)); - } - } - _ => {} - } - } - assert!(self.symbol_stack.is_empty()); - // Add thunks at the end as per above - top_level_syms.extend(thunk_syms.into_iter()); - - // Restart and do the processing for real this time - if let Some(first) = first { - symbols.seek(first); - } - - let mut final_symbols = HashSet::new(); - - for root_idx in top_level_syms { - for child_idx in self.walk_children(root_idx).into_iter() { - let &sym = unparsed_syms - .get(&child_idx) - .expect("should have parsed this"); - - self.log(|| format!("Symbol {:?} ", sym.index())); - let (name, address) = - if let Some(parsed) = self.handle_symbol_index(sym.index(), &sym)? { - final_symbols.insert(sym.index()); - match parsed { - ParsedSymbol::Data(ParsedDataSymbol { name, address, .. }) => { - (Some(name.clone()), Some(*address)) - } - ParsedSymbol::Procedure(ParsedProcedure { name, address, .. }) => { - (Some(name.clone()), Some(*address)) - } - _ => (None, None), - } - } else { - (None, None) - }; - - if let Some(name) = name { - self.named_symbols.insert(name.raw_name, sym.index()); - } - if let Some(address) = address { - if !self.addressed_symbols.contains_key(&address) { - self.addressed_symbols.insert(address, vec![]); - } - self.addressed_symbols - .get_mut(&address) - .expect("just created this") - .push( - self.indexed_symbols - .get(&sym.index()) - .ok_or_else(|| anyhow!("Can't find sym {} ?", sym.index()))? - .clone(), - ); - } - } - } - - let filtered_symbols = mem::replace(&mut self.indexed_symbols, BTreeMap::new()) - .into_iter() - .filter_map(|(idx, sym)| { - if final_symbols.contains(&idx) { - Some(sym) - } else { - None - } - }) - .collect::<Vec<_>>(); - - // The symbols overlap between modules or something, so we can't keep this info around - self.symbol_tree.clear(); - self.module_cpu_type = None; - - Ok(filtered_symbols) - } - - /// Set a symbol to be the parent of another, building the symbol tree - fn add_symbol_child(&mut self, parent: SymbolIndex, child: SymbolIndex) { - if let Some(tree) = self.symbol_tree.get_mut(&parent) { - tree.push(child); - } else { - self.symbol_tree.insert(parent, Vec::from([child])); - } - - self.symbol_parents.insert(child, parent); - } - - /// Postorder traversal of children of symbol index (only during this module parse) - fn walk_children(&self, sym: SymbolIndex) -> Vec<SymbolIndex> { - let mut children = vec![]; - - if let Some(tree) = self.symbol_tree.get(&sym) { - for &child in tree { - children.extend(self.walk_children(child).into_iter()); - } - } - - children.push(sym); - return children; - } - - /// Direct children of symbol index (only during this module parse) - fn symbol_children(&self, sym: SymbolIndex) -> Vec<SymbolIndex> { - if let Some(tree) = self.symbol_tree.get(&sym) { - tree.clone() - } else { - vec![] - } - } - - /// Direct parent of symbol index (only during this module parse) - #[allow(dead_code)] - fn symbol_parent(&self, sym: SymbolIndex) -> Option<SymbolIndex> { - self.symbol_parents.get(&sym).map(|idx| *idx) - } - - /// Find symbol by index (only during this module parse) - fn lookup_symbol(&self, sym: &SymbolIndex) -> Option<&ParsedSymbol> { - self.indexed_symbols.get(sym) - } - - /// Parse a new symbol by its index - fn handle_symbol_index( - &mut self, - idx: SymbolIndex, - sym: &Symbol, - ) -> Result<Option<&ParsedSymbol>> { - if let None = self.indexed_symbols.get(&idx) { - match sym.parse() { - Ok(data) => match self.handle_symbol(idx, &data) { - Ok(Some(parsed)) => { - self.log(|| format!("Symbol {} parsed into: {:?}", idx, parsed)); - self.indexed_symbols.insert(idx, parsed); - } - Ok(None) => {} - e => { - self.log(|| format!("Error parsing symbol {}: {:?}", idx, e)); - } - }, - Err(UnimplementedSymbolKind(k)) => { - self.log(|| format!("Not parsing unimplemented symbol {}: kind {:x?}", idx, k)); - } - Err(e) => { - self.log(|| format!("Could not parse symbol: {}: {}", idx, e)); - } - }; - } - - Ok(self.indexed_symbols.get(&idx)) - } - - /// Parse a new symbol's data - fn handle_symbol( - &mut self, - index: SymbolIndex, - data: &SymbolData, - ) -> Result<Option<ParsedSymbol>> { - match data { - SymbolData::ScopeEnd => self.handle_scope_end_symbol(index), - SymbolData::ObjName(data) => self.handle_obj_name_symbol(index, &data), - SymbolData::RegisterVariable(data) => { - self.handle_register_variable_symbol(index, &data) - } - SymbolData::Constant(data) => self.handle_constant_symbol(index, &data), - SymbolData::UserDefinedType(data) => self.handle_user_defined_type_symbol(index, &data), - SymbolData::MultiRegisterVariable(data) => { - self.handle_multi_register_variable_symbol(index, &data) - } - SymbolData::Data(data) => self.handle_data_symbol(index, &data), - SymbolData::Public(data) => self.handle_public_symbol(index, &data), - SymbolData::Procedure(data) => self.handle_procedure_symbol(index, &data), - SymbolData::ThreadStorage(data) => self.handle_thread_storage_symbol(index, &data), - SymbolData::CompileFlags(data) => self.handle_compile_flags_symbol(index, &data), - SymbolData::UsingNamespace(data) => self.handle_using_namespace_symbol(index, &data), - SymbolData::ProcedureReference(data) => { - self.handle_procedure_reference_symbol(index, &data) - } - SymbolData::DataReference(data) => self.handle_data_reference_symbol(index, &data), - SymbolData::AnnotationReference(data) => { - self.handle_annotation_reference_symbol(index, &data) - } - SymbolData::Trampoline(data) => self.handle_trampoline_symbol(index, &data), - SymbolData::Export(data) => self.handle_export_symbol(index, &data), - SymbolData::Local(data) => self.handle_local_symbol(index, &data), - SymbolData::BuildInfo(data) => self.handle_build_info_symbol(index, &data), - SymbolData::InlineSite(data) => self.handle_inline_site_symbol(index, &data), - SymbolData::InlineSiteEnd => self.handle_inline_site_end_symbol(index), - SymbolData::ProcedureEnd => self.handle_procedure_end_symbol(index), - SymbolData::Label(data) => self.handle_label_symbol(index, &data), - SymbolData::Block(data) => self.handle_block_symbol(index, &data), - SymbolData::RegisterRelative(data) => { - self.handle_register_relative_symbol(index, &data) - } - SymbolData::Thunk(data) => self.handle_thunk_symbol(index, &data), - SymbolData::SeparatedCode(data) => self.handle_separated_code_symbol(index, &data), - SymbolData::DefRange(data) => self.handle_def_range(index, &data), - SymbolData::DefRangeSubField(data) => self.handle_def_range_sub_field(index, &data), - SymbolData::DefRangeRegister(data) => self.handle_def_range_register(index, &data), - SymbolData::DefRangeFramePointerRelative(data) => { - self.handle_def_range_frame_pointer_relative_symbol(index, &data) - } - SymbolData::DefRangeFramePointerRelativeFullScope(data) => { - self.handle_def_range_frame_pointer_relative_full_scope_symbol(index, &data) - } - SymbolData::DefRangeSubFieldRegister(data) => { - self.handle_def_range_sub_field_register_symbol(index, &data) - } - SymbolData::DefRangeRegisterRelative(data) => { - self.handle_def_range_register_relative_symbol(index, &data) - } - SymbolData::BasePointerRelative(data) => { - self.handle_base_pointer_relative_symbol(index, &data) - } - SymbolData::FrameProcedure(data) => self.handle_frame_procedure_symbol(index, &data), - SymbolData::CallSiteInfo(data) => self.handle_call_site_info(index, &data), - e => Err(anyhow!("Unhandled symbol type {:?}", e)), - } - } - - fn handle_scope_end_symbol(&mut self, _index: SymbolIndex) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got ScopeEnd symbol")); - Ok(None) - } - - fn handle_obj_name_symbol( - &mut self, - _index: SymbolIndex, - data: &ObjNameSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got ObjName symbol: {:?}", data)); - Ok(None) - } - - fn handle_register_variable_symbol( - &mut self, - _index: SymbolIndex, - data: &RegisterVariableSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got RegisterVariable symbol: {:?}", data)); - - let storage = if let Some(reg) = self.convert_register(data.register) { - vec![ParsedLocation { - location: Variable { - t: VariableSourceType::RegisterVariableSourceType, - index: 0, - storage: reg, - }, - base_relative: false, - stack_relative: false, - }] - } else { - // TODO: What do we do here? - vec![] - }; - - Ok(Some(ParsedSymbol::LocalVariable(ParsedVariable { - name: data.name.to_string().to_string(), - type_: self.lookup_type_conf(&data.type_index, false)?, - storage, - is_param: data.slot.map_or(true, |slot| slot > 0), - }))) - } - - fn handle_constant_symbol( - &mut self, - _index: SymbolIndex, - data: &ConstantSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got Constant symbol: {:?}", data)); - Ok(None) - } - - fn handle_user_defined_type_symbol( - &mut self, - _index: SymbolIndex, - data: &UserDefinedTypeSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got UserDefinedType symbol: {:?}", data)); - Ok(None) - } - - fn handle_multi_register_variable_symbol( - &mut self, - _index: SymbolIndex, - data: &MultiRegisterVariableSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got MultiRegisterVariable symbol: {:?}", data)); - Ok(None) - } - - fn handle_data_symbol( - &mut self, - _index: SymbolIndex, - data: &DataSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got Data symbol: {:?}", data)); - - let rva = data.offset.to_rva(&self.address_map).unwrap_or_default(); - let raw_name = data.name.to_string().to_string(); - let (t, name) = self.demangle_to_type(&raw_name, rva)?; - let name = name.map(|n| n.string()); - - // Sometimes the demangler REALLY knows what type this is supposed to be, and the - // data symbol is actually wrong. So in those cases, let the demangler take precedence - // Otherwise-- the demangler is usually wrong and clueless - let data_type = t.merge(self.lookup_type_conf(&data.type_index, false)?); - - // Ignore symbols with no name and no type - if !self - .settings - .get_bool("pdb.features.allowUnnamedVoidSymbols", Some(self.bv), None) - && name.is_none() - { - if let Some(ty) = &data_type { - if ty.contents.type_class() == TypeClass::VoidTypeClass { - return Ok(None); - } - } else { - return Ok(None); - } - } - - let name = SymbolNames { - raw_name, - short_name: name.clone(), - full_name: name, - }; - - self.log(|| { - format!( - "DATA: 0x{:x}: {:?} {:?}", - self.bv.start() + rva.0 as u64, - &name, - &data_type - ) - }); - - Ok(Some(ParsedSymbol::Data(ParsedDataSymbol { - is_public: false, - address: self.bv.start() + rva.0 as u64, - name, - type_: data_type, - }))) - } - - fn handle_public_symbol( - &mut self, - _index: SymbolIndex, - data: &PublicSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got Public symbol: {:?}", data)); - let rva = data.offset.to_rva(&self.address_map).unwrap_or_default(); - let raw_name = data.name.to_string().to_string(); - let (t, name) = self.demangle_to_type(&raw_name, rva)?; - let name = name.map(|n| n.string()); - - let name = SymbolNames { - raw_name, - short_name: name.clone(), - full_name: name, - }; - - // These are generally low confidence because we only have the demangler to inform us of type - - if data.function { - self.log(|| { - format!( - "PUBLIC FUNCTION: 0x{:x}: {:?} {:?}", - self.bv.start() + rva.0 as u64, - &name, - t - ) - }); - - Ok(Some(ParsedSymbol::Procedure(ParsedProcedure { - is_public: true, - address: self.bv.start() + rva.0 as u64, - name, - type_: t, - locals: vec![], - }))) - } else { - self.log(|| { - format!( - "PUBLIC DATA: 0x{:x}: {:?} {:?}", - self.bv.start() + rva.0 as u64, - &name, - t - ) - }); - - Ok(Some(ParsedSymbol::Data(ParsedDataSymbol { - is_public: true, - address: self.bv.start() + rva.0 as u64, - name, - type_: t, - }))) - } - } - - /// Given a proc symbol index and guessed type (from demangler or tpi), find all the local variables - /// and parameters related to that symbol. - /// Returns Ok(Some((resolved params, locals)))) - fn lookup_locals( - &self, - index: SymbolIndex, - type_index: TypeIndex, - demangled_type: Option<Conf<Ref<Type>>>, - ) -> Result<(Option<Conf<Ref<Type>>>, Vec<ParsedVariable>)> { - // So generally speaking, here's the information we have: - // - The function type is usually accurate wrt the parameter locations - // - The parameter symbols have the names we want for the params - // - The parameter symbols are a big ugly mess - // We basically want to take the function type from the type, and just fill in the - // names of all the parameters. Non-param locals don't really matter since binja - // can't handle them anyway. - - // Type parameters order needs to be like this: - // 1. `this` pointer (if exists) - // 2. Various stack params - // 3. Various register params - // We assume that if a parameter is found in a register, that is where it is passed. - // Otherwise they are in the default order as per the CC - - // Get child objects and search for local variable names - let mut locals = vec![]; - let mut params = vec![]; - let mut known_frame = false; - for child in self.symbol_children(index) { - match self.lookup_symbol(&child) { - Some(ParsedSymbol::ProcedureInfo(info)) => { - params = info.params.clone(); - locals = info.locals.clone(); - known_frame = true; - } - _ => {} - } - } - - let raw_type = self.lookup_type_conf(&type_index, false)?; - let fancy_type = self.lookup_type_conf(&type_index, true)?; - - // Best guess so far in case of error handling - let fancier_type = fancy_type - .clone() - .merge(raw_type.clone()) - .merge(demangled_type.clone()); - - if !known_frame { - return Ok((fancier_type, vec![])); - } - - // We need both of these to exist (not sure why they wouldn't) - let (raw_type, fancy_type) = match (raw_type, fancy_type) { - (Some(raw), Some(fancy)) => (raw, fancy), - _ => return Ok((fancier_type, vec![])), - }; - - let raw_params = raw_type - .contents - .parameters() - .map_err(|_| anyhow!("no params"))?; - let mut fancy_params = fancy_type - .contents - .parameters() - .map_err(|_| anyhow!("no params"))?; - - // Collect all the parameters we are expecting from the symbols - let mut parsed_params = vec![]; - for p in ¶ms { - let param = FunctionParameter::new( - p.type_.clone().merge(Conf::new( - Type::int(self.arch.address_size(), false), - min_confidence(), - )), - p.name.clone(), - p.storage.get(0).map(|loc| loc.location.clone()), - ); - // Ignore thisptr because it's not technically part of the raw type signature - if p.name != "this" { - parsed_params.push(param); - } - } - let mut parsed_locals = vec![]; - for p in &locals { - let param = FunctionParameter::new( - p.type_.clone().merge(Conf::new( - Type::int(self.arch.address_size(), false), - min_confidence(), - )), - p.name.clone(), - p.storage.get(0).map(|loc| loc.location.clone()), - ); - // Ignore thisptr because it's not technically part of the raw type signature - if p.name != "this" { - parsed_locals.push(param); - } - } - - self.log(|| format!("Raw params: {:#x?}", raw_params)); - self.log(|| format!("Fancy params: {:#x?}", fancy_params)); - self.log(|| format!("Parsed params: {:#x?}", parsed_params)); - - // We expect one parameter for each unnamed parameter in the marked up type - let expected_param_count = fancy_params - .iter() - .filter(|p| p.name.as_str().is_empty()) - .count(); - // Sanity - if expected_param_count != raw_params.len() { - return Err(anyhow!( - "Mismatched number of formal parameters and interpreted parameters" - )); - } - - // If we don't have enough parameters to fill the slots, there's a problem here - // So just fallback to the unnamed params - if expected_param_count > parsed_params.len() { - // As per reversing of msdia140.dll (and nowhere else): if a function doesn't have - // enough parameter variables declared as parameters, the remaining parameters are - // the first however many locals. If you don't have enough of those, idk?? - if expected_param_count > (parsed_params.len() + parsed_locals.len()) { - return Ok((fancier_type, vec![])); - } - parsed_params.extend(parsed_locals.into_iter()); - } - let expected_parsed_params = parsed_params - .drain(0..expected_param_count) - .collect::<Vec<_>>(); - - // For all formal parameters, apply names to them in fancy_params - // These should be all types in fancy_params that are unnamed (named ones we inserted) - - let mut i = 0; - for p in fancy_params.iter_mut() { - if p.name.as_str().is_empty() { - if p.t.contents != expected_parsed_params[i].t.contents { - self.log(|| { - format!( - "Suspicious parameter {}: {:?} vs {:?}", - i, p, expected_parsed_params[i] - ) - }); - } - if expected_parsed_params[i].name.as_str() == "__formal" { - p.name = format!("__formal{}", i); - } else { - p.name = expected_parsed_params[i].name.clone(); - } - i += 1; - } - } - - // Now apply the default location for the params from the cc - let cc = fancy_type - .contents - .calling_convention() - .map_or_else(|_| Conf::new(self.default_cc.clone(), 0), |cc| cc); - - self.log(|| { - format!( - "Type calling convention: {:?}", - fancy_type.contents.calling_convention() - ) - }); - self.log(|| format!("Default calling convention: {:?}", self.default_cc)); - self.log(|| format!("Result calling convention: {:?}", cc)); - - let locations = cc.contents.variables_for_parameters(&fancy_params, None); - for (p, new_location) in fancy_params.iter_mut().zip(locations.into_iter()) { - p.location = Some(new_location); - } - - self.log(|| format!("Final params: {:#x?}", fancy_params)); - - // Use the new locals we've parsed to make the Real Definitely True function type - let fancy_type = Conf::new( - Type::function_with_options( - &fancy_type - .contents - .return_value() - .map_err(|_| anyhow!("no ret"))?, - fancy_params.as_slice(), - fancy_type.contents.has_variable_arguments().contents, - &cc, - fancy_type.contents.stack_adjustment(), - ), - max_confidence(), - ); - - let fancier_type = fancy_type - .clone() - .merge(raw_type.clone()) - .merge(demangled_type.clone()); - - self.log(|| format!("Raw type: {:#x?}", raw_type)); - self.log(|| format!("Demangled type: {:#x?}", demangled_type)); - self.log(|| format!("Fancy type: {:#x?}", fancy_type)); - self.log(|| format!("Result type: {:#x?}", fancier_type)); - - Ok((Some(fancier_type), vec![])) - } - - fn handle_procedure_symbol( - &mut self, - index: SymbolIndex, - data: &ProcedureSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got Procedure symbol: {:?}", data)); - - let rva = data.offset.to_rva(&self.address_map).unwrap_or_default(); - let address = self.bv.start() + rva.0 as u64; - - let mut raw_name = data.name.to_string().to_string(); - - // Generally proc symbols have real types, but use the demangler just in case the microsoft - // public pdbs have the function type as `void` - let (t, name) = self.demangle_to_type(&raw_name, rva)?; - let mut name = name.map(|n| n.string()); - - // Some proc symbols don't have a mangled name, so try and look up their name - if name.is_none() || name.as_ref().expect("just failed none") == &raw_name { - // Lookup public symbol with the same name - if let Some(others) = self.addressed_symbols.get(&address) { - for o in others { - match o { - ParsedSymbol::Procedure(ParsedProcedure { - name: proc_name, .. - }) => { - if proc_name.full_name.as_ref().unwrap_or(&proc_name.raw_name) - == &raw_name - { - name = Some(raw_name); - raw_name = proc_name.raw_name.clone(); - break; - } - } - _ => {} - } - } - } - } - - let (fn_type, locals) = self.lookup_locals(index, data.type_index, t)?; - - let name = SymbolNames { - raw_name, - short_name: name.clone(), - full_name: name, - }; - - self.log(|| format!("PROC: 0x{:x}: {:?} {:?}", address, &name, &fn_type)); - - Ok(Some(ParsedSymbol::Procedure(ParsedProcedure { - is_public: false, - address, - name, - type_: fn_type, - locals, - }))) - } - - fn handle_thread_storage_symbol( - &mut self, - _index: SymbolIndex, - data: &ThreadStorageSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got ThreadStorage symbol: {:?}", data)); - Ok(None) - } - - fn handle_compile_flags_symbol( - &mut self, - _index: SymbolIndex, - data: &CompileFlagsSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got CompileFlags symbol: {:?}", data)); - self.module_cpu_type = Some(data.cpu_type); - Ok(None) - } - - fn handle_using_namespace_symbol( - &mut self, - _index: SymbolIndex, - data: &UsingNamespaceSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got UsingNamespace symbol: {:?}", data)); - Ok(None) - } - - fn handle_procedure_reference_symbol( - &mut self, - _index: SymbolIndex, - data: &ProcedureReferenceSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got ProcedureReference symbol: {:?}", data)); - Ok(None) - } - - fn handle_data_reference_symbol( - &mut self, - _index: SymbolIndex, - data: &DataReferenceSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got DataReference symbol: {:?}", data)); - Ok(None) - } - - fn handle_annotation_reference_symbol( - &mut self, - _index: SymbolIndex, - data: &AnnotationReferenceSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got AnnotationReference symbol: {:?}", data)); - Ok(None) - } - - fn handle_trampoline_symbol( - &mut self, - _index: SymbolIndex, - data: &TrampolineSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got Trampoline symbol: {:?}", data)); - let rva = data.thunk.to_rva(&self.address_map).unwrap_or_default(); - let target_rva = data.target.to_rva(&self.address_map).unwrap_or_default(); - - let address = self.bv.start() + rva.0 as u64; - let target_address = self.bv.start() + target_rva.0 as u64; - - let mut target_name = None; - let mut thunk_name = None; - - let mut fn_type: Option<Conf<Ref<Type>>> = None; - - // These have the same name as their target, so look that up - if let Some(syms) = self.addressed_symbols.get(&target_address) { - // Take name from the public symbol - for sym in syms { - match sym { - ParsedSymbol::Procedure(proc) if proc.is_public => { - fn_type = proc.type_.clone().merge(fn_type); - target_name = Some(proc.name.clone()); - } - _ => {} - } - } - // Take type from the non-public symbol if we have one - for sym in syms { - match sym { - ParsedSymbol::Procedure(proc) if !proc.is_public => { - fn_type = proc.type_.clone().merge(fn_type); - if target_name.is_none() { - target_name = Some(proc.name.clone()); - } - } - _ => {} - } - } - } - - // And handle the fact that pdb public symbols for trampolines have the name of their target - // ugh - if let Some(syms) = self.addressed_symbols.get_mut(&address) { - if let [ParsedSymbol::Procedure(proc)] = syms.as_mut_slice() { - if let Some(tn) = &target_name { - if proc.name.raw_name == tn.raw_name - || proc.name.full_name.as_ref().unwrap_or(&proc.name.raw_name) - == tn.full_name.as_ref().unwrap_or(&tn.raw_name) - { - // Yeah it's one of these symbols - let old_name = proc.name.clone(); - let new_name = SymbolNames { - raw_name: "j_".to_string() + &old_name.raw_name, - short_name: old_name.short_name.as_ref().map(|n| "j_".to_string() + n), - full_name: old_name.full_name.as_ref().map(|n| "j_".to_string() + n), - }; - - // I'm so sorry about this - // XXX: Update the parsed public symbol's name to use j_ syntax - if let Some(idx) = self.named_symbols.remove(&old_name.raw_name) { - self.named_symbols.insert(new_name.raw_name.clone(), idx); - } - if let Some(idx) = self.parsed_symbols_by_name.remove(&old_name.raw_name) { - self.parsed_symbols_by_name - .insert(new_name.raw_name.clone(), idx); - match &mut self.parsed_symbols[idx] { - ParsedSymbol::Data(ParsedDataSymbol { - name: parsed_name, .. - }) - | ParsedSymbol::Procedure(ParsedProcedure { - name: parsed_name, - .. - }) => { - parsed_name.raw_name = new_name.raw_name.clone(); - parsed_name.short_name = new_name.short_name.clone(); - parsed_name.full_name = new_name.full_name.clone(); - } - _ => {} - } - } - proc.name = new_name.clone(); - thunk_name = Some(new_name); - } - } - } - } - - if thunk_name.is_none() { - if let Some(tn) = target_name { - thunk_name = Some(SymbolNames { - raw_name: "j_".to_string() + &tn.raw_name, - short_name: tn.short_name.as_ref().map(|n| "j_".to_string() + n), - full_name: tn.full_name.as_ref().map(|n| "j_".to_string() + n), - }); - } - } - - let name = thunk_name.unwrap_or(SymbolNames { - raw_name: format!("j_sub_{:x}", target_address), - short_name: None, - full_name: None, - }); - - self.log(|| format!("TRAMPOLINE: 0x{:x}: {:?} {:?}", address, &name, &fn_type)); - - Ok(Some(ParsedSymbol::Procedure(ParsedProcedure { - is_public: false, - address, - name, - type_: fn_type, - locals: vec![], - }))) - } - - fn handle_export_symbol( - &mut self, - _index: SymbolIndex, - data: &ExportSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got Export symbol: {:?}", data)); - Ok(None) - } - - fn handle_local_symbol( - &mut self, - index: SymbolIndex, - data: &LocalSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got Local symbol: {:?}", data)); - // Look for definition ranges for this symbol - let mut locations = vec![]; - for child in self.symbol_children(index) { - match self.lookup_symbol(&child) { - Some(ParsedSymbol::Location(loc)) => { - locations.push(loc.clone()); - } - _ => {} - } - } - - Ok(Some(ParsedSymbol::LocalVariable(ParsedVariable { - name: data.name.to_string().to_string(), - type_: self.lookup_type_conf(&data.type_index, false)?, - storage: locations, - is_param: data.flags.isparam, - }))) - } - - fn handle_build_info_symbol( - &mut self, - _index: SymbolIndex, - data: &BuildInfoSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got BuildInfo symbol: {:?}", data)); - Ok(None) - } - - fn handle_inline_site_symbol( - &mut self, - _index: SymbolIndex, - data: &InlineSiteSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got InlineSite symbol: {:?}", data)); - Ok(None) - } - - fn handle_inline_site_end_symbol( - &mut self, - _index: SymbolIndex, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got InlineSiteEnd symbol")); - Ok(None) - } - - fn handle_procedure_end_symbol(&mut self, _index: SymbolIndex) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got ProcedureEnd symbol")); - Ok(None) - } - - fn handle_label_symbol( - &mut self, - _index: SymbolIndex, - data: &LabelSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got Label symbol: {:?}", data)); - Ok(None) - } - - fn handle_block_symbol( - &mut self, - _index: SymbolIndex, - data: &BlockSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got Block symbol: {:?}", data)); - Ok(None) - } - - fn handle_register_relative_symbol( - &mut self, - _index: SymbolIndex, - data: &RegisterRelativeSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got RegisterRelative symbol: {:?}", data)); - match self.lookup_register(data.register) { - Some(X86(X86Register::EBP)) | Some(AMD64(AMD64Register::RBP)) => { - // Local is relative to base pointer - // This is just another way of writing BasePointerRelativeSymbol - Ok(Some(ParsedSymbol::LocalVariable(ParsedVariable { - name: data.name.to_string().to_string(), - type_: self.lookup_type_conf(&data.type_index, false)?, - storage: vec![ParsedLocation { - location: Variable { - t: VariableSourceType::StackVariableSourceType, - index: 0, - storage: data.offset as i64, - }, - base_relative: true, // !! - stack_relative: false, // !! - }], - is_param: data.slot.map_or(false, |slot| slot > 0), - }))) - } - Some(X86(X86Register::ESP)) | Some(AMD64(AMD64Register::RSP)) => { - // Local is relative to stack pointer - // This is the same as base pointer case except not base relative (ofc) - Ok(Some(ParsedSymbol::LocalVariable(ParsedVariable { - name: data.name.to_string().to_string(), - type_: self.lookup_type_conf(&data.type_index, false)?, - storage: vec![ParsedLocation { - location: Variable { - t: VariableSourceType::StackVariableSourceType, - index: 0, - storage: data.offset as i64, - }, - base_relative: false, // !! - stack_relative: true, // !! - }], - is_param: data.slot.map_or(false, |slot| slot > 0), - }))) - } - _ => { - // Local is relative to some non-bp register. - // This is, of course, totally possible and normal - // Binja just can't handle it in the slightest. - // Soooooooo ???? - // TODO - Ok(None) - } - } - } - - fn handle_thunk_symbol( - &mut self, - _index: SymbolIndex, - data: &ThunkSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got Thunk symbol: {:?}", data)); - let rva = data.offset.to_rva(&self.address_map).unwrap_or_default(); - let raw_name = data.name.to_string().to_string(); - let address = self.bv.start() + rva.0 as u64; - - let (t, name) = self.demangle_to_type(&raw_name, rva)?; - let name = name.map(|n| n.string()); - let mut fn_type = t; - - // These have the same name as their target, so look that up - if let Some(&idx) = self.named_symbols.get(&raw_name) { - if let Some(ParsedSymbol::Procedure(proc)) = self.indexed_symbols.get(&idx) { - fn_type = proc.type_.clone().merge(fn_type); - } - } - - let mut thunk_name = None; - - // And handle the fact that pdb public symbols for thunks have the name of their target - // ugh - if let Some(syms) = self.addressed_symbols.get_mut(&address) { - if let [ParsedSymbol::Procedure(proc)] = syms.as_mut_slice() { - // Yeah it's one of these symbols - // Make sure we don't do this twice (does that even happen?) - if !proc.name.raw_name.starts_with("j_") { - let old_name = proc.name.clone(); - let new_name = SymbolNames { - raw_name: "j_".to_string() + &old_name.raw_name, - short_name: Some( - "j_".to_string() + old_name.short_name.as_ref().unwrap_or(&raw_name), - ), - full_name: Some( - "j_".to_string() + old_name.full_name.as_ref().unwrap_or(&raw_name), - ), - }; - - // I'm so sorry about this - // XXX: Update the parsed public symbol's name to use j_ syntax - if let Some(idx) = self.named_symbols.remove(&old_name.raw_name) { - self.named_symbols.insert(new_name.raw_name.clone(), idx); - } - if let Some(idx) = self.parsed_symbols_by_name.remove(&old_name.raw_name) { - self.parsed_symbols_by_name - .insert(new_name.raw_name.clone(), idx); - match &mut self.parsed_symbols[idx] { - ParsedSymbol::Data(ParsedDataSymbol { - name: parsed_name, .. - }) - | ParsedSymbol::Procedure(ParsedProcedure { - name: parsed_name, .. - }) => { - parsed_name.raw_name = new_name.raw_name.clone(); - parsed_name.short_name = new_name.short_name.clone(); - parsed_name.full_name = new_name.full_name.clone(); - } - _ => {} - } - } - proc.name = new_name.clone(); - thunk_name = Some(new_name); - } - } - } - - let locals = vec![]; - let name = thunk_name.unwrap_or(SymbolNames { - raw_name, - short_name: name.clone(), - full_name: name, - }); - - self.log(|| format!("THUNK: 0x{:x}: {:?} {:?}", address, &name, &fn_type)); - - Ok(Some(ParsedSymbol::Procedure(ParsedProcedure { - is_public: false, - address: address, - name, - type_: fn_type, - locals, - }))) - } - - fn handle_separated_code_symbol( - &mut self, - _index: SymbolIndex, - data: &SeparatedCodeSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got SeparatedCode symbol: {:?}", data)); - Ok(None) - } - - fn handle_def_range( - &mut self, - _index: SymbolIndex, - data: &DefRangeSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got DefRange symbol: {:?}", data)); - Ok(None) - } - - fn handle_def_range_sub_field( - &mut self, - _index: SymbolIndex, - data: &DefRangeSubFieldSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got DefRangeSubField symbol: {:?}", data)); - Ok(None) - } - - fn handle_def_range_register( - &mut self, - _index: SymbolIndex, - data: &DefRangeRegisterSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got DefRangeRegister symbol: {:?}", data)); - if let Some(reg) = self.convert_register(data.register) { - Ok(Some(ParsedSymbol::Location(ParsedLocation { - location: Variable { - t: VariableSourceType::RegisterVariableSourceType, - index: 0, - storage: reg, - }, - base_relative: false, - stack_relative: false, - }))) - } else { - Ok(None) - } - } - - fn handle_def_range_frame_pointer_relative_symbol( - &mut self, - _index: SymbolIndex, - data: &DefRangeFramePointerRelativeSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got DefRangeFramePointerRelative symbol: {:?}", data)); - Ok(None) - } - - fn handle_def_range_frame_pointer_relative_full_scope_symbol( - &mut self, - _index: SymbolIndex, - data: &DefRangeFramePointerRelativeFullScopeSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| { - format!( - "Got DefRangeFramePointerRelativeFullScope symbol: {:?}", - data - ) - }); - Ok(None) - } - - fn handle_def_range_sub_field_register_symbol( - &mut self, - _index: SymbolIndex, - data: &DefRangeSubFieldRegisterSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got DefRangeSubFieldRegister symbol: {:?}", data)); - Ok(None) - } - - fn handle_def_range_register_relative_symbol( - &mut self, - _index: SymbolIndex, - data: &DefRangeRegisterRelativeSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got DefRangeRegisterRelative symbol: {:?}", data)); - Ok(None) - } - - fn handle_base_pointer_relative_symbol( - &mut self, - _index: SymbolIndex, - data: &BasePointerRelativeSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got BasePointerRelative symbol: {:?}", data)); - - // These are usually parameters if offset > 0 - - Ok(Some(ParsedSymbol::LocalVariable(ParsedVariable { - name: data.name.to_string().to_string(), - type_: self.lookup_type_conf(&data.type_index, false)?, - storage: vec![ParsedLocation { - location: Variable { - t: VariableSourceType::StackVariableSourceType, - index: 0, - storage: data.offset as i64, - }, - base_relative: true, - stack_relative: false, - }], - is_param: data.offset as i64 > 0 || data.slot.map_or(false, |slot| slot > 0), - }))) - } - - fn handle_frame_procedure_symbol( - &mut self, - index: SymbolIndex, - data: &FrameProcedureSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got FrameProcedure symbol: {:?}", data)); - - // This symbol generally comes before a proc and all various parameters - // It has a lot of information we don't care about, and some information we maybe do? - // This function also tries to find all the locals and parameters of the procedure - - let mut params = vec![]; - let mut locals = vec![]; - let mut seen_offsets = HashSet::new(); - - for child in self.symbol_children(index) { - match self.lookup_symbol(&child) { - Some(ParsedSymbol::LocalVariable(ParsedVariable { - name, - type_, - storage, - is_param, - .. - })) => { - let new_storage = storage.iter().map(|&var| var.location).collect::<Vec<_>>(); - - // See if the parameter really is a parameter. Sometimes they don't say they are - let mut really_is_param = *is_param; - for loc in &new_storage { - match loc { - Variable { - t: VariableSourceType::RegisterVariableSourceType, - .. - } => { - // Assume register vars are always parameters - really_is_param = true; - } - Variable { - t: VariableSourceType::StackVariableSourceType, - storage, - .. - } if *storage >= 0 => { - // Sometimes you can get two locals at the same offset, both rbp+(x > 0) - // I'm guessing from looking at dumps from dia2dump that only the first - // one is considered a parameter, although there are times that I see - // two params at the same offset and both are considered parameters... - // This doesn't seem possible (or correct) because they would overlap - // and only one would be useful anyway. - // Regardless of the mess, Binja can only handle one parameter per slot - // so we're just going to use the first one. - really_is_param = seen_offsets.insert(*storage); - } - _ => {} - } - } - - if really_is_param { - params.push(ParsedVariable { - name: name.clone(), - type_: type_.clone(), - storage: new_storage - .into_iter() - .map(|loc| ParsedLocation { - location: loc, - // This has been handled now - base_relative: false, - stack_relative: false, - }) - .collect(), - is_param: really_is_param, - }); - } else { - locals.push(ParsedVariable { - name: name.clone(), - type_: type_.clone(), - storage: new_storage - .into_iter() - .map(|loc| ParsedLocation { - location: loc, - // This has been handled now - base_relative: false, - stack_relative: false, - }) - .collect(), - is_param: really_is_param, - }); - } - } - Some(ParsedSymbol::Data(_)) => { - // Apparently you can have static data symbols as parameters - // Because of course you can - } - None => {} - e => self.log(|| format!("Unexpected symbol type in frame: {:?}", e)), - } - } - - Ok(Some(ParsedSymbol::ProcedureInfo(ParsedProcedureInfo { - params, - locals, - }))) - } - - fn handle_call_site_info( - &mut self, - _index: SymbolIndex, - data: &CallSiteInfoSymbol, - ) -> Result<Option<ParsedSymbol>> { - self.log(|| format!("Got CallSiteInfo symbol: {:?}", data)); - Ok(None) - } - - /// Demangle a name and get a type out - /// Also fixes void(void) and __s_RTTI_Nonsense - fn demangle_to_type( - &self, - raw_name: &String, - rva: Rva, - ) -> Result<(Option<Conf<Ref<Type>>>, Option<QualifiedName>)> { - let (mut t, mut name) = match demangle_ms(&self.arch, raw_name, true) { - Ok((Some(t), name)) => (Some(Conf::new(t, DEMANGLE_CONFIDENCE)), name), - Ok((_, name)) => (None, name), - _ => (None, vec![raw_name.clone()]), - }; - - if let Some(ty) = t.as_ref() { - if ty.contents.type_class() == TypeClass::FunctionTypeClass { - // demangler makes (void) into (void arg1) which is wrong - let parameters = ty - .contents - .parameters() - .map_err(|_| anyhow!("no parameters"))?; - if let [p] = parameters.as_slice() { - if p.t.contents.type_class() == TypeClass::VoidTypeClass { - t = Some(Conf::new( - Type::function::<_>( - &ty.contents - .return_value() - .map_err(|_| anyhow!("no return value"))?, - &[], - ty.contents.has_variable_arguments().contents, - ), - ty.confidence, - )) - } - } - } - } - - // These have types but they aren't actually set anywhere. So it's the demangler's - // job to take care of them, apparently? - static MEM: OnceLock<Vec<(String, Vec<String>)>> = OnceLock::new(); - let name_to_type = MEM.get_or_init(|| { - vec![ - ( - "`RTTI Complete Object Locator'".to_string(), - vec![ - "_s_RTTICompleteObjectLocator".to_string(), - "_s__RTTICompleteObjectLocator".to_string(), - "_s__RTTICompleteObjectLocator2".to_string(), - ], - ), - ( - "`RTTI Class Hierarchy Descriptor'".to_string(), - vec![ - "_s_RTTIClassHierarchyDescriptor".to_string(), - "_s__RTTIClassHierarchyDescriptor".to_string(), - "_s__RTTIClassHierarchyDescriptor2".to_string(), - ], - ), - ( - // TODO: This type is dynamic - "`RTTI Base Class Array'".to_string(), - vec![ - "_s_RTTIBaseClassArray".to_string(), - "_s__RTTIBaseClassArray".to_string(), - "_s__RTTIBaseClassArray2".to_string(), - ], - ), - ( - "`RTTI Base Class Descriptor at (".to_string(), - vec![ - "_s_RTTIBaseClassDescriptor".to_string(), - "_s__RTTIBaseClassDescriptor".to_string(), - "_s__RTTICBaseClassDescriptor2".to_string(), - ], - ), - ( - "`RTTI Type Descriptor'".to_string(), - vec!["_TypeDescriptor".to_string()], - ), - ] - }); - - if let Some(last_name) = name.last() { - for (search_name, search_types) in name_to_type.iter() { - if last_name.contains(search_name) { - for search_type in search_types { - if let Some(ty) = self.named_types.get(search_type) { - // Fallback in case we don't find a specific one - t = Some(Conf::new( - Type::named_type_from_type(search_type, ty.as_ref()), - DEMANGLE_CONFIDENCE, - )); - - if self.settings.get_bool( - "pdb.features.expandRTTIStructures", - Some(self.bv), - None, - ) { - if let Some((lengthy_type, length)) = - self.make_lengthy_type(ty, self.bv.start() + rva.0 as u64)? - { - // See if we have a type with this length - let lengthy_name = - format!("${}$_extraBytes_{}", search_type, length); - - if let Some(ty) = self.named_types.get(&lengthy_name) { - // Wow! - t = Some(Conf::new( - Type::named_type_from_type(lengthy_name, ty.as_ref()), - DEMANGLE_CONFIDENCE, - )); - } else { - t = Some(Conf::new(lengthy_type, DEMANGLE_CONFIDENCE)); - } - } - } - } - } - } - } - } - - // VTables have types on their data symbols, - if let Some((class_name, last)) = name.join("::").rsplit_once("::") { - if last.contains("`vftable'") { - let mut vt_name = class_name.to_string() + "::" + "VTable"; - if last.contains("{for") { - // DerivedClass::`vftable'{for `BaseClass'} - let mut base_name = last.to_owned(); - base_name.drain(0..("`vftable'{for `".len())); - base_name.drain((base_name.len() - "'}".len())..(base_name.len())); - // Multiply inherited classes have multiple vtable types - // TODO: Do that - vt_name = base_name + "::" + "VTable"; - } - - vt_name = vt_name - .replace("class ", "") - .replace("struct ", "") - .replace("enum ", ""); - - if let Some(ty) = self.named_types.get(&vt_name) { - t = Some(Conf::new( - Type::named_type_from_type(&vt_name, ty.as_ref()), - DEMANGLE_CONFIDENCE, - )); - } else { - // Sometimes the demangler has trouble with `class Foo` in templates - vt_name = vt_name - .replace("class ", "") - .replace("struct ", "") - .replace("enum ", ""); - - if let Some(ty) = self.named_types.get(&vt_name) { - t = Some(Conf::new( - Type::named_type_from_type(&vt_name, ty.as_ref()), - DEMANGLE_CONFIDENCE, - )); - } else { - t = Some(Conf::new( - Type::named_type_from_type( - &vt_name, - Type::structure(StructureBuilder::new().finalize().as_ref()) - .as_ref(), - ), - DEMANGLE_CONFIDENCE, - )); - } - } - } - } - - if let Some(last_name) = name.last_mut() { - if last_name.starts_with("__imp_") { - last_name.drain(0..("__imp_".len())); - } - } - - let name = if name.len() == 1 && &name[0] == raw_name && raw_name.starts_with('?') { - None - } else if name.len() == 1 && name[0] == "" { - None - } else if name.len() > 0 && name[0].starts_with("\x7f") { - // Not sure why these exist but they do Weird Stuff - name[0].drain(0..1); - Some(QualifiedName::from(name)) - } else { - Some(QualifiedName::from(name)) - }; - - Ok((t, name)) - } - - fn make_lengthy_type( - &self, - base_type: &Ref<Type>, - base_address: u64, - ) -> Result<Option<(Ref<Type>, usize)>> { - if base_type.type_class() != TypeClass::StructureTypeClass { - return Ok(None); - } - let structure = base_type - .get_structure() - .map_err(|_| anyhow!("Expected structure"))?; - let mut members = structure - .members() - .map_err(|_| anyhow!("Expected structure to have members"))?; - let last_member = members - .last_mut() - .ok_or_else(|| anyhow!("Not enough members"))?; - - if last_member.ty.contents.type_class() != TypeClass::ArrayTypeClass { - return Ok(None); - } - if last_member.ty.contents.count() != 0 { - return Ok(None); - } - - let member_element = last_member - .ty - .contents - .element_type() - .map_err(|_| anyhow!("Last member has no type"))? - .contents; - let member_width = member_element.width(); - - // Read member_width bytes from bv starting at that member, until we read all zeroes - let member_address = base_address + last_member.offset; - - let mut bytes = Vec::<u8>::new(); - bytes.resize(member_width as usize, 0); - - let mut element_count = 0; - while self.bv.read( - bytes.as_mut_slice(), - member_address + member_width * element_count, - ) == member_width as usize - { - if bytes.iter().all(|&b| b == 0) { - break; - } - element_count += 1; - } - - // Make a new copy of the type with the correct element count - last_member.ty.contents = Type::array(member_element.as_ref(), element_count); - - Ok(Some(( - Type::structure(StructureBuilder::from(members).finalize().as_ref()), - element_count as usize, - ))) - } - - /// Sorry about the type names - /// Given a pdb::Register (u32), get a pdb::register::Register (big enum with names) - fn lookup_register(&self, reg: pdb::Register) -> Option<pdb::register::Register> { - if let Some(cpu) = self.module_cpu_type { - pdb::register::Register::new(reg, cpu).ok() - } else { - None - } - } - - /// Convert a pdb::Register (u32) to a binja register index for the current arch - fn convert_register(&self, reg: pdb::Register) -> Option<i64> { - match self.lookup_register(reg) { - Some(X86(xreg)) => { - self.log(|| format!("Register {:?} ==> {:?}", reg, xreg)); - self.arch - .register_by_name(xreg.to_string().to_lowercase()) - .map(|reg| reg.id() as i64) - } - Some(AMD64(areg)) => { - self.log(|| format!("Register {:?} ==> {:?}", reg, areg)); - self.arch - .register_by_name(areg.to_string().to_lowercase()) - .map(|reg| reg.id() as i64) - } - // TODO: Other arches - _ => None, - } - } -} |
