diff options
Diffstat (limited to 'plugins/warp/src/convert.rs')
| -rw-r--r-- | plugins/warp/src/convert.rs | 702 |
1 files changed, 39 insertions, 663 deletions
diff --git a/plugins/warp/src/convert.rs b/plugins/warp/src/convert.rs index b6c2f7b4..8272768b 100644 --- a/plugins/warp/src/convert.rs +++ b/plugins/warp/src/convert.rs @@ -1,678 +1,54 @@ -use std::collections::HashSet; +pub mod symbol; +pub mod types; -use binaryninja::architecture::Architecture as BNArchitecture; -use binaryninja::architecture::ArchitectureExt; -use binaryninja::binary_view::{BinaryView, BinaryViewExt}; -use binaryninja::calling_convention::CoreCallingConvention as BNCallingConvention; -use binaryninja::confidence::{Conf as BNConf, MAX_CONFIDENCE}; -use binaryninja::rc::Ref as BNRef; -use binaryninja::symbol::{Symbol as BNSymbol, SymbolType as BNSymbolType}; -use binaryninja::types::{ - BaseStructure as BNBaseStructure, EnumerationBuilder as BNEnumerationBuilder, - FunctionParameter as BNFunctionParameter, MemberAccess as BNMemberAccess, MemberAccess, - MemberScope as BNMemberScope, NamedTypeReference, NamedTypeReference as BNNamedTypeReference, - NamedTypeReferenceClass, StructureBuilder as BNStructureBuilder, - StructureMember as BNStructureMember, -}; -use binaryninja::types::{ - StructureType as BNStructureType, Type as BNType, TypeClass as BNTypeClass, -}; +use binaryninja::function::Comment as BNComment; +use binaryninja::function::Function as BNFunction; +use binaryninja::platform::Platform; +use binaryninja::rc::Ref; +pub use symbol::*; +pub use types::*; +use warp::signature::comment::FunctionComment; +use warp::target::Target; -use crate::cache::{cached_type_reference, TypeRefID}; -use warp::r#type::class::array::ArrayModifiers; -use warp::r#type::class::function::{Location, RegisterLocation}; -use warp::r#type::class::pointer::PointerAddressing; -use warp::r#type::class::structure::StructureMemberModifiers; -use warp::r#type::class::{ - ArrayClass, BooleanClass, CallingConvention, CharacterClass, EnumerationClass, - EnumerationMember, FloatClass, FunctionClass, FunctionMember, IntegerClass, PointerClass, - ReferrerClass, StructureClass, StructureMember, TypeClass, -}; -use warp::r#type::Type; -use warp::symbol::class::SymbolClass; -use warp::symbol::{Symbol, SymbolModifiers}; - -pub fn from_bn_symbol(raw_symbol: &BNSymbol) -> Symbol { - // TODO: Use this? - let _is_export = raw_symbol.external(); - let symbol_name = raw_symbol.raw_name().to_string_lossy().to_string(); - match raw_symbol.sym_type() { - BNSymbolType::ImportAddress => { - Symbol::new( - symbol_name, - SymbolClass::Function, - // TODO: External = symbolic i guess - SymbolModifiers::External, - ) - } - BNSymbolType::Data => { - Symbol::new( - symbol_name, - // TODO: Data? - SymbolClass::Data, - SymbolModifiers::default(), - ) - } - BNSymbolType::Symbolic => { - Symbol::new( - symbol_name, - SymbolClass::Function, - // TODO: External = symbolic i guess - SymbolModifiers::External, - ) - } - BNSymbolType::LocalLabel => { - // TODO: This is a placeholder for another symbol. - Symbol::new(symbol_name, SymbolClass::Data, SymbolModifiers::External) - } - BNSymbolType::External => Symbol::new( - symbol_name, - // TODO: External data? - SymbolClass::Function, - SymbolModifiers::External, - ), - BNSymbolType::ImportedData => { - Symbol::new(symbol_name, SymbolClass::Data, SymbolModifiers::External) - } - BNSymbolType::LibraryFunction | BNSymbolType::Function => Symbol::new( - symbol_name, - SymbolClass::Function, - SymbolModifiers::default(), - ), - BNSymbolType::ImportedFunction => Symbol::new( - symbol_name, - SymbolClass::Function, - // TODO: Exported? - SymbolModifiers::External, - ), - } -} - -pub fn to_bn_symbol_at_address(view: &BinaryView, symbol: &Symbol, addr: u64) -> BNRef<BNSymbol> { - let is_external = symbol.modifiers.contains(SymbolModifiers::External); - let _is_exported = symbol.modifiers.contains(SymbolModifiers::Exported); - let symbol_type = match symbol.class { - SymbolClass::Function if is_external => BNSymbolType::ImportedFunction, - // TODO: We should instead make it a Function, however due to the nature of the imports we are setting them to library for now. - SymbolClass::Function => BNSymbolType::LibraryFunction, - SymbolClass::Data if is_external => BNSymbolType::ImportedData, - SymbolClass::Data => BNSymbolType::Data, - }; - let raw_name = symbol.name.as_str(); - let mut symbol_builder = BNSymbol::builder(symbol_type, &symbol.name, addr); - // Demangle symbol name (short is with simplifications). - if let Some(arch) = view.default_arch() { - if let Some((full_name, _)) = - binaryninja::demangle::demangle_generic(&arch, raw_name, Some(view), false) - { - symbol_builder = symbol_builder.full_name(full_name); - } - if let Some((short_name, _)) = - binaryninja::demangle::demangle_generic(&arch, raw_name, Some(view), false) - { - symbol_builder = symbol_builder.short_name(short_name); - } - } - symbol_builder.create() -} - -pub fn from_bn_type(view: &BinaryView, raw_ty: &BNType, confidence: u8) -> Type { - from_bn_type_internal(view, &mut HashSet::new(), raw_ty, confidence) -} - -pub fn from_bn_type_internal( - view: &BinaryView, - visited_refs: &mut HashSet<TypeRefID>, - raw_ty: &BNType, - confidence: u8, -) -> Type { - let bytes_to_bits = |val| val * 8; - let raw_ty_bit_width = bytes_to_bits(raw_ty.width()); - let type_class = match raw_ty.type_class() { - BNTypeClass::VoidTypeClass => TypeClass::Void, - BNTypeClass::BoolTypeClass => { - let bool_class = BooleanClass { width: None }; - TypeClass::Boolean(bool_class) - } - BNTypeClass::IntegerTypeClass => { - let signed = raw_ty.is_signed().contents; - let width = Some(raw_ty_bit_width as u16); - if signed && width == Some(8) { - // NOTE: if its an i8, its a char. - let char_class = CharacterClass { width: None }; - TypeClass::Character(char_class) - } else { - let int_class = IntegerClass { width, signed }; - TypeClass::Integer(int_class) - } - } - BNTypeClass::FloatTypeClass => { - let float_class = FloatClass { - width: Some(raw_ty_bit_width as u16), - }; - TypeClass::Float(float_class) - } - // TODO: Union????? - BNTypeClass::StructureTypeClass => { - let raw_struct = raw_ty.get_structure().unwrap(); - - let mut members = raw_struct - .members() - .into_iter() - .map(|raw_member| { - let bit_offset = bytes_to_bits(raw_member.offset); - let mut modifiers = StructureMemberModifiers::empty(); - // If this member is not public mark it as internal. - modifiers.set( - StructureMemberModifiers::Internal, - !matches!(raw_member.access, MemberAccess::PublicAccess), - ); - StructureMember { - name: Some(raw_member.name), - offset: bit_offset, - ty: from_bn_type_internal( - view, - visited_refs, - &raw_member.ty.contents, - raw_member.ty.confidence, - ), - modifiers, - } - }) - .collect::<Vec<_>>(); - - // Add base structures as flattened members - let base_to_member_iter = raw_struct.base_structures().into_iter().map(|base_struct| { - let bit_offset = bytes_to_bits(base_struct.offset); - let mut modifiers = StructureMemberModifiers::empty(); - modifiers.set(StructureMemberModifiers::Flattened, true); - let base_struct_ty = from_bn_type_internal( - view, - visited_refs, - &BNType::named_type(&base_struct.ty), - MAX_CONFIDENCE, - ); - StructureMember { - name: base_struct_ty.name.to_owned(), - offset: bit_offset, - ty: base_struct_ty, - modifiers, - } - }); - members.extend(base_to_member_iter); - - // TODO: Check if union - let struct_class = StructureClass::new(members); - TypeClass::Structure(struct_class) - } - BNTypeClass::EnumerationTypeClass => { - let raw_enum = raw_ty.get_enumeration().unwrap(); - - let enum_ty_signed = raw_ty.is_signed().contents; - let enum_ty = Type::builder::<String, _>() - .class(TypeClass::Integer(IntegerClass { - width: Some(raw_ty_bit_width as u16), - signed: enum_ty_signed, - })) - .build(); - - let members = raw_enum - .members() - .into_iter() - .map(|raw_member| EnumerationMember { - name: Some(raw_member.name), - constant: raw_member.value, - }) - .collect(); - - let enum_class = EnumerationClass::new(enum_ty, members); - TypeClass::Enumeration(enum_class) - } - BNTypeClass::PointerTypeClass => { - let raw_child_ty = raw_ty.target().unwrap(); - let ptr_class = PointerClass { - width: Some(raw_ty_bit_width as u16), - child_type: from_bn_type_internal( - view, - visited_refs, - &raw_child_ty.contents, - raw_child_ty.confidence, - ), - // TODO: Handle addressing. - addressing: PointerAddressing::Absolute, - }; - TypeClass::Pointer(ptr_class) - } - BNTypeClass::ArrayTypeClass => { - let length = raw_ty.count(); - let raw_member_ty = raw_ty.element_type().unwrap(); - let array_class = ArrayClass { - length: Some(length), - member_type: from_bn_type_internal( - view, - visited_refs, - &raw_member_ty.contents, - raw_member_ty.confidence, - ), - modifiers: ArrayModifiers::empty(), - }; - TypeClass::Array(array_class) - } - BNTypeClass::FunctionTypeClass => { - let in_members = raw_ty - .parameters() - .unwrap() - .into_iter() - .map(|raw_member| { - // TODO: Location... - let _location = Location::Register(RegisterLocation); - FunctionMember { - name: Some(raw_member.name), - ty: from_bn_type_internal( - view, - visited_refs, - &raw_member.ty.contents, - raw_member.ty.confidence, - ), - // TODO: Just omit location for now? - // TODO: Location should be optional... - locations: vec![], - } - }) - .collect(); - - let mut out_members = Vec::new(); - if let Some(return_ty) = raw_ty.return_value() { - out_members.push(FunctionMember { - name: None, - ty: from_bn_type_internal( - view, - visited_refs, - &return_ty.contents, - return_ty.confidence, - ), - locations: vec![], - }); - } - - let calling_convention = raw_ty - .calling_convention() - .map(|bn_cc| from_bn_calling_convention(bn_cc.contents)); - - let func_class = FunctionClass { - calling_convention, - in_members, - out_members, - }; - TypeClass::Function(func_class) - } - BNTypeClass::VarArgsTypeClass => TypeClass::Void, - BNTypeClass::ValueTypeClass => { - // What the is this. - TypeClass::Void - } - BNTypeClass::NamedTypeReferenceClass => { - let raw_ntr = raw_ty.get_named_type_reference().unwrap(); - let ref_id = TypeRefID::from(raw_ntr.as_ref()); - let mut ref_class = ReferrerClass::new(None, Some(raw_ntr.name().to_string())); - if visited_refs.insert(ref_id) { - // This ntr is NOT self-referential, meaning we can deduce a type GUID. - if let Some(computed_ty) = cached_type_reference(view, visited_refs, &raw_ntr) { - // NOTE: The GUID here must always equal the same for any given type for this to work effectively. - ref_class.guid = Some(computed_ty.guid); - } - visited_refs.remove(&ref_id); - } - TypeClass::Referrer(ref_class) - } - BNTypeClass::WideCharTypeClass => { - let char_class = CharacterClass { - width: Some(raw_ty_bit_width as u16), - }; - TypeClass::Character(char_class) - } - }; - - let name = raw_ty.registered_name().map(|n| n.name().to_string()); - - Type { - name, - class: Box::new(type_class), - confidence, - // TODO: Fill these out... - modifiers: vec![], - alignment: Default::default(), - // TODO: Filling this out is... weird. - // TODO: we _do_ want this for networked types (this is the only way we can update type is if we fill this out) - ancestors: vec![], +pub fn bn_comment_to_comment(func: &BNFunction, bn_comment: BNComment) -> FunctionComment { + let offset = (bn_comment.addr as i64) - (func.start() as i64); + FunctionComment { + offset, + text: bn_comment.comment, } } -pub fn from_bn_calling_convention(raw_cc: BNRef<BNCallingConvention>) -> CallingConvention { - // NOTE: Currently calling convention just stores the name. - CallingConvention::new(raw_cc.name().as_str()) -} - -pub fn to_bn_calling_convention<A: BNArchitecture>( - arch: &A, - calling_convention: &CallingConvention, -) -> BNRef<BNCallingConvention> { - for cc in &arch.calling_conventions() { - if cc.name().as_str() == calling_convention.name { - return cc.clone(); - } +pub fn comment_to_bn_comment(func: &BNFunction, comment: FunctionComment) -> BNComment { + BNComment { + addr: comment + .offset + .checked_add_unsigned(func.start()) + .unwrap_or_default() as u64, + comment: comment.text, } - arch.get_default_calling_convention().unwrap() } -pub fn to_bn_type<A: BNArchitecture>(arch: &A, ty: &Type) -> BNRef<BNType> { - let bits_to_bytes = |val: u64| (val / 8); - let addr_size = arch.address_size() as u64; - match ty.class.as_ref() { - TypeClass::Void => BNType::void(), - TypeClass::Boolean(_) => BNType::bool(), - TypeClass::Integer(c) => { - let width = c.width.map(|w| bits_to_bytes(w as _)).unwrap_or(4); - BNType::int(width as usize, c.signed) - } - TypeClass::Character(c) => match c.width { - Some(w) => BNType::wide_char(bits_to_bytes(w as _) as usize), - None => BNType::char(), - }, - TypeClass::Float(c) => { - let width = c.width.map(|w| bits_to_bytes(w as _)).unwrap_or(4); - BNType::float(width as usize) +pub fn platform_to_target(platform: &Platform) -> Target { + let arch_name = platform.arch().name(); + let platform_name = platform.name(); + // We do not want to populate the platform if we are actually only the architecture. + if arch_name == platform_name { + Target { + architecture: Some(arch_name), + platform: None, } - TypeClass::Pointer(ref c) => { - let child_type = to_bn_type(arch, &c.child_type); - let ptr_width = c.width.map(|w| bits_to_bytes(w as _)).unwrap_or(addr_size); - // TODO: Child type confidence - let constant = ty.is_const(); - let volatile = ty.is_volatile(); - // TODO: If the pointer is to a null terminated array of chars, make it a pointer to char - // TODO: Addressing mode - BNType::pointer_of_width(&child_type, ptr_width as usize, constant, volatile, None) - } - TypeClass::Array(c) => { - let member_type = to_bn_type(arch, &c.member_type); - // TODO: How to handle DST array (length is None) - BNType::array(&member_type, c.length.unwrap_or(0)) - } - TypeClass::Structure(c) => { - let mut builder = BNStructureBuilder::new(); - // TODO: Structure type class? - // TODO: Alignment - // TODO: Other modifiers? - let mut base_structs: Vec<BNBaseStructure> = Vec::new(); - for member in &c.members { - let member_type = BNConf::new(to_bn_type(arch, &member.ty), u8::MAX); - let member_name = member.name.to_owned().unwrap_or("field_OFFSET".into()); - let member_offset = bits_to_bytes(member.offset); - let member_access = if member - .modifiers - .contains(StructureMemberModifiers::Internal) - { - BNMemberAccess::PrivateAccess - } else { - BNMemberAccess::PublicAccess - }; - // TODO: Member scope - let member_scope = BNMemberScope::NoScope; - if member - .modifiers - .contains(StructureMemberModifiers::Flattened) - { - // Add member as a base structure to inherit its fields. - match member.ty.class.as_ref() { - TypeClass::Referrer(c) => { - // We only support base structures with a referrer right now. - let base_struct_ntr_name = - c.name.to_owned().unwrap_or("base_UNKNOWN".into()); - let base_struct_ntr = match c.guid { - Some(guid) => BNNamedTypeReference::new_with_id( - NamedTypeReferenceClass::UnknownNamedTypeClass, - &guid.to_string(), - base_struct_ntr_name, - ), - None => BNNamedTypeReference::new( - NamedTypeReferenceClass::UnknownNamedTypeClass, - base_struct_ntr_name, - ), - }; - base_structs.push(BNBaseStructure::new( - base_struct_ntr, - member_offset, - member.ty.size().unwrap_or(0), - )) - } - _ => { - log::error!( - "Adding base {:?} with invalid ty: {:?}", - ty.name, - member.ty - ); - } - } - } else { - builder.insert_member( - BNStructureMember::new( - member_type, - member_name, - member_offset, - member_access, - member_scope, - ), - false, - ); - } - } - builder.base_structures(&base_structs); - BNType::structure(&builder.finalize()) - } - TypeClass::Enumeration(c) => { - let mut builder = BNEnumerationBuilder::new(); - for member in &c.members { - // TODO: Add default name? - let member_name = member.name.to_owned().unwrap_or("enum_VAL".into()); - let member_value = member.constant; - builder.insert(&member_name, member_value); - } - // TODO: Warn if enumeration has no size. - let width = bits_to_bytes(c.member_type.size().unwrap()) as usize; - let signed = matches!(*c.member_type.class, TypeClass::Integer(c) if c.signed); - // TODO: Passing width like this is weird. - BNType::enumeration(&builder.finalize(), width.try_into().unwrap(), signed) - } - TypeClass::Union(c) => { - let mut builder = BNStructureBuilder::new(); - builder.structure_type(BNStructureType::UnionStructureType); - for member in &c.members { - let member_type = BNConf::new(to_bn_type(arch, &member.ty), u8::MAX); - let member_name = member.name.to_owned(); - // TODO: Member access - let member_access = BNMemberAccess::PublicAccess; - // TODO: Member scope - let member_scope = BNMemberScope::NoScope; - let structure_member = BNStructureMember::new( - member_type, - member_name, - 0, // Union members all exist at 0 right? - member_access, - member_scope, - ); - builder.insert_member(structure_member, false); - } - BNType::structure(&builder.finalize()) - } - TypeClass::Function(c) => { - let return_type = if !c.out_members.is_empty() { - // TODO: WTF - to_bn_type(arch, &c.out_members[0].ty) - } else { - BNType::void() - }; - let params: Vec<_> = c - .in_members - .iter() - .map(|member| { - let member_type = to_bn_type(arch, &member.ty); - let name = member.name.clone(); - // TODO: Location AND fix default param name - BNFunctionParameter::new(member_type, name.unwrap_or("param_IDK".into()), None) - }) - .collect(); - // TODO: Variable arguments - let variable_args = false; - // If we have a calling convention we run the extended function type creation. - match c.calling_convention.as_ref() { - Some(cc) => { - let calling_convention = to_bn_calling_convention(arch, cc); - BNType::function_with_opts( - &return_type, - ¶ms, - variable_args, - BNConf::new(calling_convention, u8::MAX), - BNConf::new(0, 0), - ) - } - None => BNType::function(&return_type, params, variable_args), - } - } - TypeClass::Referrer(c) => { - let ntr = match c.guid { - Some(guid) => { - let guid_str = guid.to_string(); - let ntr_name = c.name.to_owned().unwrap_or(guid_str.clone()); - NamedTypeReference::new_with_id( - NamedTypeReferenceClass::UnknownNamedTypeClass, - &guid_str, - ntr_name, - ) - } - None => match c.name.as_ref() { - Some(ntr_name) => NamedTypeReference::new( - NamedTypeReferenceClass::UnknownNamedTypeClass, - ntr_name, - ), - None => { - log::error!("Referrer with no reference! {:?}", c); - NamedTypeReference::new( - NamedTypeReferenceClass::UnknownNamedTypeClass, - "AHHHHHH", - ) - } - }, - }; - BNType::named_type(&ntr) + } else { + Target { + architecture: Some(arch_name), + platform: Some(platform_name), } } } -#[cfg(test)] -mod tests { - use super::*; - use binaryninja::binary_view::BinaryViewExt; - use binaryninja::headless::Session; - use std::path::PathBuf; - use std::sync::OnceLock; - use warp::r#type::guid::TypeGUID; - - static INIT: OnceLock<Session> = OnceLock::new(); - - fn get_session<'a>() -> &'a Session { - INIT.get_or_init(|| Session::new().expect("Failed to initialize session")) - } - - #[test] - fn type_conversion() { - let session = get_session(); - let out_dir = env!("OUT_DIR").parse::<PathBuf>().unwrap(); - for entry in std::fs::read_dir(out_dir).expect("Failed to read OUT_DIR") { - let entry = entry.expect("Failed to read directory entry"); - let path = entry.path(); - if path.is_file() { - if let Some(bv) = session.load(path.to_str().unwrap()) { - let types_len = bv.types().len(); - let converted_types: Vec<_> = bv - .types() - .iter() - .map(|qualified_name_and_type| { - let ty = from_bn_type(&bv, &qualified_name_and_type.ty, u8::MAX); - (TypeGUID::from(&ty), ty) - }) - .collect(); - assert_eq!(types_len, converted_types.len()); - } - } - } - } - - #[ignore] - #[test] - fn check_for_leaks() { - let session = get_session(); - let out_dir = env!("OUT_DIR").parse::<PathBuf>().unwrap(); - for entry in std::fs::read_dir(out_dir).expect("Failed to read OUT_DIR") { - let entry = entry.expect("Failed to read directory entry"); - let path = entry.path(); - if path.is_file() { - if let Some(inital_bv) = session.load(path.to_str().unwrap()) { - let types_len = inital_bv.types().len(); - let converted_types: Vec<_> = inital_bv - .types() - .iter() - .map(|t| { - let ty = from_bn_type(&inital_bv, &t.ty, u8::MAX); - (TypeGUID::from(&ty), ty) - }) - .collect(); - assert_eq!(types_len, converted_types.len()); - // Hold on to a reference to the core to prevent view getting dropped in worker thread. - let _core_ref = inital_bv - .functions() - .iter() - .next() - .map(|f| f.unresolved_stack_adjustment_graph()); - // Drop the file and view. - inital_bv.file().close(); - std::mem::drop(inital_bv); - let initial_memory_info = binaryninja::memory_info(); - if let Some(second_bv) = session.load(path.to_str().unwrap()) { - let types_len = second_bv.types().len(); - let converted_types: Vec<_> = second_bv - .types() - .iter() - .map(|t| { - let ty = from_bn_type(&second_bv, &t.ty, u8::MAX); - (TypeGUID::from(&ty), ty) - }) - .collect(); - assert_eq!(types_len, converted_types.len()); - // Hold on to a reference to the core to prevent view getting dropped in worker thread. - let _core_ref = second_bv - .functions() - .iter() - .next() - .map(|f| f.unresolved_stack_adjustment_graph()); - // Drop the file and view. - second_bv.file().close(); - std::mem::drop(second_bv); - let final_memory_info = binaryninja::memory_info(); - for info in initial_memory_info { - let initial_count = info.1; - if let Some(&final_count) = final_memory_info.get(&info.0) { - assert!( - final_count <= initial_count, - "{}: final objects {} vs initial objects {}", - info.0, - final_count, - initial_count - ); - } - } - } - } - } - } +pub fn target_to_platform(target: Target) -> Option<Ref<Platform>> { + // First try using the platform, then try using the arch. + match Platform::by_name(&target.platform.unwrap()) { + None => Platform::by_name(&target.architecture.unwrap()).map(|platform| platform), + Some(platform) => Some(platform), } } |
