mod dictionary;
mod generic;
mod list;
use crate::{
emulation::{
memory::DictionaryKey,
runtime::hook::{Hook, HookContext, HookManager, PreHookResult},
thread::EmulationThread,
EmValue,
},
Result,
};
pub fn register(manager: &HookManager) -> Result<()> {
dictionary::register(manager)?;
generic::register(manager)?;
list::register(manager)?;
manager.register(
Hook::new("System.Collections.IEnumerator.MoveNext")
.match_name("System.Collections", "IEnumerator", "MoveNext")
.pre(ienumerator_move_next_pre),
)?;
manager.register(
Hook::new("System.Collections.IEnumerator.get_Current")
.match_name("System.Collections", "IEnumerator", "get_Current")
.pre(ienumerator_get_current_pre),
)?;
manager.register(
Hook::new("System.Collections.IEnumerator.Reset")
.match_name("System.Collections", "IEnumerator", "Reset")
.pre(ienumerator_reset_pre),
)?;
manager.register(
Hook::new("System.Collections.ReadOnlyCollectionBase.GetEnumerator")
.match_name(
"System.Collections",
"ReadOnlyCollectionBase",
"GetEnumerator",
)
.pre(collection_get_enumerator_pre),
)?;
manager.register(
Hook::new("System.Collections.Hashtable..ctor")
.match_name("System.Collections", "Hashtable", ".ctor")
.pre(hashtable_ctor_pre),
)?;
manager.register(
Hook::new("System.Collections.Hashtable.Add")
.match_name("System.Collections", "Hashtable", "Add")
.pre(hashtable_add_pre),
)?;
manager.register(
Hook::new("System.Collections.Hashtable.ContainsKey")
.match_name("System.Collections", "Hashtable", "ContainsKey")
.pre(hashtable_contains_key_pre),
)?;
manager.register(
Hook::new("System.Collections.Hashtable.Contains")
.match_name("System.Collections", "Hashtable", "Contains")
.pre(hashtable_contains_key_pre),
)?;
manager.register(
Hook::new("System.Collections.Hashtable.get_Item")
.match_name("System.Collections", "Hashtable", "get_Item")
.pre(hashtable_get_item_pre),
)?;
manager.register(
Hook::new("System.Collections.Hashtable.set_Item")
.match_name("System.Collections", "Hashtable", "set_Item")
.pre(hashtable_set_item_pre),
)?;
manager.register(
Hook::new("System.Collections.Hashtable.get_Count")
.match_name("System.Collections", "Hashtable", "get_Count")
.pre(hashtable_get_count_pre),
)?;
Ok(())
}
fn ienumerator_move_next_pre(
_ctx: &HookContext<'_>,
_thread: &mut EmulationThread,
) -> PreHookResult {
PreHookResult::Bypass(Some(EmValue::I32(0))) }
fn ienumerator_get_current_pre(
_ctx: &HookContext<'_>,
_thread: &mut EmulationThread,
) -> PreHookResult {
PreHookResult::Bypass(Some(EmValue::Null))
}
fn ienumerator_reset_pre(_ctx: &HookContext<'_>, _thread: &mut EmulationThread) -> PreHookResult {
PreHookResult::Bypass(None)
}
fn collection_get_enumerator_pre(
_ctx: &HookContext<'_>,
thread: &mut EmulationThread,
) -> PreHookResult {
match thread
.heap_mut()
.alloc_object(crate::emulation::tokens::collections::ENUMERATOR)
{
Ok(obj_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(obj_ref))),
Err(_) => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
fn hashtable_ctor_pre(ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
if let Some(EmValue::ObjectRef(this_ref)) = ctx.this {
let _ = thread.heap_mut().replace_with_dictionary(*this_ref);
}
PreHookResult::Bypass(None)
}
fn hashtable_add_pre(ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
if let Some(EmValue::ObjectRef(this_ref)) = ctx.this {
let key = ctx
.args
.first()
.and_then(|v| DictionaryKey::from_emvalue(v, thread.heap()));
let value = ctx.args.get(1).cloned();
if let (Some(key), Some(value)) = (key, value) {
if !thread.heap().is_dictionary(*this_ref).unwrap_or(false) {
let _ = thread.heap_mut().replace_with_dictionary(*this_ref);
}
let _ = thread.heap_mut().dictionary_set(*this_ref, key, value);
}
}
PreHookResult::Bypass(None)
}
fn hashtable_contains_key_pre(
ctx: &HookContext<'_>,
thread: &mut EmulationThread,
) -> PreHookResult {
if let Some(EmValue::ObjectRef(this_ref)) = ctx.this {
if let Some(key) = ctx
.args
.first()
.and_then(|v| DictionaryKey::from_emvalue(v, thread.heap()))
{
if let Ok(Some(_)) = thread.heap().dictionary_get(*this_ref, &key) {
return PreHookResult::Bypass(Some(EmValue::I32(1))); }
}
}
PreHookResult::Bypass(Some(EmValue::I32(0))) }
fn hashtable_get_item_pre(ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
if let Some(EmValue::ObjectRef(this_ref)) = ctx.this {
if let Some(key) = ctx
.args
.first()
.and_then(|v| DictionaryKey::from_emvalue(v, thread.heap()))
{
if let Ok(Some(value)) = thread.heap().dictionary_get(*this_ref, &key) {
return PreHookResult::Bypass(Some(value));
}
}
}
PreHookResult::Bypass(Some(EmValue::Null))
}
fn hashtable_set_item_pre(ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
if let Some(EmValue::ObjectRef(this_ref)) = ctx.this {
let key = ctx
.args
.first()
.and_then(|v| DictionaryKey::from_emvalue(v, thread.heap()));
let value = ctx.args.get(1).cloned();
if let (Some(key), Some(value)) = (key, value) {
let _ = thread.heap_mut().dictionary_set(*this_ref, key, value);
}
}
PreHookResult::Bypass(None)
}
fn hashtable_get_count_pre(ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
if let Some(EmValue::ObjectRef(this_ref)) = ctx.this {
if let Ok(count) = thread.heap().dictionary_count(*this_ref) {
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
return PreHookResult::Bypass(Some(EmValue::I32(count as i32)));
}
}
PreHookResult::Bypass(Some(EmValue::I32(0)))
}