use crate::{
emulation::{
capture::{AssemblyLoadMethod, CaptureSource},
runtime::hook::{Hook, HookContext, HookManager, PreHookResult},
thread::EmulationThread,
EmValue,
},
metadata::{token::Token, typesystem::CilFlavor},
};
pub fn register(manager: &mut HookManager) {
manager.register(
Hook::new("System.AppDomain.get_CurrentDomain")
.match_name("System", "AppDomain", "get_CurrentDomain")
.pre(appdomain_get_current_domain_pre),
);
manager.register(
Hook::new("System.AppDomain.add_AssemblyResolve")
.match_name("System", "AppDomain", "add_AssemblyResolve")
.pre(appdomain_add_assembly_resolve_pre),
);
manager.register(
Hook::new("System.AppDomain.remove_AssemblyResolve")
.match_name("System", "AppDomain", "remove_AssemblyResolve")
.pre(appdomain_remove_assembly_resolve_pre),
);
manager.register(
Hook::new("System.AppDomain.GetAssemblies")
.match_name("System", "AppDomain", "GetAssemblies")
.pre(appdomain_get_assemblies_pre),
);
manager.register(
Hook::new("System.Reflection.Assembly.Load")
.match_name("System.Reflection", "Assembly", "Load")
.pre(assembly_load_pre),
);
manager.register(
Hook::new("System.Reflection.Assembly.LoadFrom")
.match_name("System.Reflection", "Assembly", "LoadFrom")
.pre(assembly_load_from_pre),
);
manager.register(
Hook::new("System.Reflection.Assembly.GetExecutingAssembly")
.match_name("System.Reflection", "Assembly", "GetExecutingAssembly")
.pre(assembly_get_executing_assembly_pre),
);
manager.register(
Hook::new("System.Reflection.Assembly.GetCallingAssembly")
.match_name("System.Reflection", "Assembly", "GetCallingAssembly")
.pre(assembly_get_calling_assembly_pre),
);
manager.register(
Hook::new("System.Reflection.Assembly.GetEntryAssembly")
.match_name("System.Reflection", "Assembly", "GetEntryAssembly")
.pre(assembly_get_entry_assembly_pre),
);
manager.register(
Hook::new("System.Reflection.Assembly.GetManifestResourceStream")
.match_name("System.Reflection", "Assembly", "GetManifestResourceStream")
.pre(assembly_get_manifest_resource_stream_pre),
);
manager.register(
Hook::new("System.Reflection.Assembly.GetManifestResourceNames")
.match_name("System.Reflection", "Assembly", "GetManifestResourceNames")
.pre(assembly_get_manifest_resource_names_pre),
);
manager.register(
Hook::new("System.Delegate..ctor")
.match_name("System", "Delegate", ".ctor")
.pre(delegate_ctor_pre),
);
manager.register(
Hook::new("System.MulticastDelegate..ctor")
.match_name("System", "MulticastDelegate", ".ctor")
.pre(delegate_ctor_pre),
);
manager.register(
Hook::new("System.ResolveEventHandler..ctor")
.match_name("System", "ResolveEventHandler", ".ctor")
.pre(delegate_ctor_pre),
);
}
fn appdomain_get_current_domain_pre(
_ctx: &HookContext<'_>,
thread: &mut EmulationThread,
) -> PreHookResult {
if let Some(domain_ref) = thread.fake_objects().app_domain() {
return PreHookResult::Bypass(Some(EmValue::ObjectRef(domain_ref)));
}
match thread.heap_mut().alloc_object(Token::new(0x0100_0011)) {
Ok(domain_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(domain_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
fn appdomain_add_assembly_resolve_pre(
_ctx: &HookContext<'_>,
_thread: &mut EmulationThread,
) -> PreHookResult {
PreHookResult::Bypass(None)
}
fn appdomain_remove_assembly_resolve_pre(
_ctx: &HookContext<'_>,
_thread: &mut EmulationThread,
) -> PreHookResult {
PreHookResult::Bypass(None)
}
fn appdomain_get_assemblies_pre(
_ctx: &HookContext<'_>,
thread: &mut EmulationThread,
) -> PreHookResult {
match thread.heap_mut().alloc_array(CilFlavor::Object, 0) {
Ok(array_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(array_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
fn assembly_load_pre(ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
if let Some(EmValue::ObjectRef(array_ref)) = ctx.args.first() {
if let Some(bytes) = thread.heap().get_byte_array(*array_ref) {
let source = CaptureSource::new(
thread.current_method().unwrap_or(Token::new(0)),
thread.id(),
thread.current_offset().unwrap_or(0),
0,
);
thread
.capture()
.capture_assembly(bytes, source, AssemblyLoadMethod::LoadBytes, None);
}
}
match thread.heap_mut().alloc_object(Token::new(0x0100_0010)) {
Ok(assembly_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(assembly_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
fn assembly_load_from_pre(_ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
match thread.heap_mut().alloc_object(Token::new(0x0100_0010)) {
Ok(assembly_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(assembly_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
fn assembly_get_executing_assembly_pre(
_ctx: &HookContext<'_>,
thread: &mut EmulationThread,
) -> PreHookResult {
if let Some(assembly_ref) = thread.fake_objects().assembly() {
return PreHookResult::Bypass(Some(EmValue::ObjectRef(assembly_ref)));
}
match thread.heap_mut().alloc_object(Token::new(0x0100_0010)) {
Ok(assembly_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(assembly_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
fn assembly_get_calling_assembly_pre(
_ctx: &HookContext<'_>,
thread: &mut EmulationThread,
) -> PreHookResult {
if let Some(assembly_ref) = thread.fake_objects().assembly() {
return PreHookResult::Bypass(Some(EmValue::ObjectRef(assembly_ref)));
}
match thread.heap_mut().alloc_object(Token::new(0x0100_0010)) {
Ok(assembly_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(assembly_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
fn assembly_get_entry_assembly_pre(
_ctx: &HookContext<'_>,
thread: &mut EmulationThread,
) -> PreHookResult {
if let Some(assembly_ref) = thread.fake_objects().assembly() {
return PreHookResult::Bypass(Some(EmValue::ObjectRef(assembly_ref)));
}
match thread.heap_mut().alloc_object(Token::new(0x0100_0010)) {
Ok(assembly_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(assembly_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
fn assembly_get_manifest_resource_stream_pre(
ctx: &HookContext<'_>,
thread: &mut EmulationThread,
) -> PreHookResult {
let resource_name = match ctx.args.first() {
Some(EmValue::ObjectRef(href)) => thread
.heap()
.get_string(*href)
.ok()
.map(|arc| arc.to_string()),
_ => None,
};
let Some(resource_name) = resource_name else {
return PreHookResult::Bypass(Some(EmValue::Null));
};
let Some(assembly) = thread.assembly() else {
return PreHookResult::Bypass(Some(EmValue::Null));
};
let resources = assembly.resources();
let resource = resources.get(&resource_name);
let Some(resource) = resource else {
return PreHookResult::Bypass(Some(EmValue::Null));
};
let Some(data) = resources.get_data(&resource) else {
return PreHookResult::Bypass(Some(EmValue::Null));
};
match thread.heap_mut().alloc_stream(data.to_vec()) {
Ok(stream_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(stream_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
fn assembly_get_manifest_resource_names_pre(
_ctx: &HookContext<'_>,
thread: &mut EmulationThread,
) -> PreHookResult {
match thread.heap_mut().alloc_array(CilFlavor::String, 0) {
Ok(array_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(array_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
fn delegate_ctor_pre(_ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
match thread.heap_mut().alloc_object(Token::new(0x0100_0012)) {
Ok(delegate_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(delegate_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
#[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(), 14);
}
#[test]
fn test_get_current_domain() {
let ctx = HookContext::new(
Token::new(0x0A000001),
"System",
"AppDomain",
"get_CurrentDomain",
PointerSize::Bit64,
);
let mut thread = create_test_thread();
let result = appdomain_get_current_domain_pre(&ctx, &mut thread);
match result {
PreHookResult::Bypass(Some(EmValue::ObjectRef(_))) => {}
_ => panic!("Expected Bypass with ObjectRef"),
}
}
#[test]
fn test_assembly_load_captures_bytes() {
let ctx = HookContext::new(
Token::new(0x0A000001),
"System.Reflection",
"Assembly",
"Load",
PointerSize::Bit64,
);
let mut thread = create_test_thread();
let test_data = vec![0x4D, 0x5A, 0x90, 0x00]; let array_ref = thread.heap_mut().alloc_byte_array(&test_data).unwrap();
let args = [EmValue::ObjectRef(array_ref)];
let ctx = ctx.with_args(&args);
let result = assembly_load_pre(&ctx, &mut thread);
match result {
PreHookResult::Bypass(Some(EmValue::ObjectRef(_))) => {}
_ => panic!("Expected Bypass with ObjectRef"),
}
let captured = thread.capture().assemblies();
assert_eq!(captured.len(), 1);
assert_eq!(captured[0].data, test_data);
}
}