summaryrefslogtreecommitdiff
path: root/rust/plugin_examples/data_renderer/src/lib.rs
blob: e9c6b354860b5b6c521f26729a730ded20eb2fcd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
use binaryninja::binary_view::{BinaryView, BinaryViewBase};
use binaryninja::data_renderer::{
    register_data_renderer, CustomDataRenderer, RegistrationType, TypeContext,
};
use binaryninja::disassembly::{
    DisassemblyTextLine, InstructionTextToken, InstructionTextTokenKind,
};
use binaryninja::types::{Type, TypeClass};
use uuid::Uuid;

struct UuidDataRenderer {}

impl CustomDataRenderer for UuidDataRenderer {
    const REGISTRATION_TYPE: RegistrationType = RegistrationType::Specific;

    fn is_valid_for_data(
        &self,
        _view: &BinaryView,
        _addr: u64,
        type_: &Type,
        types: &[TypeContext],
    ) -> bool {
        // We only want to render arrays with a size of 16 elements
        if type_.type_class() != TypeClass::ArrayTypeClass {
            return false;
        }
        if type_.count() != 0x10 {
            return false;
        }

        // The array elements must be of the type uint8_t
        let Some(element_type_conf) = type_.element_type() else {
            return false;
        };
        let element_type = element_type_conf.contents;
        if element_type.type_class() != TypeClass::IntegerTypeClass {
            return false;
        }
        if element_type.width() != 1 {
            return false;
        }

        // The array should be embedded in a named type reference with the id macho:["uuid"]
        for type_ctx in types {
            if type_ctx.ty().type_class() != TypeClass::NamedTypeReferenceClass {
                continue;
            }

            let Some(name_ref) = type_ctx.ty().get_named_type_reference() else {
                continue;
            };

            if name_ref.id() == "macho:[\"uuid\"]" {
                return true;
            }
        }

        false
    }

    fn lines_for_data(
        &self,
        view: &BinaryView,
        addr: u64,
        _type_: &Type,
        prefix: Vec<InstructionTextToken>,
        _width: usize,
        _types_ctx: &[TypeContext],
        _language: &str,
    ) -> Vec<DisassemblyTextLine> {
        let mut tokens = prefix.clone();

        let mut buf = [0u8; 0x10];
        let bytes_read = view.read(&mut buf, addr);

        // Make sure that we've read all UUID bytes and convert them to token
        if bytes_read == 0x10 {
            tokens.extend([
                InstructionTextToken::new("UUID(\"", InstructionTextTokenKind::Text),
                InstructionTextToken::new(
                    Uuid::from_bytes(buf).to_string(),
                    InstructionTextTokenKind::String { value: 0 },
                ),
                InstructionTextToken::new("\")", InstructionTextTokenKind::Text),
            ]);
        } else {
            tokens.push(InstructionTextToken::new(
                "error: cannot read 0x10 bytes",
                InstructionTextTokenKind::Annotation,
            ));
        }

        vec![DisassemblyTextLine::new_with_addr(tokens, addr)]
    }
}

/// # Safety
/// This function is called from Binary Ninja once to initialize the plugin.
#[allow(non_snake_case)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn CorePluginInit() -> bool {
    // Initialize logging
    binaryninja::tracing_init!();
    tracing::info!("Core plugin initialized");

    // Register data renderer
    register_data_renderer(UuidDataRenderer {});

    true
}