|
| 1 | +use binaryninja::binary_view::{BinaryView, BinaryViewBase}; |
| 2 | +use binaryninja::data_renderer::{ |
| 3 | + CustomDataRenderer, TypeContext, register_specific_data_renderer, |
| 4 | +}; |
| 5 | +use binaryninja::disassembly::{ |
| 6 | + DisassemblyTextLine, InstructionTextToken, InstructionTextTokenKind, |
| 7 | +}; |
| 8 | +use binaryninja::types::{Type, TypeClass}; |
| 9 | +use log::debug; |
| 10 | +use uuid::Uuid; |
| 11 | + |
| 12 | +struct UuidDataRenderer {} |
| 13 | + |
| 14 | +impl CustomDataRenderer for UuidDataRenderer { |
| 15 | + fn is_valid_for_data( |
| 16 | + &self, |
| 17 | + view: &BinaryView, |
| 18 | + addr: u64, |
| 19 | + type_: &Type, |
| 20 | + types: &[TypeContext], |
| 21 | + ) -> bool { |
| 22 | + // We only want to render arrays with a size of 16 elements |
| 23 | + if type_.type_class() != TypeClass::ArrayTypeClass { |
| 24 | + return false; |
| 25 | + } |
| 26 | + if type_.count() != 0x10 { |
| 27 | + return false; |
| 28 | + } |
| 29 | + |
| 30 | + // The array elements must be of the type uint8_t |
| 31 | + let Some(element_type_conf) = type_.element_type() else { |
| 32 | + return false; |
| 33 | + }; |
| 34 | + let element_type = element_type_conf.contents; |
| 35 | + if element_type.type_class() != TypeClass::IntegerTypeClass { |
| 36 | + return false; |
| 37 | + } |
| 38 | + if element_type.width() != 1 { |
| 39 | + return false; |
| 40 | + } |
| 41 | + |
| 42 | + // The array should be embedded in a named type reference with the id macho:["uuid"] |
| 43 | + for type_ctx in types { |
| 44 | + if type_ctx.type_().type_class() != TypeClass::NamedTypeReferenceClass { |
| 45 | + continue; |
| 46 | + } |
| 47 | + |
| 48 | + let Some(name_ref) = type_ctx.type_().get_named_type_reference() else { |
| 49 | + continue; |
| 50 | + }; |
| 51 | + |
| 52 | + if name_ref.id() == "macho:[\"uuid\"]" { |
| 53 | + return true; |
| 54 | + } |
| 55 | + } |
| 56 | + |
| 57 | + false |
| 58 | + } |
| 59 | + |
| 60 | + fn lines_for_data( |
| 61 | + &self, |
| 62 | + view: &BinaryView, |
| 63 | + addr: u64, |
| 64 | + type_: &Type, |
| 65 | + prefix: Vec<InstructionTextToken>, |
| 66 | + width: usize, |
| 67 | + types_ctx: &[TypeContext], |
| 68 | + language: &str, |
| 69 | + ) -> Vec<DisassemblyTextLine> { |
| 70 | + let mut tokens = prefix.clone(); |
| 71 | + |
| 72 | + let mut buf = [0u8; 0x10]; |
| 73 | + let bytes_read = view.read(&mut buf, addr); |
| 74 | + |
| 75 | + // Make sure that we've read all UUID bytes and convert them to token |
| 76 | + if bytes_read == 0x10 { |
| 77 | + tokens.extend([ |
| 78 | + InstructionTextToken::new("UUID(\"", InstructionTextTokenKind::Text), |
| 79 | + InstructionTextToken::new( |
| 80 | + Uuid::from_bytes(buf).to_string(), |
| 81 | + InstructionTextTokenKind::String { value: 0 }, |
| 82 | + ), |
| 83 | + InstructionTextToken::new("\")", InstructionTextTokenKind::Text), |
| 84 | + ]); |
| 85 | + } else { |
| 86 | + tokens.push(InstructionTextToken::new( |
| 87 | + "error: cannot read 0x10 bytes", |
| 88 | + InstructionTextTokenKind::Annotation, |
| 89 | + )); |
| 90 | + } |
| 91 | + |
| 92 | + vec![DisassemblyTextLine::new_with_addr(tokens, addr)] |
| 93 | + } |
| 94 | +} |
| 95 | + |
| 96 | +#[allow(non_snake_case)] |
| 97 | +#[unsafe(no_mangle)] |
| 98 | +pub unsafe extern "C" fn CorePluginInit() -> bool { |
| 99 | + // Initialize logging |
| 100 | + binaryninja::logger::Logger::new("UUID Data Renderer") |
| 101 | + .with_level(log::LevelFilter::Debug) |
| 102 | + .init(); |
| 103 | + |
| 104 | + // Register data renderer |
| 105 | + register_specific_data_renderer(UuidDataRenderer {}); |
| 106 | + |
| 107 | + true |
| 108 | +} |
0 commit comments