diff options
Diffstat (limited to 'plugins/warp/src/convert')
| -rw-r--r-- | plugins/warp/src/convert/symbol.rs | 91 | ||||
| -rw-r--r-- | plugins/warp/src/convert/types.rs | 516 |
2 files changed, 607 insertions, 0 deletions
diff --git a/plugins/warp/src/convert/symbol.rs b/plugins/warp/src/convert/symbol.rs new file mode 100644 index 00000000..0107cff5 --- /dev/null +++ b/plugins/warp/src/convert/symbol.rs @@ -0,0 +1,91 @@ +use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::rc::Ref as BNRef; +use binaryninja::symbol::Symbol as BNSymbol; +use binaryninja::symbol::SymbolType as BNSymbolType; +use warp::symbol::{Symbol, SymbolClass, SymbolModifiers}; + +pub fn from_bn_symbol(raw_symbol: &BNSymbol) -> Symbol { + // TODO: Use this? + let _is_export = raw_symbol.external(); + let raw_symbol_name = raw_symbol.raw_name(); + let symbol_name = raw_symbol_name.to_string_lossy(); + 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, + _ => 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() +} diff --git a/plugins/warp/src/convert/types.rs b/plugins/warp/src/convert/types.rs new file mode 100644 index 00000000..1dfe5fa9 --- /dev/null +++ b/plugins/warp/src/convert/types.rs @@ -0,0 +1,516 @@ +use crate::cache::{cached_type_reference, TypeRefID}; +use binaryninja::architecture::Architecture as BNArchitecture; +use binaryninja::architecture::ArchitectureExt; +use binaryninja::binary_view::BinaryView; +use binaryninja::calling_convention::CoreCallingConvention as BNCallingConvention; +use binaryninja::confidence::Conf as BNConf; +use binaryninja::confidence::MAX_CONFIDENCE; +use binaryninja::rc::Ref as BNRef; +use binaryninja::types::BaseStructure as BNBaseStructure; +use binaryninja::types::EnumerationBuilder as BNEnumerationBuilder; +use binaryninja::types::FunctionParameter as BNFunctionParameter; +use binaryninja::types::MemberAccess as BNMemberAccess; +use binaryninja::types::MemberScope as BNMemberScope; +use binaryninja::types::NamedTypeReference as BNNamedTypeReference; +use binaryninja::types::StructureBuilder as BNStructureBuilder; +use binaryninja::types::StructureMember as BNStructureMember; +use binaryninja::types::StructureType as BNStructureType; +use binaryninja::types::Type as BNType; +use binaryninja::types::TypeClass as BNTypeClass; +use binaryninja::types::{NamedTypeReference, NamedTypeReferenceClass}; +use std::collections::HashSet; +use warp::r#type::class::array::ArrayModifiers; +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, TypeModifiers}; + +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, BNMemberAccess::PublicAccess), + ); + StructureMember { + name: Some(raw_member.name), + offset: bit_offset, + ty: Box::new(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: Box::new(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: Box::new(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: Box::new(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: Box::new(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... + location: None, + } + }) + .collect(); + + let mut out_members = Vec::new(); + if let Some(return_ty) = raw_ty.return_value() { + out_members.push(FunctionMember { + name: None, + ty: Box::new(from_bn_type_internal( + view, + visited_refs, + &return_ty.contents, + return_ty.confidence, + )), + location: None, + }); + } + + 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: type_class, + confidence, + // TODO: Fill these out... + modifiers: TypeModifiers::empty(), + metadata: 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 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(); + } + } + 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 { + 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) + } + 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 { + 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::TypedefNamedTypeClass, + &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) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use binaryninja::binary_view::BinaryViewExt; + use binaryninja::headless::Session; + use std::path::PathBuf; + use warp::r#type::guid::TypeGUID; + + #[test] + fn type_conversion() { + let session = Session::new().expect("Failed to initialize 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) { + 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()); + } + } + } + } +} |
