use crate::{
emulation::{
runtime::hook::{Hook, HookContext, HookManager, PreHookResult},
thread::EmulationThread,
EmValue,
},
utils::{compute_hmac_sha256, compute_hmac_sha512},
Result,
};
pub fn register(manager: &HookManager) -> Result<()> {
manager.register(
Hook::new("System.Security.Cryptography.HMACSHA256..ctor")
.match_name("System.Security.Cryptography", "HMACSHA256", ".ctor")
.pre(hmacsha256_ctor_pre),
)?;
manager.register(
Hook::new("System.Security.Cryptography.HMACSHA256.ComputeHash")
.match_name("System.Security.Cryptography", "HMACSHA256", "ComputeHash")
.pre(hmac_compute_hash_pre),
)?;
manager.register(
Hook::new("System.Security.Cryptography.HMACSHA512..ctor")
.match_name("System.Security.Cryptography", "HMACSHA512", ".ctor")
.pre(hmacsha512_ctor_pre),
)?;
manager.register(
Hook::new("System.Security.Cryptography.HMACSHA512.ComputeHash")
.match_name("System.Security.Cryptography", "HMACSHA512", "ComputeHash")
.pre(hmac_compute_hash_pre),
)?;
Ok(())
}
fn hmacsha256_ctor_pre(ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
if let Some(EmValue::ObjectRef(_this_ref)) = ctx.this {
}
let key = match ctx.args.first() {
Some(EmValue::ObjectRef(handle)) => {
try_hook!(thread.heap().get_byte_array(*handle)).unwrap_or_default()
}
_ => Vec::new(),
};
let type_token = thread.resolve_type_token("System.Security.Cryptography", "HMACSHA256");
match thread
.heap()
.alloc_hmac_algorithm("HMACSHA256", key, type_token)
{
Ok(handle) => PreHookResult::Bypass(Some(EmValue::ObjectRef(handle))),
Err(e) => PreHookResult::Error(format!("heap allocation failed: {e}")),
}
}
fn hmacsha512_ctor_pre(ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
let key = match ctx.args.first() {
Some(EmValue::ObjectRef(handle)) => {
try_hook!(thread.heap().get_byte_array(*handle)).unwrap_or_default()
}
_ => Vec::new(),
};
let type_token = thread.resolve_type_token("System.Security.Cryptography", "HMACSHA512");
match thread
.heap()
.alloc_hmac_algorithm("HMACSHA512", key, type_token)
{
Ok(handle) => PreHookResult::Bypass(Some(EmValue::ObjectRef(handle))),
Err(e) => PreHookResult::Error(format!("heap allocation failed: {e}")),
}
}
fn hmac_compute_hash_pre(ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
let (algo_type, hmac_key) = match ctx.this {
Some(EmValue::ObjectRef(handle)) => {
let algo = try_hook!(thread.heap().get_crypto_algorithm_type(*handle))
.unwrap_or_else(|| "HMACSHA256".into());
let key = try_hook!(thread.heap().get_hmac_key(*handle));
(algo, key)
}
_ => return PreHookResult::Bypass(Some(EmValue::Null)),
};
if ctx.args.is_empty() {
return PreHookResult::Bypass(Some(EmValue::Null));
}
match &ctx.args[0] {
EmValue::ObjectRef(handle) => {
if let Some(bytes) = try_hook!(thread.heap().get_byte_array(*handle)) {
let key = hmac_key.as_deref().unwrap_or(&[]);
let hash = match algo_type.as_ref() {
"HMACSHA512" => compute_hmac_sha512(key, &bytes),
_ => compute_hmac_sha256(key, &bytes),
};
match thread.heap().alloc_byte_array(&hash) {
Ok(result) => PreHookResult::Bypass(Some(EmValue::ObjectRef(result))),
Err(e) => PreHookResult::Error(format!("heap allocation failed: {e}")),
}
} else {
PreHookResult::Bypass(Some(EmValue::Null))
}
}
_ => PreHookResult::Bypass(Some(EmValue::Null)),
}
}
#[cfg(test)]
mod tests {
use crate::{
emulation::{
runtime::hook::{HookContext, PreHookResult},
EmValue,
},
metadata::{token::Token, typesystem::PointerSize},
test::emulation::create_test_thread,
utils::{compute_hmac_sha256, compute_hmac_sha512},
};
#[test]
fn test_hmacsha256_ctor_hook() {
let mut thread = create_test_thread();
let key_data = thread.heap().alloc_byte_array(b"secret-key").unwrap();
let args = [EmValue::ObjectRef(key_data)];
let ctx = HookContext::new(
Token::new(0x0A000001),
"System.Security.Cryptography",
"HMACSHA256",
".ctor",
PointerSize::Bit64,
)
.with_args(&args);
let result = super::hmacsha256_ctor_pre(&ctx, &mut thread);
if let PreHookResult::Bypass(Some(EmValue::ObjectRef(handle))) = result {
let algo_type = thread
.heap()
.get_crypto_algorithm_type(handle)
.unwrap()
.unwrap();
assert_eq!(algo_type.as_ref(), "HMACSHA256");
let stored_key = thread.heap().get_hmac_key(handle).unwrap().unwrap();
assert_eq!(stored_key, b"secret-key");
} else {
panic!("Expected ObjectRef from HMACSHA256 ctor");
}
}
#[test]
fn test_hmacsha512_ctor_hook() {
let mut thread = create_test_thread();
let key_data = thread.heap().alloc_byte_array(b"another-key").unwrap();
let args = [EmValue::ObjectRef(key_data)];
let ctx = HookContext::new(
Token::new(0x0A000001),
"System.Security.Cryptography",
"HMACSHA512",
".ctor",
PointerSize::Bit64,
)
.with_args(&args);
let result = super::hmacsha512_ctor_pre(&ctx, &mut thread);
if let PreHookResult::Bypass(Some(EmValue::ObjectRef(handle))) = result {
let algo_type = thread
.heap()
.get_crypto_algorithm_type(handle)
.unwrap()
.unwrap();
assert_eq!(algo_type.as_ref(), "HMACSHA512");
let stored_key = thread.heap().get_hmac_key(handle).unwrap().unwrap();
assert_eq!(stored_key, b"another-key");
} else {
panic!("Expected ObjectRef from HMACSHA512 ctor");
}
}
#[test]
fn test_hmacsha256_compute_hash_hook() {
let mut thread = create_test_thread();
let key_bytes = b"test-key";
let data_bytes = b"hello world";
let key_ref = thread.heap().alloc_byte_array(key_bytes).unwrap();
let ctor_args = [EmValue::ObjectRef(key_ref)];
let ctor_ctx = HookContext::new(
Token::new(0x0A000001),
"System.Security.Cryptography",
"HMACSHA256",
".ctor",
PointerSize::Bit64,
)
.with_args(&ctor_args);
let hmac_ref = match super::hmacsha256_ctor_pre(&ctor_ctx, &mut thread) {
PreHookResult::Bypass(Some(EmValue::ObjectRef(h))) => h,
_ => panic!("Expected ObjectRef"),
};
let input = thread.heap().alloc_byte_array(data_bytes).unwrap();
let compute_args = [EmValue::ObjectRef(input)];
let this = EmValue::ObjectRef(hmac_ref);
let compute_ctx = HookContext::new(
Token::new(0x0A000001),
"System.Security.Cryptography",
"HMACSHA256",
"ComputeHash",
PointerSize::Bit64,
)
.with_this(Some(&this))
.with_args(&compute_args);
let result = super::hmac_compute_hash_pre(&compute_ctx, &mut thread);
if let PreHookResult::Bypass(Some(EmValue::ObjectRef(hash_ref))) = result {
let hash = thread.heap().get_byte_array(hash_ref).unwrap().unwrap();
let expected = compute_hmac_sha256(key_bytes, data_bytes);
assert_eq!(hash, expected);
assert_eq!(hash.len(), 32);
} else {
panic!("Expected ObjectRef from ComputeHash");
}
}
#[test]
fn test_hmacsha512_compute_hash_hook() {
let mut thread = create_test_thread();
let key_bytes = b"test-key-512";
let data_bytes = b"hello world";
let key_ref = thread.heap().alloc_byte_array(key_bytes).unwrap();
let ctor_args = [EmValue::ObjectRef(key_ref)];
let ctor_ctx = HookContext::new(
Token::new(0x0A000001),
"System.Security.Cryptography",
"HMACSHA512",
".ctor",
PointerSize::Bit64,
)
.with_args(&ctor_args);
let hmac_ref = match super::hmacsha512_ctor_pre(&ctor_ctx, &mut thread) {
PreHookResult::Bypass(Some(EmValue::ObjectRef(h))) => h,
_ => panic!("Expected ObjectRef"),
};
let input = thread.heap().alloc_byte_array(data_bytes).unwrap();
let compute_args = [EmValue::ObjectRef(input)];
let this = EmValue::ObjectRef(hmac_ref);
let compute_ctx = HookContext::new(
Token::new(0x0A000001),
"System.Security.Cryptography",
"HMACSHA512",
"ComputeHash",
PointerSize::Bit64,
)
.with_this(Some(&this))
.with_args(&compute_args);
let result = super::hmac_compute_hash_pre(&compute_ctx, &mut thread);
if let PreHookResult::Bypass(Some(EmValue::ObjectRef(hash_ref))) = result {
let hash = thread.heap().get_byte_array(hash_ref).unwrap().unwrap();
let expected = compute_hmac_sha512(key_bytes, data_bytes);
assert_eq!(hash, expected);
assert_eq!(hash.len(), 64);
} else {
panic!("Expected ObjectRef from ComputeHash");
}
}
}