From 37008b7fa16837d04c1658868646cad681cbe035 Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Wed, 11 Feb 2026 18:04:07 -0800 Subject: Add BNTL utility plugin Allow users to easily create, diff, dump and validate type libraries Supports the following formats: - C header files (via core type parsers) - Binary files (collects exported and imported functions) - WinMD files (via `windows-metadata` crate) - Existing type library files (for easy fixups) - Apiset files (to resolve through forwarded windows dlls) Can be invoked as a regular plugin via UI commands or via CLI. Processing of type libraries inherently requires external linking, processing will automatically merge and deduplicate colliding type libraries so prefer to use inside a project or a directory and process all information (for a given platform) at once, rather than smaller invocations. --- plugins/bntl_utils/src/dump.rs | 146 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 plugins/bntl_utils/src/dump.rs (limited to 'plugins/bntl_utils/src/dump.rs') diff --git a/plugins/bntl_utils/src/dump.rs b/plugins/bntl_utils/src/dump.rs new file mode 100644 index 00000000..ddc9f602 --- /dev/null +++ b/plugins/bntl_utils/src/dump.rs @@ -0,0 +1,146 @@ +use binaryninja::binary_view::{BinaryView, BinaryViewExt}; +use binaryninja::file_metadata::FileMetadata; +use binaryninja::metadata::{Metadata, MetadataType}; +use binaryninja::platform::Platform; +use binaryninja::rc::Ref; +use binaryninja::types::printer::TokenEscapingType; +use binaryninja::types::{CoreTypePrinter, TypeLibrary}; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum TILDumpError { + #[error("Failed to create empty BinaryView")] + ViewCreationFailed, + + #[error("Type library has no associated platforms")] + NoPlatformFound, + + #[error("Platform '{0}' not found in Binary Ninja")] + PlatformNotFound(String), + + #[error("Failed to print types from library")] + PrinterError, + + #[error("Metadata error: {0}")] + MetadataError(String), + + #[error("Unexpected metadata type for 'ordinals': {0:?}")] + UnexpectedMetadataType(MetadataType), +} + +pub struct TILDump { + /// The type libraries that are accessible to the type printer. + available_type_libs: Vec>, +} + +impl TILDump { + pub fn new() -> Self { + Self { + available_type_libs: Vec::new(), + } + } + + pub fn with_type_libs(mut self, type_libs: Vec>) -> Self { + self.available_type_libs = type_libs; + self + } + + pub fn dump(&self, type_lib: &TypeLibrary) -> Result { + let empty_file = FileMetadata::new(); + let empty_bv = BinaryView::from_data(&empty_file, &[]) + .map_err(|_| TILDumpError::ViewCreationFailed)?; + + let type_lib_plats = type_lib.platform_names(); + let platform_name = type_lib_plats + .iter() + .next() + .ok_or(TILDumpError::NoPlatformFound)?; + + let platform_name_str = platform_name.to_string(); + let platform = Platform::by_name(&platform_name_str) + .ok_or_else(|| TILDumpError::PlatformNotFound(platform_name_str))?; + + empty_bv.set_default_platform(&platform); + + for dependency in &self.available_type_libs { + empty_bv.add_type_library(dependency); + } + empty_bv.add_type_library(type_lib); + + for ty in &type_lib.named_types() { + empty_bv.import_type_library_type(ty.name, None); + } + for obj in &type_lib.named_objects() { + empty_bv.import_type_library_object(obj.name, None); + } + + let dep_sorted_types = empty_bv.dependency_sorted_types(); + let unsorted_functions = type_lib.named_objects(); + let mut all_types: Vec<_> = dep_sorted_types + .iter() + .chain(unsorted_functions.iter()) + .collect(); + all_types.sort_by_key(|t| t.name.clone()); + + let type_printer = CoreTypePrinter::default(); + let printed_types = type_printer + .print_all_types( + all_types, + &empty_bv, + 4, + TokenEscapingType::NoTokenEscapingType, + ) + .ok_or(TILDumpError::PrinterError)?; + + let mut printed_types_str = printed_types.to_string_lossy().to_string(); + printed_types_str.push_str("\n// TYPE LIBRARY INFORMATION\n"); + + let metadata_lines = type_library_metadata_to_string(type_lib)?; + printed_types_str.push_str(&metadata_lines.join("\n")); + + empty_file.close(); + Ok(printed_types_str) + } +} + +fn type_library_metadata_to_string(type_lib: &TypeLibrary) -> Result, TILDumpError> { + let mut result = Vec::new(); + for alt_name in &type_lib.alternate_names() { + result.push(format!("// ALTERNATE NAME: {}", alt_name)); + } + + let mut add_ordinals = |metadata: Ref| -> Result<(), TILDumpError> { + if let Some(map) = metadata.get_value_store() { + let mut list = map.iter().collect::>(); + list.sort_by_key(|&(key, _)| key.parse::().unwrap_or_default()); + for (key, value) in list { + result.push(format!("// ORDINAL {}: {}", key, value)); + } + } + Ok(()) + }; + + if let Some(ordinal_key) = type_lib.query_metadata("ordinals") { + match ordinal_key.get_type() { + MetadataType::StringDataType => { + let queried_key = ordinal_key.get_string().ok_or_else(|| { + TILDumpError::MetadataError("Failed to get ordinal key string".into()) + })?; + + let queried_key_str = queried_key.to_string_lossy(); + let queried_md = type_lib.query_metadata(&queried_key_str).ok_or_else(|| { + TILDumpError::MetadataError(format!( + "Failed to query metadata for key: {}", + queried_key_str + )) + })?; + + add_ordinals(queried_md)?; + } + MetadataType::KeyValueDataType => add_ordinals(ordinal_key)?, + ty => return Err(TILDumpError::UnexpectedMetadataType(ty)), + } + } + + Ok(result) +} -- cgit v1.3.1