summaryrefslogtreecommitdiff
path: root/plugins/bntl_utils/src/dump.rs
blob: d86500c01730622a3cf33f31f6ff8dc545312fc6 (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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
use binaryninja::binary_view::BinaryView;
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<Ref<TypeLibrary>>,
}

impl TILDump {
    pub fn new() -> Self {
        Self {
            available_type_libs: Vec::new(),
        }
    }

    pub fn with_type_libs(mut self, type_libs: Vec<Ref<TypeLibrary>>) -> Self {
        self.available_type_libs = type_libs;
        self
    }

    pub fn dump(&self, type_lib: &TypeLibrary) -> Result<String, TILDumpError> {
        let empty_file = FileMetadata::new();
        let empty_bv = BinaryView::from_data(&empty_file, &[]);

        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(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<Vec<String>, 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<Metadata>| -> Result<(), TILDumpError> {
        if let Some(map) = metadata.get_value_store() {
            let mut list = map.iter().collect::<Vec<_>>();
            list.sort_by_key(|&(key, _)| key.parse::<u64>().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)
}