use crate::{
emulation::{
runtime::hook::{Hook, HookContext, HookManager, PreHookResult},
thread::{EmulationThread, ReflectionInvokeRequest},
EmValue, HeapObject,
},
metadata::token::Token,
};
pub fn register(manager: &mut HookManager) {
manager.register(
Hook::new("System.Type.get_Module")
.match_name("System", "Type", "get_Module")
.pre(type_get_module_pre),
);
manager.register(
Hook::new("System.Type.GetTypeFromHandle")
.match_name("System", "Type", "GetTypeFromHandle")
.pre(type_get_type_from_handle_pre),
);
manager.register(
Hook::new("System.Type.GetMethod")
.match_name("System", "Type", "GetMethod")
.pre(type_get_method_pre),
);
manager.register(
Hook::new("System.Type.GetField")
.match_name("System", "Type", "GetField")
.pre(type_get_field_pre),
);
manager.register(
Hook::new("System.Type.GetProperty")
.match_name("System", "Type", "GetProperty")
.pre(type_get_property_pre),
);
manager.register(
Hook::new("System.Type.GetConstructor")
.match_name("System", "Type", "GetConstructor")
.pre(type_get_constructor_pre),
);
manager.register(
Hook::new("System.Reflection.MethodBase.Invoke")
.match_name("System.Reflection", "MethodBase", "Invoke")
.pre(method_invoke_pre),
);
manager.register(
Hook::new("System.Reflection.MethodInfo.Invoke")
.match_name("System.Reflection", "MethodInfo", "Invoke")
.pre(method_invoke_pre),
);
manager.register(
Hook::new("System.Reflection.ConstructorInfo.Invoke")
.match_name("System.Reflection", "ConstructorInfo", "Invoke")
.pre(constructor_invoke_pre),
);
manager.register(
Hook::new("System.Reflection.Module.get_FullyQualifiedName")
.match_name("System.Reflection", "Module", "get_FullyQualifiedName")
.pre(module_get_fully_qualified_name_pre),
);
manager.register(
Hook::new("System.Reflection.Module.get_Assembly")
.match_name("System.Reflection", "Module", "get_Assembly")
.pre(module_get_assembly_pre),
);
manager.register(
Hook::new("System.Reflection.Module.ResolveMethod")
.match_name("System.Reflection", "Module", "ResolveMethod")
.pre(module_resolve_method_pre),
);
manager.register(
Hook::new("System.Reflection.Module.ResolveType")
.match_name("System.Reflection", "Module", "ResolveType")
.pre(module_resolve_type_pre),
);
manager.register(
Hook::new("System.Reflection.Assembly.get_GlobalAssemblyCache")
.match_name("System.Reflection", "Assembly", "get_GlobalAssemblyCache")
.pre(|_ctx, _thread| {
PreHookResult::Bypass(Some(EmValue::I32(0)))
}),
);
manager.register(
Hook::new("System.Reflection.FieldInfo.GetValue")
.match_name("System.Reflection", "FieldInfo", "GetValue")
.pre(field_get_value_pre),
);
manager.register(
Hook::new("System.Reflection.FieldInfo.SetValue")
.match_name("System.Reflection", "FieldInfo", "SetValue")
.pre(field_set_value_pre),
);
}
fn type_get_module_pre(_ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
match thread.heap_mut().alloc_object(Token::new(0x0100_0003)) {
Ok(module_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(module_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
fn type_get_type_from_handle_pre(
_ctx: &HookContext<'_>,
thread: &mut EmulationThread,
) -> PreHookResult {
match thread.heap_mut().alloc_object(Token::new(0x0100_0001)) {
Ok(type_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(type_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
fn type_get_method_pre(_ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
match thread.heap_mut().alloc_object(Token::new(0x0100_0002)) {
Ok(method_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(method_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
fn type_get_field_pre(_ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
match thread.heap_mut().alloc_object(Token::new(0x0100_0004)) {
Ok(field_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(field_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
fn type_get_property_pre(_ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
match thread.heap_mut().alloc_object(Token::new(0x0100_0005)) {
Ok(prop_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(prop_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
fn type_get_constructor_pre(_ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
match thread.heap_mut().alloc_object(Token::new(0x0100_0006)) {
Ok(ctor_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(ctor_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
fn method_invoke_pre(ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
if let Some(EmValue::ObjectRef(method_ref)) = ctx.this {
if let Ok(HeapObject::ReflectionMethod { method_token }) = thread.heap().get(*method_ref) {
let this_ref = ctx.args.first().cloned();
let method_args = if let Some(EmValue::ObjectRef(arr_ref)) = ctx.args.get(1) {
if let Ok(HeapObject::Array { elements, .. }) = thread.heap().get(*arr_ref) {
elements
} else {
Vec::new()
}
} else {
Vec::new()
};
thread.set_pending_reflection_invoke(ReflectionInvokeRequest {
method_token,
this_ref,
args: method_args,
});
return PreHookResult::Bypass(Some(EmValue::Null));
}
}
PreHookResult::Bypass(Some(EmValue::Null))
}
fn constructor_invoke_pre(_ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
match thread.heap_mut().alloc_object(Token::new(0x0100_0007)) {
Ok(obj_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(obj_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
fn module_get_fully_qualified_name_pre(
_ctx: &HookContext<'_>,
thread: &mut EmulationThread,
) -> PreHookResult {
match thread
.heap_mut()
.alloc_string("C:\\Program Files\\App\\module.exe")
{
Ok(str_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(str_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
fn module_get_assembly_pre(_ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
if let Some(asm_ref) = thread.fake_objects().assembly() {
return PreHookResult::Bypass(Some(EmValue::ObjectRef(asm_ref)));
}
match thread.heap_mut().alloc_object(Token::new(0x0100_0008)) {
Ok(asm_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(asm_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
fn module_resolve_method_pre(ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
#[allow(clippy::cast_sign_loss)]
let method_token = if let Some(token_value) = ctx.args.first().and_then(EmValue::as_i32) {
Token::new(token_value as u32)
} else {
Token::new(0x0100_0002)
};
match thread.heap_mut().alloc_reflection_method(method_token) {
Ok(method_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(method_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
fn module_resolve_type_pre(_ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
match thread.heap_mut().alloc_object(Token::new(0x0100_0001)) {
Ok(type_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(type_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
fn field_get_value_pre(_ctx: &HookContext<'_>, _thread: &mut EmulationThread) -> PreHookResult {
PreHookResult::Bypass(Some(EmValue::Null))
}
fn field_set_value_pre(_ctx: &HookContext<'_>, _thread: &mut EmulationThread) -> PreHookResult {
PreHookResult::Bypass(None)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
emulation::runtime::hook::HookManager, metadata::typesystem::PointerSize,
test::emulation::create_test_thread,
};
#[test]
fn test_register_hooks() {
let mut manager = HookManager::new();
register(&mut manager);
assert_eq!(manager.len(), 16);
}
#[test]
fn test_get_module_hook() {
let ctx = HookContext::new(
Token::new(0x0A000001),
"System",
"Type",
"get_Module",
PointerSize::Bit64,
);
let mut thread = create_test_thread();
let result = type_get_module_pre(&ctx, &mut thread);
match result {
PreHookResult::Bypass(Some(EmValue::ObjectRef(_))) => {}
_ => panic!("Expected Bypass with ObjectRef"),
}
}
#[test]
fn test_get_type_from_handle_hook() {
let ctx = HookContext::new(
Token::new(0x0A000001),
"System",
"Type",
"GetTypeFromHandle",
PointerSize::Bit64,
);
let mut thread = create_test_thread();
let result = type_get_type_from_handle_pre(&ctx, &mut thread);
match result {
PreHookResult::Bypass(Some(EmValue::ObjectRef(_))) => {}
_ => panic!("Expected Bypass with ObjectRef"),
}
}
#[test]
fn test_method_invoke_hook() {
let ctx = HookContext::new(
Token::new(0x0A000001),
"System.Reflection",
"MethodBase",
"Invoke",
PointerSize::Bit64,
);
let mut thread = create_test_thread();
let result = method_invoke_pre(&ctx, &mut thread);
match result {
PreHookResult::Bypass(Some(EmValue::Null)) => {}
_ => panic!("Expected Bypass with Null"),
}
}
#[test]
fn test_field_get_value_hook() {
let ctx = HookContext::new(
Token::new(0x0A000001),
"System.Reflection",
"FieldInfo",
"GetValue",
PointerSize::Bit64,
);
let mut thread = create_test_thread();
let result = field_get_value_pre(&ctx, &mut thread);
match result {
PreHookResult::Bypass(Some(EmValue::Null)) => {}
_ => panic!("Expected Bypass with Null"),
}
}
}