#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
use anyhow::Result;
use byteorder::{ByteOrder, LittleEndian};
use log::debug;
use crate::{aspace::AddressSpace, loader::pe::PE, RVA};
pub struct ResourceSectionData {
buf: Vec<u8>,
}
impl ResourceSectionData {
#[allow(clippy::unnecessary_wraps)]
fn read_u16(&self, offset: usize) -> Result<u16> {
let buf = &self.buf[offset..offset + 2];
Ok(LittleEndian::read_u16(buf))
}
#[allow(clippy::unnecessary_wraps)]
fn read_u32(&self, offset: usize) -> Result<u32> {
let buf = &self.buf[offset..offset + 4];
Ok(LittleEndian::read_u32(buf))
}
#[allow(clippy::unnecessary_wraps)]
fn read_buf(&self, offset: usize, length: usize) -> Result<Vec<u8>> {
Ok(self.buf[offset..offset + length].to_vec())
}
pub fn root(&self) -> Result<ResourceNode> {
ResourceNode::read(self, 0x0)
}
pub fn from_pe(pe: &PE) -> Result<Option<ResourceSectionData>> {
let opt_header = match pe.optional_header {
None => return Ok(None),
Some(opt_header) => opt_header,
};
let rsrc_table = match opt_header.data_directories.get_resource_table() {
None => return Ok(None),
Some(rsrc_table) => rsrc_table,
};
debug!(
"rsrc: table at {:#x}-{:#x}",
pe.module.address_space.base_address + rsrc_table.virtual_address as RVA,
pe.module.address_space.base_address + rsrc_table.virtual_address as RVA + rsrc_table.size as RVA
);
let buf = pe.module.address_space.read_bytes(
pe.module.address_space.base_address + rsrc_table.virtual_address as RVA,
rsrc_table.size as usize,
)?;
Ok(Some(ResourceSectionData { buf }))
}
}
struct ResourceNodeHeader {
_flags: u32,
_timestamp: u32,
_major_version: u16,
_minor_version: u16,
named_entry_count: u16,
id_entry_count: u16,
}
impl ResourceNodeHeader {
fn read(rsrc: &ResourceSectionData, offset: usize) -> Result<ResourceNodeHeader> {
Ok(ResourceNodeHeader {
_flags: rsrc.read_u32(offset)?,
_timestamp: rsrc.read_u32(offset + 4)?,
_major_version: rsrc.read_u16(offset + 8)?,
_minor_version: rsrc.read_u16(offset + 10)?,
named_entry_count: rsrc.read_u16(offset + 12)?,
id_entry_count: rsrc.read_u16(offset + 14)?,
})
}
}
#[derive(Clone)]
pub struct ResourceNodeEntry {
id: u32,
offset: u32,
}
impl ResourceNodeEntry {
fn read(rsrc: &ResourceSectionData, offset: usize) -> Result<ResourceNodeEntry> {
Ok(ResourceNodeEntry {
id: rsrc.read_u32(offset)?,
offset: rsrc.read_u32(offset + 4)?,
})
}
fn has_name(&self) -> bool {
self.id & 0x8000_0000 > 0
}
fn is_branch_node(&self) -> bool {
self.offset & 0x8000_0000 > 0
}
pub fn id(&self, rsrc: &ResourceSectionData) -> Result<NodeIdentifier> {
let offset = self.id & 0x7FFF_FFFF;
if self.has_name() {
Ok(NodeIdentifier::Name(
ResourceNodeName::read(rsrc, offset as usize)?.name()?,
))
} else {
Ok(NodeIdentifier::ID(offset))
}
}
pub fn child(&self, rsrc: &ResourceSectionData) -> Result<NodeChild> {
let offset = (self.offset & 0x7FFF_FFFF) as usize;
if self.is_branch_node() {
Ok(NodeChild::Node(ResourceNode::read(rsrc, offset)?))
} else {
Ok(NodeChild::Data(ResourceDataDescriptor::read(rsrc, offset)?))
}
}
}
pub struct ResourceNode {
_header: ResourceNodeHeader,
named_entries: Vec<ResourceNodeEntry>,
id_entries: Vec<ResourceNodeEntry>,
}
impl ResourceNode {
fn read(rsrc: &ResourceSectionData, offset: usize) -> Result<ResourceNode> {
let header = ResourceNodeHeader::read(rsrc, offset)?;
let mut named_entries = vec![];
let mut id_entries = vec![];
let mut offset = offset + 16;
for _ in 0..header.named_entry_count {
named_entries.push(ResourceNodeEntry::read(rsrc, offset)?);
offset += 8;
}
for _ in 0..header.id_entry_count {
id_entries.push(ResourceNodeEntry::read(rsrc, offset)?);
offset += 8;
}
Ok(ResourceNode {
_header: header,
named_entries,
id_entries,
})
}
pub fn get_child_by_name(&self, rsrc: &ResourceSectionData, name: &str) -> Result<Option<NodeChild>> {
for child in self.named_entries.iter() {
match child.id(rsrc)? {
NodeIdentifier::ID(_) => continue,
NodeIdentifier::Name(child_name) => {
if child_name == name {
return Ok(Some(child.child(rsrc)?));
}
}
}
}
Ok(None)
}
pub fn get_child_by_id(&self, rsrc: &ResourceSectionData, id: u32) -> Result<Option<NodeChild>> {
for child in self.id_entries.iter() {
match child.id(rsrc)? {
NodeIdentifier::Name(_) => continue,
NodeIdentifier::ID(i) => {
if i == id {
return Ok(Some(child.child(rsrc)?));
}
}
}
}
Ok(None)
}
pub fn children(&self, rsrc: &ResourceSectionData) -> Result<Vec<(ResourceNodeEntry, NodeChild)>> {
let mut ret = vec![];
for entry in self.named_entries.iter().cloned() {
let child = entry.child(rsrc)?;
ret.push((entry, child));
}
for entry in self.id_entries.iter().cloned() {
let child = entry.child(rsrc)?;
ret.push((entry, child));
}
Ok(ret)
}
}
#[derive(Debug)]
pub enum ResourceDataType {
RT_CURSOR = 1,
RT_BITMAP = 2,
RT_ICON = 3,
RT_MENU = 4,
RT_DIALOG = 5,
RT_STRING = 6,
RT_FONTDIR = 7,
RT_FONT = 8,
RT_ACCELERATOR = 9,
RT_RCDATA = 10,
RT_MESSAGETABLE = 11,
RT_GROUP_CURSOR = 12,
RT_GROUP_ICON = 14,
RT_VERSION = 16,
RT_DLGINCLUDE = 17,
RT_PLUGPLAY = 19,
RT_VXD = 20,
RT_ANICURSOR = 21,
RT_ANIICON = 22,
RT_HTML = 23,
RT_MANIFEST = 24,
}
impl ResourceDataType {
pub fn from_u32(v: u32) -> Option<ResourceDataType> {
match v {
1 => Some(ResourceDataType::RT_CURSOR),
2 => Some(ResourceDataType::RT_BITMAP),
3 => Some(ResourceDataType::RT_ICON),
4 => Some(ResourceDataType::RT_MENU),
5 => Some(ResourceDataType::RT_DIALOG),
6 => Some(ResourceDataType::RT_STRING),
7 => Some(ResourceDataType::RT_FONTDIR),
8 => Some(ResourceDataType::RT_FONT),
9 => Some(ResourceDataType::RT_ACCELERATOR),
10 => Some(ResourceDataType::RT_RCDATA),
11 => Some(ResourceDataType::RT_MESSAGETABLE),
12 => Some(ResourceDataType::RT_GROUP_CURSOR),
14 => Some(ResourceDataType::RT_GROUP_ICON),
16 => Some(ResourceDataType::RT_VERSION),
17 => Some(ResourceDataType::RT_DLGINCLUDE),
19 => Some(ResourceDataType::RT_PLUGPLAY),
20 => Some(ResourceDataType::RT_VXD),
21 => Some(ResourceDataType::RT_ANICURSOR),
22 => Some(ResourceDataType::RT_ANIICON),
23 => Some(ResourceDataType::RT_HTML),
24 => Some(ResourceDataType::RT_MANIFEST),
_ => None,
}
}
}
pub enum NodeIdentifier {
Name(String),
ID(u32),
}
struct ResourceNodeName {
character_buf: Vec<u8>,
}
impl ResourceNodeName {
fn read(rsrc: &ResourceSectionData, offset: usize) -> Result<ResourceNodeName> {
let character_count = rsrc.read_u16(offset)?;
let character_buf = rsrc.read_buf(offset + 2, 2 * character_count as usize)?;
Ok(ResourceNodeName { character_buf })
}
fn name(&self) -> Result<String> {
let chars: Vec<u16> = self
.character_buf
.as_chunks::<2>()
.0
.iter()
.map(|c| LittleEndian::read_u16(c))
.collect();
widestring::U16String::from_vec(chars).to_string().map_err(|e| e.into())
}
}
pub struct ResourceDataDescriptor {
pub rva: u32,
pub size: u32,
}
impl ResourceDataDescriptor {
fn read(rsrc: &ResourceSectionData, offset: usize) -> Result<ResourceDataDescriptor> {
Ok(ResourceDataDescriptor {
rva: rsrc.read_u32(offset)?,
size: rsrc.read_u32(offset + 4)?,
})
}
pub fn data(&self, pe: &PE) -> Result<Vec<u8>> {
pe.module
.address_space
.relative
.read_bytes(self.rva as RVA, self.size as usize)
}
}
pub enum NodeChild {
Node(ResourceNode),
Data(ResourceDataDescriptor),
}