use std::{collections::HashSet, sync::Arc};
use crate::{
emulation::{
AssemblyLoadMethod, CaptureSource, EmValue, EmulationThread, Hook, HookContext,
HookPriority, PreHookResult,
},
metadata::{tables::TableId, token::Token, typesystem::CilFlavor},
};
#[must_use]
pub fn create_antitamper_bypass_hook() -> Hook {
Hook::new("netreactor-antitamper-bypass")
.with_priority(HookPriority::HIGHEST)
.match_name(
"System.Security.Cryptography",
"RSACryptoServiceProvider",
"VerifyHash",
)
.pre(|_ctx, _thread| PreHookResult::Bypass(Some(EmValue::I32(1))))
}
#[must_use]
pub fn create_trial_bypass_hook(tokens: HashSet<Token>) -> Hook {
let tokens = Arc::new(tokens);
Hook::new("netreactor-trial-bypass")
.with_priority(HookPriority::HIGHEST)
.match_runtime("trial-token-check", {
let tokens = Arc::clone(&tokens);
move |ctx: &HookContext<'_>, _thread: &EmulationThread| {
let is_methoddef = ctx.method_token.is_table(TableId::MethodDef);
is_methoddef && tokens.contains(&ctx.method_token)
}
})
.pre(|ctx, _thread| {
let return_value = match ctx.return_type {
None | Some(CilFlavor::Void) => None,
Some(
CilFlavor::Boolean
| CilFlavor::I1
| CilFlavor::U1
| CilFlavor::I2
| CilFlavor::U2
| CilFlavor::Char
| CilFlavor::I4
| CilFlavor::U4
| CilFlavor::TypedRef { .. }
| CilFlavor::Unknown,
) => Some(EmValue::I32(0)),
Some(CilFlavor::I8 | CilFlavor::U8) => Some(EmValue::I64(0)),
Some(CilFlavor::R4) => Some(EmValue::F32(0.0)),
Some(CilFlavor::R8) => Some(EmValue::F64(0.0)),
Some(
CilFlavor::I
| CilFlavor::U
| CilFlavor::Pointer
| CilFlavor::ByRef
| CilFlavor::FnPtr { .. }
| CilFlavor::Pinned,
) => Some(EmValue::NativeInt(0)),
Some(
CilFlavor::String
| CilFlavor::Object
| CilFlavor::Class
| CilFlavor::Interface
| CilFlavor::Array { .. }
| CilFlavor::GenericInstance
| CilFlavor::GenericParameter { .. },
) => Some(EmValue::Null),
Some(CilFlavor::ValueType) => Some(EmValue::I32(0)),
};
PreHookResult::Bypass(return_value)
})
}
#[must_use]
pub fn create_resources_load_shim_hook(
tokens: HashSet<Token>,
assembly_typeref_token: Token,
) -> Hook {
let tokens = Arc::new(tokens);
Hook::new("netreactor-resources-load-shim")
.with_priority(HookPriority::HIGHEST)
.match_runtime("nr-resources-load-shim-token-check", {
let tokens = Arc::clone(&tokens);
move |ctx: &HookContext<'_>, _thread: &EmulationThread| {
tokens.contains(&ctx.method_token)
}
})
.pre(move |ctx, thread: &mut EmulationThread| {
let bytes_opt = match ctx.args.first() {
Some(EmValue::ObjectRef(arr_ref)) => {
thread.heap().get_byte_array(*arr_ref).ok().flatten()
}
_ => None,
};
if let Some(bytes) = bytes_opt {
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(assembly_typeref_token) {
Ok(asm_ref) => PreHookResult::Bypass(Some(EmValue::ObjectRef(asm_ref))),
Err(e) => PreHookResult::Error(format!("heap allocation failed: {e}")),
}
})
}