libmwemu 0.24.5

x86 32/64bits and system internals emulator, for securely emulating malware and other stuff.
Documentation
use crate::windows::structures;

use super::PE32;

macro_rules! read_u16_le {
    ($raw:expr, $off:expr) => {
        u16::from_le_bytes([$raw[$off], $raw[$off + 1]])
    };
}

macro_rules! read_u32_le {
    ($raw:expr, $off:expr) => {
        u32::from_le_bytes([
            $raw[$off],
            $raw[$off + 1],
            $raw[$off + 2],
            $raw[$off + 3],
        ])
    };
}

impl PE32 {
    pub fn pe32_locate_resource_data_entry(
        &self,
        rsrc: &[u8],
        off: usize,
        level: u32,
        type_id: Option<u32>,
        name_id: Option<u32>,
        type_name: Option<&str>,
        name: Option<&str>,
    ) -> Option<structures::ImageResourceDataEntry32> {
        if level >= 10 {
            return None;
        }

        let mut dir = structures::ImageResourceDirectory::new();
        dir.characteristics = read_u32_le!(rsrc, off);
        dir.time_date_stamp = read_u32_le!(rsrc, off + 4);
        dir.major_version = read_u16_le!(rsrc, off + 8);
        dir.minor_version = read_u16_le!(rsrc, off + 10);
        dir.number_of_named_entries = read_u16_le!(rsrc, off + 12);
        dir.number_of_id_entries = read_u16_le!(rsrc, off + 14);

        let entries = dir.number_of_named_entries + dir.number_of_id_entries;

        for i in 0..entries {
            let mut entry = structures::ImageResourceDirectoryEntry::new();
            let off2 = off + i as usize * 8 + structures::ImageResourceDirectory::size() as usize;
            entry.name_or_id = read_u32_le!(rsrc, off2);
            entry.data_or_directory = read_u32_le!(rsrc, off2 + 4);

            let matched: bool;

            if entry.is_id() {
                if level == 0 && type_id.is_some() && type_id.unwrap() == entry.get_name_or_id() {
                    matched = true;
                } else if level == 1
                    && name_id.is_some()
                    && name_id.unwrap() == entry.get_name_or_id()
                {
                    matched = true;
                } else if level > 1 {
                    matched = true;
                } else {
                    matched = false;
                }
            } else if level == 0
                && type_name.is_some()
                && type_name.unwrap() == self.get_resource_name(&entry)
            {
                matched = true;
            } else if level == 1
                && name.is_some()
                && name.unwrap() == self.get_resource_name(&entry)
            {
                matched = true;
            } else {
                matched = level > 1;
            }

            if matched {
                if entry.is_directory() {
                    return self.locate_resource_data_entry(
                        rsrc,
                        off2,
                        level + 1,
                        type_id,
                        name_id,
                        type_name,
                        name,
                    );
                } else {
                    let mut data_entry = structures::ImageResourceDataEntry32::new();
                    let off = PE32::vaddr_to_off(&self.sect_hdr, entry.get_offset()) as usize;
                    data_entry.offset_to_data = read_u32_le!(self.raw, off);
                    data_entry.size = read_u32_le!(self.raw, off + 4);
                    data_entry.code_page = read_u32_le!(self.raw, off + 8);
                    data_entry.reserved = read_u32_le!(self.raw, off + 12);

                    return Some(data_entry);
                }
            }
        }

        None
    }

    pub fn pe32_get_resource(
        &self,
        type_id: Option<u32>,
        name_id: Option<u32>,
        type_name: Option<&str>,
        name: Option<&str>,
    ) -> Option<(u64, usize)> {
        let rsrc = self.get_section_ptr_by_name(".rsrc")?;
        let data_entry =
            self.locate_resource_data_entry(rsrc, 0, 0, type_id, name_id, type_name, name)?;
        let data_off = PE32::vaddr_to_off(&self.sect_hdr, data_entry.offset_to_data as u32)
            as usize
            - self.opt.image_base as usize;
        Some((data_off as u64, data_entry.size as usize))
    }

    pub fn pe32_get_resource_name(&self, entry: &structures::ImageResourceDirectoryEntry) -> String {
        let off = PE32::vaddr_to_off(&self.sect_hdr, entry.get_name_or_id() as u32) as usize;
        let length = u16::from_le_bytes([self.raw[off], self.raw[off + 1]]) as usize;
        let string_start = off + 2;
        let utf16_data: Vec<u16> = (0..length)
            .map(|i| {
                let idx = string_start + i * 2;
                u16::from_le_bytes([self.raw[idx], self.raw[idx + 1]])
            })
            .collect();

        String::from_utf16_lossy(&utf16_data)
    }
}