bininfo/pe/
resource.rs

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
use exe::{
    ImageDirectoryEntry, ImageResourceDataEntry, PETranslation, ResolvedDirectoryID,
    ResourceDirectory, ResourceID, PE, RVA,
};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, PartialOrd, Ord, Eq)]
pub struct ResourceEntry {
    pub resource_type: String,
    pub offset: Option<u32>,
    pub resource_id: String,
    pub language_id: String,
    pub data_start: Option<usize>,
    pub data_end: Option<usize>,
}

#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, PartialOrd, Ord, Eq)]
pub struct Resources {
    pub minor_version: u16,
    pub major_version: u16,
    pub number_of_id_entries: u16,
    pub number_of_named_entries: u16,
    pub timestamp: u32,
    pub resources: Vec<ResourceEntry>,
}

impl Resources {
    pub fn parse<P: PE>(pe: &P) -> Result<Option<Resources>, exe::Error> {
        if !pe.has_data_directory(ImageDirectoryEntry::Resource) {
            return Ok(None);
        }
        let rsrc = ResourceDirectory::parse(pe)?;

        let mut result: Resources = Resources {
            minor_version: rsrc.root_node.directory.minor_version,
            major_version: rsrc.root_node.directory.major_version,
            number_of_id_entries: rsrc.root_node.directory.number_of_id_entries,
            number_of_named_entries: rsrc.root_node.directory.number_of_named_entries,
            timestamp: rsrc.root_node.directory.time_date_stamp,
            resources: vec![],
        };
        for entry in rsrc.resources {
            let mut resource_entry = ResourceEntry::default();
            resource_entry.resource_type = match &entry.type_id {
                ResolvedDirectoryID::ID(id) => {
                    resource_entry.offset = Some(*id);
                    resource_id_to_type(ResourceID::from_u32(*id))
                }
                ResolvedDirectoryID::Name(name) => name.to_owned(),
            };
            resource_entry.data_end = None;
            resource_entry.data_start = None;
            resource_entry.resource_id = format!("{:?}", entry.rsrc_id).to_string();
            resource_entry.language_id = format!("{:?}", entry.lang_id).to_string();

            let data_entry = entry.get_data_entry(pe).unwrap_or(&ImageResourceDataEntry {
                offset_to_data: RVA(0),
                size: 0,
                code_page: 0,
                reserved: 0,
            });

            let offset = pe.translate(PETranslation::Memory(data_entry.offset_to_data));
            if let Ok(offset) = offset {
                resource_entry.data_start = Some(offset);
                resource_entry.data_end = Some(offset + data_entry.size as usize);
            };

            result.resources.push(resource_entry);
        }

        Ok(Some(result))
    }
}

pub fn resource_id_to_type(id: ResourceID) -> String {
    match id {
        ResourceID::Cursor => "Cursor".to_owned(),
        ResourceID::Bitmap => "Bitmap".to_owned(),
        ResourceID::Icon => "Icon".to_owned(),
        ResourceID::Menu => "Menu".to_owned(),
        ResourceID::Dialog => "Dialog".to_owned(),
        ResourceID::String => "String".to_owned(),
        ResourceID::FontDir => "FontDir".to_owned(),
        ResourceID::Font => "Font".to_owned(),
        ResourceID::Accelerator => "Accelerator".to_owned(),
        ResourceID::RCData => "RCData".to_owned(),
        ResourceID::MessageTable => "MessageTable".to_owned(),
        ResourceID::GroupCursor => "GroupCursor".to_owned(),
        ResourceID::Reserved => "Reserved".to_owned(),
        ResourceID::GroupIcon => "GroupIcon".to_owned(),
        ResourceID::Reserved2 => "Reserved2".to_owned(),
        ResourceID::Version => "Version".to_owned(),
        ResourceID::DlgInclude => "DlgInclude".to_owned(),
        ResourceID::Reserved3 => "Reserved3".to_owned(),
        ResourceID::PlugPlay => "PlugPlay".to_owned(),
        ResourceID::VXD => "VXD".to_owned(),
        ResourceID::AniCursor => "AniCursor".to_owned(),
        ResourceID::AniIcon => "AniIcon".to_owned(),
        ResourceID::HTML => "HTML".to_owned(),
        ResourceID::Manifest => "Manifest".to_owned(),
        ResourceID::Unknown => "Unknown".to_owned(),
    }
}