diff options
Diffstat (limited to 'rust/examples/pdb-ng/src/parser.rs')
| -rw-r--r-- | rust/examples/pdb-ng/src/parser.rs | 508 |
1 files changed, 0 insertions, 508 deletions
diff --git a/rust/examples/pdb-ng/src/parser.rs b/rust/examples/pdb-ng/src/parser.rs deleted file mode 100644 index 2d56a76d..00000000 --- a/rust/examples/pdb-ng/src/parser.rs +++ /dev/null @@ -1,508 +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::env; -use std::fmt::Display; -use std::sync::OnceLock; - -use anyhow::{anyhow, Result}; -use log::{debug, info}; -use pdb::*; - -use binaryninja::architecture::{Architecture, CoreArchitecture}; -use binaryninja::binaryview::{BinaryView, BinaryViewExt}; -use binaryninja::callingconvention::CallingConvention; -use binaryninja::debuginfo::{DebugFunctionInfo, DebugInfo}; -use binaryninja::platform::Platform; -use binaryninja::rc::Ref; -use binaryninja::settings::Settings; -use binaryninja::types::{ - min_confidence, Conf, DataVariableAndName, EnumerationBuilder, NamedTypeReference, - NamedTypeReferenceClass, StructureBuilder, StructureType, Type, TypeClass, -}; - -use crate::symbol_parser::{ParsedDataSymbol, ParsedProcedure, ParsedSymbol}; -use crate::type_parser::ParsedType; - -/// Megastruct for all the parsing -/// Certain fields are only used by specific files, as marked below. -/// Why not make new structs for them? Because vvvv this garbage -pub struct PDBParserInstance<'a, S: Source<'a> + 'a> { - /// DebugInfo where types/functions will be stored eventually - pub(crate) debug_info: &'a mut DebugInfo, - /// Parent binary view (usually during BinaryView::Finalize) - pub(crate) bv: &'a BinaryView, - /// Default arch of self.bv - pub(crate) arch: CoreArchitecture, - /// Default calling convention for self.arch - pub(crate) default_cc: Ref<CallingConvention<CoreArchitecture>>, - /// Thiscall calling convention for self.bv, or default_cc if we can't find one - pub(crate) thiscall_cc: Ref<CallingConvention<CoreArchitecture>>, - /// Cdecl calling convention for self.bv, or default_cc if we can't find one - pub(crate) cdecl_cc: Ref<CallingConvention<CoreArchitecture>>, - /// Default platform of self.bv - pub(crate) platform: Ref<Platform>, - /// pdb-rs structure for making lifetime hell a real place - pub(crate) pdb: PDB<'a, S>, - /// pdb-rs Mapping of modules to addresses for resolving RVAs - pub(crate) address_map: AddressMap<'a>, - /// Binja Settings instance (for optimization) - pub(crate) settings: Ref<Settings>, - - /// type_parser.rs - - /// TypeIndex -> ParsedType enum used during parsing - pub(crate) indexed_types: BTreeMap<TypeIndex, ParsedType>, - /// QName -> Binja Type for finished types - pub(crate) named_types: BTreeMap<String, Ref<Type>>, - /// Raw (mangled) name -> TypeIndex for resolving forward references - pub(crate) full_type_indices: BTreeMap<String, TypeIndex>, - /// Stack of types we're currently parsing - pub(crate) type_stack: Vec<TypeIndex>, - /// Stack of parent types we're parsing nested types inside of - pub(crate) namespace_stack: Vec<String>, - /// Type Index -> Does it return on the stack - pub(crate) type_default_returnable: BTreeMap<TypeIndex, bool>, - - /// symbol_parser.rs - - /// List of fully parsed symbols from all modules - pub(crate) parsed_symbols: Vec<ParsedSymbol>, - /// Raw name -> index in parsed_symbols - pub(crate) parsed_symbols_by_name: BTreeMap<String, usize>, - /// Raw name -> Symbol index for looking up symbols for the currently parsing module (mostly for thunks) - pub(crate) named_symbols: BTreeMap<String, SymbolIndex>, - /// Parent -> Children symbol index tree for the currently parsing module - pub(crate) symbol_tree: BTreeMap<SymbolIndex, Vec<SymbolIndex>>, - /// Child -> Parent symbol index mapping, inverse of symbol_tree - pub(crate) symbol_parents: BTreeMap<SymbolIndex, SymbolIndex>, - /// Stack of (start, end) indices for the current symbols being parsed while constructing the tree - pub(crate) symbol_stack: Vec<(SymbolIndex, SymbolIndex)>, - /// Index -> parsed symbol for the currently parsing module - pub(crate) indexed_symbols: BTreeMap<SymbolIndex, ParsedSymbol>, - /// Symbol address -> Symbol for looking up by address - pub(crate) addressed_symbols: BTreeMap<u64, Vec<ParsedSymbol>>, - /// CPU type of the currently parsing module - pub(crate) module_cpu_type: Option<CPUType>, -} - -impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { - /// Try to create a new parser instance from a given bv/pdb - pub fn new( - debug_info: &'a mut DebugInfo, - bv: &'a BinaryView, - mut pdb: PDB<'a, S>, - ) -> Result<Self> { - let arch = if let Some(arch) = bv.default_arch() { - arch - } else { - return Err(anyhow!("Cannot parse to view with no architecture")); - }; - - let platform = bv - .default_platform() - .expect("Expected bv to have a platform"); - - let address_map = pdb.address_map()?; - - let default_cc = platform - .get_default_calling_convention() - .expect("Expected default calling convention"); - - let thiscall_cc = Self::find_calling_convention(platform.as_ref(), "thiscall") - .unwrap_or(default_cc.clone()); - - let cdecl_cc = platform - .get_cdecl_calling_convention() - .unwrap_or(default_cc.clone()); - - Ok(Self { - debug_info, - bv, - arch, - default_cc, - thiscall_cc, - cdecl_cc, - platform, - pdb, - address_map, - settings: Settings::new(""), - indexed_types: Default::default(), - named_types: Default::default(), - full_type_indices: Default::default(), - type_stack: Default::default(), - namespace_stack: Default::default(), - type_default_returnable: Default::default(), - parsed_symbols: Default::default(), - parsed_symbols_by_name: Default::default(), - named_symbols: Default::default(), - symbol_tree: Default::default(), - symbol_parents: Default::default(), - symbol_stack: Default::default(), - indexed_symbols: Default::default(), - addressed_symbols: Default::default(), - module_cpu_type: None, - }) - } - - /// Try to parse the pdb into the DebugInfo - pub fn try_parse_info( - &mut self, - progress: Box<dyn Fn(usize, usize) -> Result<()> + 'a>, - ) -> Result<()> { - self.parse_types(Self::split_progress(&progress, 0, &[1.0, 3.0, 0.5, 0.5]))?; - for (name, ty) in self.named_types.iter() { - self.debug_info.add_type(name, ty.as_ref(), &[]); // TODO : Components - } - - info!( - "PDB found {} types (before resolving NTRs)", - self.named_types.len() - ); - - if self - .settings - .get_bool("pdb.features.parseSymbols", Some(self.bv), None) - { - let (symbols, functions) = - self.parse_symbols(Self::split_progress(&progress, 1, &[1.0, 3.0, 0.5, 0.5]))?; - - if self - .settings - .get_bool("pdb.features.createMissingNamedTypes", Some(self.bv), None) - { - self.resolve_missing_ntrs( - &symbols, - Self::split_progress(&progress, 2, &[1.0, 3.0, 0.5, 0.5]), - )?; - self.resolve_missing_ntrs( - &functions, - Self::split_progress(&progress, 3, &[1.0, 3.0, 0.5, 0.5]), - )?; - } - - info!("PDB found {} types", self.named_types.len()); - info!("PDB found {} data variables", symbols.len()); - info!("PDB found {} functions", functions.len()); - - let allow_void = - self.settings - .get_bool("pdb.features.allowVoidGlobals", Some(self.bv), None); - - let min_confidence_type = Conf::new(Type::void(), min_confidence()); - for sym in symbols.iter() { - match sym { - ParsedSymbol::Data(ParsedDataSymbol { - address, - name, - type_, - .. - }) => { - let real_type = - type_.as_ref().unwrap_or(&min_confidence_type); - - if real_type.contents.type_class() == TypeClass::VoidTypeClass { - if !allow_void { - self.log(|| { - format!("Not adding void-typed symbol {:?}@{:x}", name, address) - }); - continue; - } - } - - self.log(|| { - format!( - "Adding data variable: 0x{:x}: {} {:?}", - address, &name.raw_name, real_type - ) - }); - self.debug_info - .add_data_variable_info(DataVariableAndName::new( - *address, - real_type.clone(), - true, - name.full_name.as_ref().unwrap_or(&name.raw_name), - )); - } - s => { - self.log(|| format!("Not adding non-data symbol {:?}", s)); - } - } - } - - for sym in functions { - match sym { - ParsedSymbol::Procedure(ParsedProcedure { - address, - name, - type_, - locals: _, - .. - }) => { - self.log(|| { - format!( - "Adding function: 0x{:x}: {} {:?}", - address, &name.raw_name, type_ - ) - }); - self.debug_info.add_function(DebugFunctionInfo::new( - Some(name.short_name.unwrap_or(name.raw_name.clone())), - Some(name.full_name.unwrap_or(name.raw_name.clone())), - Some(name.raw_name), - type_.clone().and_then(|conf| { - // TODO: When DebugInfo support confidence on function types, remove this - if conf.confidence == 0 { - None - } else { - Some(conf.contents) - } - }), - Some(address), - Some(self.platform.clone()), - vec![], // TODO : Components - vec![], //TODO: local variables - )); - } - _ => {} - } - } - } - - Ok(()) - } - - fn collect_name( - &self, - name: &NamedTypeReference, - unknown_names: &mut HashMap<String, NamedTypeReferenceClass>, - ) { - let used_name = name.name().to_string(); - if let Some(&found) = - unknown_names.get(&used_name) - { - if found != name.class() { - // Interesting case, not sure we care - self.log(|| { - format!( - "Mismatch unknown NTR class for {}: {} ?", - &used_name, - name.class() as u32 - ) - }); - } - } else { - self.log(|| format!("Found new unused name: {}", &used_name)); - unknown_names.insert(used_name, name.class()); - } - } - - fn collect_names( - &self, - ty: &Type, - unknown_names: &mut HashMap<String, NamedTypeReferenceClass>, - ) { - match ty.type_class() { - TypeClass::StructureTypeClass => { - if let Ok(structure) = ty.get_structure() { - if let Ok(members) = structure.members() { - for member in members { - self.collect_names(member.ty.contents.as_ref(), unknown_names); - } - } - if let Ok(bases) = structure.base_structures() { - for base in bases { - self.collect_name(base.ty.as_ref(), unknown_names); - } - } - } - } - TypeClass::PointerTypeClass => { - if let Ok(target) = ty.target() { - self.collect_names(target.contents.as_ref(), unknown_names); - } - } - TypeClass::ArrayTypeClass => { - if let Ok(element_type) = ty.element_type() { - self.collect_names(element_type.contents.as_ref(), unknown_names); - } - } - TypeClass::FunctionTypeClass => { - if let Ok(return_value) = ty.return_value() { - self.collect_names(return_value.contents.as_ref(), unknown_names); - } - if let Ok(params) = ty.parameters() { - for param in params { - self.collect_names(param.t.contents.as_ref(), unknown_names); - } - } - } - TypeClass::NamedTypeReferenceClass => { - if let Ok(ntr) = ty.get_named_type_reference() { - self.collect_name(ntr.as_ref(), unknown_names); - } - } - _ => {} - } - } - - fn resolve_missing_ntrs( - &mut self, - symbols: &Vec<ParsedSymbol>, - progress: Box<dyn Fn(usize, usize) -> Result<()> + '_>, - ) -> Result<()> { - let mut unknown_names = HashMap::new(); - let mut known_names = self - .bv - .types() - .iter() - .map(|qnat| qnat.name().string()) - .collect::<HashSet<_>>(); - - for ty in &self.named_types { - known_names.insert(ty.0.clone()); - } - - let count = symbols.len(); - for (i, sym) in symbols.into_iter().enumerate() { - match sym { - ParsedSymbol::Data(ParsedDataSymbol { - type_: Some(type_), .. - }) => { - self.collect_names(type_.contents.as_ref(), &mut unknown_names); - } - ParsedSymbol::Procedure(ParsedProcedure { - type_: Some(type_), - locals, - .. - }) => { - self.collect_names(type_.contents.as_ref(), &mut unknown_names); - for l in locals { - if let Some(ltype) = &l.type_ { - self.collect_names(ltype.contents.as_ref(), &mut unknown_names); - } - } - } - _ => {} - } - (progress)(i, count)?; - } - - for (name, class) in unknown_names.into_iter() { - if known_names.contains(&name) { - self.log(|| format!("Found referenced name and ignoring: {}", &name)); - continue; - } - self.log(|| format!("Adding referenced but unknown type {} (likely due to demangled name and stripped type)", &name)); - match class { - NamedTypeReferenceClass::UnknownNamedTypeClass - | NamedTypeReferenceClass::TypedefNamedTypeClass => { - self.debug_info.add_type(name, Type::void().as_ref(), &[]); // TODO : Components - } - NamedTypeReferenceClass::ClassNamedTypeClass - | NamedTypeReferenceClass::StructNamedTypeClass - | NamedTypeReferenceClass::UnionNamedTypeClass => { - let structure = StructureBuilder::new(); - match class { - NamedTypeReferenceClass::ClassNamedTypeClass => { - structure.set_structure_type(StructureType::ClassStructureType); - } - NamedTypeReferenceClass::StructNamedTypeClass => { - structure.set_structure_type(StructureType::StructStructureType); - } - NamedTypeReferenceClass::UnionNamedTypeClass => { - structure.set_structure_type(StructureType::UnionStructureType); - } - _ => {} - } - structure.set_width(1); - structure.set_alignment(1); - - self.debug_info.add_type( - name, - Type::structure(structure.finalize().as_ref()).as_ref(), - &[], // TODO : Components - ); - } - NamedTypeReferenceClass::EnumNamedTypeClass => { - let enumeration = EnumerationBuilder::new(); - self.debug_info.add_type( - name, - Type::enumeration( - enumeration.finalize().as_ref(), - self.arch.default_integer_size(), - false, - ) - .as_ref(), - &[], // TODO : Components - ); - } - } - } - - Ok(()) - } - - /// Lazy logging function that prints like 20MB of messages - pub(crate) fn log<F: FnOnce() -> D, D: Display>(&self, msg: F) { - static MEM: OnceLock<bool> = OnceLock::new(); - let debug_pdb = MEM.get_or_init(|| { - env::var("BN_DEBUG_PDB").is_ok() - }); - if *debug_pdb { - let space = "\t".repeat(self.type_stack.len()) + &"\t".repeat(self.symbol_stack.len()); - let msg = format!("{}", msg()); - debug!( - "{}{}", - space, - msg.replace("\n", &*("\n".to_string() + &space)) - ); - } - } - - pub(crate) fn split_progress<'b, F: Fn(usize, usize) -> Result<()> + 'b>( - original_fn: F, - subpart: usize, - subpart_weights: &[f64], - ) -> Box<dyn Fn(usize, usize) -> Result<()> + 'b> { - // Normalize weights - let weight_sum: f64 = subpart_weights.iter().sum(); - if weight_sum < 0.0001 { - return Box::new(|_, _| Ok(())); - } - - // Keep a running count of weights for the start - let mut subpart_starts = vec![]; - let mut start = 0f64; - for w in subpart_weights { - subpart_starts.push(start); - start += *w; - } - - let subpart_start = subpart_starts[subpart] / weight_sum; - let weight = subpart_weights[subpart] / weight_sum; - - Box::new(move |cur: usize, max: usize| { - // Just use a large number for easy divisibility - let steps = 1000000f64; - let subpart_size = steps * weight; - let subpart_progress = ((cur as f64) / (max as f64)) * subpart_size; - - original_fn( - (subpart_start * steps + subpart_progress) as usize, - steps as usize, - ) - }) - } -} |
