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::PE64;
use crate::loaders::pe::readers::{
    read_u16_le as read_u16_le_shared, read_u32_le as read_u32_le_shared,
};

macro_rules! read_u16_le {
    ($raw:expr, $off:expr) => {
        read_u16_le_shared(($raw).as_ref(), $off)
    };
}

macro_rules! read_u32_le {
    ($raw:expr, $off:expr) => {
        read_u32_le_shared(($raw).as_ref(), $off)
    };
}

impl PE64 {
    pub(crate) fn pe64_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::ImageResourceDataEntry64> {
        if level >= 10 {
            log::warn!("Resource directory recursion limit reached");
            return None;
        }

        if off + 16 > rsrc.len() {
            log::warn!(
                "Resource directory at offset {} is out of bounds (rsrc size: {})",
                off,
                rsrc.len()
            );
            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 entry_off = off + (i as usize * 8) + 16;
            if entry_off + 8 > rsrc.len() {
                log::warn!(
                    "Resource directory entry {} at offset {} is out of bounds",
                    i,
                    entry_off
                );
                continue;
            }

            let mut entry = structures::ImageResourceDirectoryEntry::new();
            entry.name_or_id = read_u32_le!(rsrc, entry_off);
            entry.data_or_directory = read_u32_le!(rsrc, entry_off + 4);

            let matched: bool;

            if entry.is_id() {
                let entry_id = entry.get_name_or_id();
                if level == 0 && type_id.is_some() && type_id.unwrap() == entry_id {
                    matched = true;
                } else if level == 1 && name_id.is_some() && name_id.unwrap() == entry_id {
                    matched = true;
                } else if level == 2 {
                    matched = true;
                } else {
                    matched = false;
                }
            } else {
                let name_offset = (entry.get_name_or_id() & 0x7FFFFFFF) as usize;
                let rsrc_section = self.get_section_ptr_by_name(".rsrc");
                if rsrc_section.is_none() {
                    continue;
                }

                if name_offset >= rsrc.len() {
                    continue;
                }

                let resource_name = self.pe64_read_resource_name_from_rsrc(rsrc, name_offset);
                if level == 0 && type_name.is_some() && type_name.unwrap() == resource_name {
                    matched = true;
                } else if level == 1 && name.is_some() && name.unwrap() == resource_name {
                    matched = true;
                } else {
                    matched = false;
                }
            }

            if matched {
                if entry.is_directory() {
                    let next_dir_offset = entry.get_offset() & 0x7FFFFFFF;
                    return self.pe64_locate_resource_data_entry(
                        rsrc,
                        next_dir_offset as usize,
                        level + 1,
                        type_id,
                        name_id,
                        type_name,
                        name,
                    );
                } else {
                    let data_entry_offset = entry.get_offset();
                    if data_entry_offset as usize + 16 > rsrc.len() {
                        return None;
                    }

                    let mut data_entry = structures::ImageResourceDataEntry64::new();
                    data_entry.offset_to_data =
                        read_u32_le!(rsrc, data_entry_offset as usize) as u64;
                    data_entry.size = read_u32_le!(rsrc, data_entry_offset as usize + 4) as u64;
                    data_entry.code_page =
                        read_u32_le!(rsrc, data_entry_offset as usize + 8) as u64;
                    data_entry.reserved =
                        read_u32_le!(rsrc, data_entry_offset as usize + 12) as u64;

                    return Some(data_entry);
                }
            }
        }

        None
    }

    pub(crate) fn pe64_read_resource_name_from_rsrc(
        &self,
        rsrc: &[u8],
        offset: usize,
    ) -> String {
        if offset + 1 >= rsrc.len() {
            return String::new();
        }

        let length = u16::from_le_bytes([rsrc[offset], rsrc[offset + 1]]) as usize;
        let string_start = offset + 2;

        let required_bytes = string_start + (length * 2);
        if required_bytes > rsrc.len() {
            return String::new();
        }

        let utf16_data: Vec<u16> = (0..length)
            .map(|i| {
                let idx = string_start + i * 2;
                u16::from_le_bytes([rsrc[idx], rsrc[idx + 1]])
            })
            .collect();

        String::from_utf16_lossy(&utf16_data)
    }

    pub(crate) fn pe64_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.pe64_locate_resource_data_entry(rsrc, 0, 0, type_id, name_id, type_name, name)?;
        let data_off = PE64::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(crate) fn pe64_get_resource_name(
        &self,
        entry: &structures::ImageResourceDirectoryEntry,
    ) -> String {
        let rsrc = match self.get_section_ptr_by_name(".rsrc") {
            Some(rsrc) => rsrc,
            None => return String::new(),
        };

        let name_offset = (entry.get_name_or_id() & 0x7FFFFFFF) as usize;
        self.pe64_read_resource_name_from_rsrc(rsrc, name_offset)
    }
}