use std::collections::HashMap;
use crate::{
metadata::{
imports::ImportType,
method::MethodBody,
signatures::{parse_field_signature, TypeSignature},
tables::{ClassLayoutRaw, FieldRaw, MethodDefRaw},
token::Token,
},
CilObject,
};
pub(super) const MAX_METHOD_BODY_SIZE: usize = 65536;
pub(super) fn build_pinvoke_import_map(assembly: &CilObject) -> HashMap<Token, String> {
let mut map = HashMap::new();
for import_entry in assembly.imports().cil() {
let import = import_entry.value();
if let ImportType::Method(method) = &import.import {
map.insert(method.token, import.name.clone());
}
}
map
}
pub(super) fn resolve_call_target(
assembly: &CilObject,
token: Token,
import_map: &HashMap<Token, String>,
) -> Option<String> {
if token.table() == 0x06 {
if let Some(import_name) = import_map.get(&token) {
return Some(import_name.clone());
}
}
assembly.resolve_method_name(token)
}
pub(super) fn get_type_name_from_token(assembly: &CilObject, token: Token) -> Option<String> {
if let Some(cil_type) = assembly.types().get(&token) {
return Some(cil_type.fullname());
}
if let Some(member_ref) = assembly.member_ref(&token) {
if let Some(type_name) = member_ref.declaredby.fullname() {
return Some(format!("{}::{}", type_name, member_ref.name));
}
}
None
}
pub fn find_encrypted_methods(assembly: &CilObject) -> Vec<Token> {
assembly
.methods()
.iter()
.filter_map(|entry| {
let method = entry.value();
if method.rva.is_some_and(|rva| rva > 0) && !method.has_body() {
Some(method.token)
} else {
None
}
})
.collect()
}
pub(super) fn get_method_rva(assembly: &CilObject, token: Token) -> Option<u32> {
let tables = assembly.tables()?;
let method_table = tables.table::<MethodDefRaw>()?;
let row = token.row();
let method_row = method_table.get(row)?;
Some(method_row.rva)
}
pub(super) fn extract_method_body_at_rva(memory: &[u8], rva: u32) -> Option<Vec<u8>> {
let rva_usize = rva as usize;
if rva_usize >= memory.len() {
return None;
}
let available = memory.len() - rva_usize;
let read_size = available.min(MAX_METHOD_BODY_SIZE);
let body_slice = &memory[rva_usize..rva_usize + read_size];
let body = MethodBody::from(body_slice).ok()?;
let il_start = body.size_header;
let il_end = il_start + body.size_code;
if il_end > body_slice.len() {
return None;
}
let il_code = &body_slice[il_start..il_end];
let mut output = Vec::new();
body.write_to(&mut output, il_code).ok()?;
Some(output)
}
pub(super) fn get_field_data_size(assembly: &CilObject, field_rid: u32) -> Option<usize> {
let tables = assembly.tables()?;
let blobs = assembly.blob()?;
let field_table = tables.table::<FieldRaw>()?;
let field_row = field_table.get(field_rid)?;
let sig_data = blobs.get(field_row.signature as usize).ok()?;
let field_sig = parse_field_signature(sig_data).ok()?;
match &field_sig.base {
TypeSignature::ValueType(token) => {
if token.table() != 0x02 {
return None;
}
let type_rid = token.row();
let class_layout_table = tables.table::<ClassLayoutRaw>()?;
for layout in class_layout_table {
if layout.parent == type_rid {
return Some(layout.class_size as usize);
}
}
None
}
_ => None,
}
}