use crate::{
emulation::{
capture::CaptureSource,
memory::EncodingType,
runtime::hook::{Hook, HookContext, HookManager, PreHookResult},
thread::EmulationThread,
EmValue,
},
metadata::token::Token,
Result,
};
pub fn register(manager: &HookManager) -> Result<()> {
manager.register(
Hook::new("System.Text.Encoding.GetBytes")
.match_name("System.Text", "Encoding", "GetBytes")
.pre(encoding_get_bytes_pre),
)?;
manager.register(
Hook::new("System.Text.Encoding.GetString")
.match_name("System.Text", "Encoding", "GetString")
.pre(encoding_get_string_pre),
)?;
manager.register(
Hook::new("System.Text.Encoding.GetByteCount")
.match_name("System.Text", "Encoding", "GetByteCount")
.pre(encoding_get_byte_count_pre),
)?;
manager.register(
Hook::new("System.Text.Encoding.GetCharCount")
.match_name("System.Text", "Encoding", "GetCharCount")
.pre(encoding_get_char_count_pre),
)?;
manager.register(
Hook::new("System.Text.Encoding.get_UTF8")
.match_name("System.Text", "Encoding", "get_UTF8")
.pre(encoding_get_utf8_pre),
)?;
manager.register(
Hook::new("System.Text.Encoding.get_ASCII")
.match_name("System.Text", "Encoding", "get_ASCII")
.pre(encoding_get_ascii_pre),
)?;
manager.register(
Hook::new("System.Text.Encoding.get_Unicode")
.match_name("System.Text", "Encoding", "get_Unicode")
.pre(encoding_get_unicode_pre),
)?;
manager.register(
Hook::new("System.Text.Encoding.get_BigEndianUnicode")
.match_name("System.Text", "Encoding", "get_BigEndianUnicode")
.pre(encoding_get_big_endian_unicode_pre),
)?;
manager.register(
Hook::new("System.Text.Encoding.get_UTF32")
.match_name("System.Text", "Encoding", "get_UTF32")
.pre(encoding_get_utf32_pre),
)?;
manager.register(
Hook::new("System.Text.Encoding.GetEncoding")
.match_name("System.Text", "Encoding", "GetEncoding")
.pre(encoding_get_encoding_pre),
)?;
manager.register(
Hook::new("System.Text.UTF8Encoding.GetBytes")
.match_name("System.Text", "UTF8Encoding", "GetBytes")
.pre(utf8_get_bytes_pre),
)?;
manager.register(
Hook::new("System.Text.UTF8Encoding.GetString")
.match_name("System.Text", "UTF8Encoding", "GetString")
.pre(utf8_get_string_pre),
)?;
manager.register(
Hook::new("System.Text.ASCIIEncoding.GetBytes")
.match_name("System.Text", "ASCIIEncoding", "GetBytes")
.pre(ascii_get_bytes_pre),
)?;
manager.register(
Hook::new("System.Text.ASCIIEncoding.GetString")
.match_name("System.Text", "ASCIIEncoding", "GetString")
.pre(ascii_get_string_pre),
)?;
manager.register(
Hook::new("System.Text.UnicodeEncoding.GetBytes")
.match_name("System.Text", "UnicodeEncoding", "GetBytes")
.pre(unicode_get_bytes_pre),
)?;
manager.register(
Hook::new("System.Text.UnicodeEncoding.GetString")
.match_name("System.Text", "UnicodeEncoding", "GetString")
.pre(unicode_get_string_pre),
)?;
Ok(())
}
fn encoding_get_bytes_pre(ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
if ctx.args.is_empty() {
return PreHookResult::Bypass(Some(EmValue::Null));
}
let encoding_type = ctx
.this
.and_then(|this_val| {
if let EmValue::ObjectRef(href) = this_val {
thread.heap().get_encoding_type(*href).ok().flatten()
} else {
None
}
})
.unwrap_or(EncodingType::Utf8);
if let EmValue::ObjectRef(handle) = &ctx.args[0] {
if let Ok(s) = thread.heap().get_string(*handle) {
let bytes = encode_string(&s, encoding_type);
match thread.heap_mut().alloc_byte_array(&bytes) {
Ok(arr_handle) => {
return PreHookResult::Bypass(Some(EmValue::ObjectRef(arr_handle)))
}
Err(e) => return PreHookResult::Error(format!("heap allocation failed: {e}")),
}
}
}
PreHookResult::Bypass(Some(EmValue::Null))
}
fn encoding_get_string_pre(ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
if ctx.args.is_empty() {
return PreHookResult::Bypass(Some(EmValue::Null));
}
let encoding_type = ctx
.this
.and_then(|this_val| {
if let EmValue::ObjectRef(href) = this_val {
thread.heap().get_encoding_type(*href).ok().flatten()
} else {
None
}
})
.unwrap_or(EncodingType::Utf8);
if let EmValue::ObjectRef(handle) = &ctx.args[0] {
if let Some(all_bytes) = try_hook!(thread.heap().get_byte_array(*handle)) {
let bytes = if ctx.args.len() >= 3 {
#[allow(clippy::cast_sign_loss)]
let offset = match &ctx.args[1] {
EmValue::I32(o) => *o as usize,
_ => 0,
};
#[allow(clippy::cast_sign_loss)]
let count = match &ctx.args[2] {
EmValue::I32(c) => *c as usize,
_ => all_bytes.len(),
};
if offset + count <= all_bytes.len() {
all_bytes[offset..offset + count].to_vec()
} else {
all_bytes
}
} else {
all_bytes
};
let s = decode_bytes(&bytes, encoding_type);
let source = CaptureSource::new(
thread.current_method().unwrap_or(Token::new(0)),
thread.id(),
thread.current_offset().unwrap_or(0),
0,
);
thread.capture().capture_string(s.clone(), source);
match thread.heap_mut().alloc_string(&s) {
Ok(str_handle) => {
return PreHookResult::Bypass(Some(EmValue::ObjectRef(str_handle)))
}
Err(e) => return PreHookResult::Error(format!("heap allocation failed: {e}")),
}
}
}
PreHookResult::Bypass(Some(EmValue::Null))
}
fn encoding_get_byte_count_pre(
ctx: &HookContext<'_>,
thread: &mut EmulationThread,
) -> PreHookResult {
if ctx.args.is_empty() {
return PreHookResult::Bypass(Some(EmValue::I32(0)));
}
let encoding_type = ctx
.this
.and_then(|this_val| {
if let EmValue::ObjectRef(href) = this_val {
thread.heap().get_encoding_type(*href).ok().flatten()
} else {
None
}
})
.unwrap_or(EncodingType::Utf8);
if let EmValue::ObjectRef(handle) = &ctx.args[0] {
if let Ok(s) = thread.heap().get_string(*handle) {
let byte_len = encode_string(&s, encoding_type).len();
let len = i32::try_from(byte_len).unwrap_or(i32::MAX);
return PreHookResult::Bypass(Some(EmValue::I32(len)));
}
}
PreHookResult::Bypass(Some(EmValue::I32(0)))
}
fn encoding_get_char_count_pre(
ctx: &HookContext<'_>,
thread: &mut EmulationThread,
) -> PreHookResult {
if ctx.args.is_empty() {
return PreHookResult::Bypass(Some(EmValue::I32(0)));
}
let encoding_type = ctx
.this
.and_then(|this_val| {
if let EmValue::ObjectRef(href) = this_val {
thread.heap().get_encoding_type(*href).ok().flatten()
} else {
None
}
})
.unwrap_or(EncodingType::Utf8);
if let EmValue::ObjectRef(handle) = &ctx.args[0] {
if let Some(bytes) = try_hook!(thread.heap().get_byte_array(*handle)) {
let s = decode_bytes(&bytes, encoding_type);
let count = i32::try_from(s.chars().count()).unwrap_or(i32::MAX);
return PreHookResult::Bypass(Some(EmValue::I32(count)));
}
}
PreHookResult::Bypass(Some(EmValue::I32(0)))
}
fn encoding_get_utf8_pre(_ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
let type_token = thread.resolve_type_token("System.Text", "Encoding");
match thread
.heap_mut()
.alloc_encoding(EncodingType::Utf8, type_token)
{
Ok(handle) => PreHookResult::Bypass(Some(EmValue::ObjectRef(handle))),
Err(e) => PreHookResult::Error(format!("heap allocation failed: {e}")),
}
}
fn encoding_get_ascii_pre(_ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
let type_token = thread.resolve_type_token("System.Text", "Encoding");
match thread
.heap_mut()
.alloc_encoding(EncodingType::Ascii, type_token)
{
Ok(handle) => PreHookResult::Bypass(Some(EmValue::ObjectRef(handle))),
Err(e) => PreHookResult::Error(format!("heap allocation failed: {e}")),
}
}
fn encoding_get_unicode_pre(_ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
let type_token = thread.resolve_type_token("System.Text", "Encoding");
match thread
.heap_mut()
.alloc_encoding(EncodingType::Utf16Le, type_token)
{
Ok(handle) => PreHookResult::Bypass(Some(EmValue::ObjectRef(handle))),
Err(e) => PreHookResult::Error(format!("heap allocation failed: {e}")),
}
}
fn encoding_get_big_endian_unicode_pre(
_ctx: &HookContext<'_>,
thread: &mut EmulationThread,
) -> PreHookResult {
let type_token = thread.resolve_type_token("System.Text", "Encoding");
match thread
.heap_mut()
.alloc_encoding(EncodingType::Utf16Be, type_token)
{
Ok(handle) => PreHookResult::Bypass(Some(EmValue::ObjectRef(handle))),
Err(e) => PreHookResult::Error(format!("heap allocation failed: {e}")),
}
}
fn encoding_get_utf32_pre(_ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
let type_token = thread.resolve_type_token("System.Text", "Encoding");
match thread
.heap_mut()
.alloc_encoding(EncodingType::Utf32, type_token)
{
Ok(handle) => PreHookResult::Bypass(Some(EmValue::ObjectRef(handle))),
Err(e) => PreHookResult::Error(format!("heap allocation failed: {e}")),
}
}
fn encoding_get_encoding_pre(ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
let encoding_type = if let Some(EmValue::I32(code_page)) = ctx.args.first() {
match *code_page {
20127 => EncodingType::Ascii, 1200 => EncodingType::Utf16Le, 1201 => EncodingType::Utf16Be, 12000 => EncodingType::Utf32, _ => EncodingType::Utf8,
}
} else {
EncodingType::Utf8
};
let type_token = thread.resolve_type_token("System.Text", "Encoding");
match thread.heap_mut().alloc_encoding(encoding_type, type_token) {
Ok(handle) => PreHookResult::Bypass(Some(EmValue::ObjectRef(handle))),
Err(e) => PreHookResult::Error(format!("heap allocation failed: {e}")),
}
}
fn utf8_get_bytes_pre(ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
if ctx.args.is_empty() {
return PreHookResult::Bypass(Some(EmValue::Null));
}
if let EmValue::ObjectRef(handle) = &ctx.args[0] {
if let Ok(s) = thread.heap().get_string(*handle) {
let bytes: Vec<u8> = s.as_bytes().to_vec();
match thread.heap_mut().alloc_byte_array(&bytes) {
Ok(arr_handle) => {
return PreHookResult::Bypass(Some(EmValue::ObjectRef(arr_handle)))
}
Err(e) => return PreHookResult::Error(format!("heap allocation failed: {e}")),
}
}
}
PreHookResult::Bypass(Some(EmValue::Null))
}
fn utf8_get_string_pre(ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
if ctx.args.is_empty() {
return PreHookResult::Bypass(Some(EmValue::Null));
}
if let EmValue::ObjectRef(handle) = &ctx.args[0] {
if let Some(all_bytes) = try_hook!(thread.heap().get_byte_array(*handle)) {
let bytes = if ctx.args.len() >= 3 {
#[allow(clippy::cast_sign_loss)]
let offset = match &ctx.args[1] {
EmValue::I32(o) => *o as usize,
_ => 0,
};
#[allow(clippy::cast_sign_loss)]
let count = match &ctx.args[2] {
EmValue::I32(c) => *c as usize,
_ => all_bytes.len(),
};
if offset + count <= all_bytes.len() {
all_bytes[offset..offset + count].to_vec()
} else {
all_bytes
}
} else {
all_bytes
};
let s = String::from_utf8_lossy(&bytes).into_owned();
let source = CaptureSource::new(
thread.current_method().unwrap_or(Token::new(0)),
thread.id(),
thread.current_offset().unwrap_or(0),
0,
);
thread.capture().capture_string(s.clone(), source);
match thread.heap_mut().alloc_string(&s) {
Ok(str_handle) => {
return PreHookResult::Bypass(Some(EmValue::ObjectRef(str_handle)))
}
Err(e) => return PreHookResult::Error(format!("heap allocation failed: {e}")),
}
}
}
PreHookResult::Bypass(Some(EmValue::Null))
}
fn ascii_get_bytes_pre(ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
if ctx.args.is_empty() {
return PreHookResult::Bypass(Some(EmValue::Null));
}
if let EmValue::ObjectRef(handle) = &ctx.args[0] {
if let Ok(s) = thread.heap().get_string(*handle) {
let bytes: Vec<u8> = s
.chars()
.map(|c| if c.is_ascii() { c as u8 } else { b'?' })
.collect();
match thread.heap_mut().alloc_byte_array(&bytes) {
Ok(arr_handle) => {
return PreHookResult::Bypass(Some(EmValue::ObjectRef(arr_handle)))
}
Err(e) => return PreHookResult::Error(format!("heap allocation failed: {e}")),
}
}
}
PreHookResult::Bypass(Some(EmValue::Null))
}
fn ascii_get_string_pre(ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
if ctx.args.is_empty() {
return PreHookResult::Bypass(Some(EmValue::Null));
}
if let EmValue::ObjectRef(handle) = &ctx.args[0] {
if let Some(all_bytes) = try_hook!(thread.heap().get_byte_array(*handle)) {
let bytes = if ctx.args.len() >= 3 {
#[allow(clippy::cast_sign_loss)]
let offset = match &ctx.args[1] {
EmValue::I32(o) => *o as usize,
_ => 0,
};
#[allow(clippy::cast_sign_loss)]
let count = match &ctx.args[2] {
EmValue::I32(c) => *c as usize,
_ => all_bytes.len(),
};
if offset + count <= all_bytes.len() {
all_bytes[offset..offset + count].to_vec()
} else {
all_bytes
}
} else {
all_bytes
};
let s: String = bytes
.iter()
.map(|&b| if b < 128 { char::from(b) } else { '?' })
.collect();
let source = CaptureSource::new(
thread.current_method().unwrap_or(Token::new(0)),
thread.id(),
thread.current_offset().unwrap_or(0),
0,
);
thread.capture().capture_string(s.clone(), source);
match thread.heap_mut().alloc_string(&s) {
Ok(str_handle) => {
return PreHookResult::Bypass(Some(EmValue::ObjectRef(str_handle)))
}
Err(e) => return PreHookResult::Error(format!("heap allocation failed: {e}")),
}
}
}
PreHookResult::Bypass(Some(EmValue::Null))
}
fn unicode_get_bytes_pre(ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
if ctx.args.is_empty() {
return PreHookResult::Bypass(Some(EmValue::Null));
}
if let EmValue::ObjectRef(handle) = &ctx.args[0] {
if let Ok(s) = thread.heap().get_string(*handle) {
let bytes: Vec<u8> = s.encode_utf16().flat_map(u16::to_le_bytes).collect();
match thread.heap_mut().alloc_byte_array(&bytes) {
Ok(arr_handle) => {
return PreHookResult::Bypass(Some(EmValue::ObjectRef(arr_handle)))
}
Err(e) => return PreHookResult::Error(format!("heap allocation failed: {e}")),
}
}
}
PreHookResult::Bypass(Some(EmValue::Null))
}
fn unicode_get_string_pre(ctx: &HookContext<'_>, thread: &mut EmulationThread) -> PreHookResult {
if ctx.args.is_empty() {
return PreHookResult::Bypass(Some(EmValue::Null));
}
if let EmValue::ObjectRef(handle) = &ctx.args[0] {
if let Some(all_bytes) = try_hook!(thread.heap().get_byte_array(*handle)) {
let bytes = if ctx.args.len() >= 3 {
#[allow(clippy::cast_sign_loss)]
let offset = match &ctx.args[1] {
EmValue::I32(o) => *o as usize,
_ => 0,
};
#[allow(clippy::cast_sign_loss)]
let count = match &ctx.args[2] {
EmValue::I32(c) => *c as usize,
_ => all_bytes.len(),
};
if offset + count <= all_bytes.len() {
all_bytes[offset..offset + count].to_vec()
} else {
all_bytes
}
} else {
all_bytes
};
if bytes.len() % 2 != 0 {
return PreHookResult::Bypass(Some(EmValue::Null));
}
let u16s: Vec<u16> = bytes
.chunks_exact(2)
.map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
.collect();
let s = String::from_utf16_lossy(&u16s);
let source = CaptureSource::new(
thread.current_method().unwrap_or(Token::new(0)),
thread.id(),
thread.current_offset().unwrap_or(0),
0,
);
thread.capture().capture_string(s.clone(), source);
match thread.heap_mut().alloc_string(&s) {
Ok(str_handle) => {
return PreHookResult::Bypass(Some(EmValue::ObjectRef(str_handle)))
}
Err(e) => return PreHookResult::Error(format!("heap allocation failed: {e}")),
}
}
}
PreHookResult::Bypass(Some(EmValue::Null))
}
fn encode_string(s: &str, encoding_type: EncodingType) -> Vec<u8> {
match encoding_type {
EncodingType::Utf8 => s.as_bytes().to_vec(),
EncodingType::Ascii => s.bytes().map(|b| if b > 127 { b'?' } else { b }).collect(),
EncodingType::Utf16Le => s.encode_utf16().flat_map(u16::to_le_bytes).collect(),
EncodingType::Utf16Be => s.encode_utf16().flat_map(u16::to_be_bytes).collect(),
EncodingType::Utf32 => s.chars().flat_map(|c| (c as u32).to_le_bytes()).collect(),
}
}
fn decode_bytes(bytes: &[u8], encoding_type: EncodingType) -> String {
match encoding_type {
EncodingType::Utf8 => String::from_utf8_lossy(bytes).into_owned(),
EncodingType::Ascii => bytes.iter().map(|&b| b as char).collect(),
EncodingType::Utf16Le => {
if !bytes.len().is_multiple_of(2) {
return String::new();
}
let u16s: Vec<u16> = bytes
.chunks_exact(2)
.map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
.collect();
String::from_utf16_lossy(&u16s)
}
EncodingType::Utf16Be => {
if !bytes.len().is_multiple_of(2) {
return String::new();
}
let u16s: Vec<u16> = bytes
.chunks_exact(2)
.map(|chunk| u16::from_be_bytes([chunk[0], chunk[1]]))
.collect();
String::from_utf16_lossy(&u16s)
}
EncodingType::Utf32 => {
if !bytes.len().is_multiple_of(4) {
return String::new();
}
bytes
.chunks_exact(4)
.filter_map(|chunk| {
let code = u32::from_le_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
char::from_u32(code)
})
.collect()
}
}
}
#[cfg(test)]
mod tests {
use crate::{
emulation::{
runtime::{
bcl::text::encoding::{
ascii_get_bytes_pre, encoding_get_big_endian_unicode_pre,
encoding_get_byte_count_pre, encoding_get_char_count_pre,
encoding_get_encoding_pre, encoding_get_string_pre, encoding_get_unicode_pre,
register, unicode_get_bytes_pre, unicode_get_string_pre, utf8_get_bytes_pre,
utf8_get_string_pre,
},
hook::{HookContext, HookManager, PreHookResult},
},
EmValue,
},
metadata::{token::Token, typesystem::PointerSize},
test::emulation::create_test_thread,
};
#[test]
fn test_register_hooks() {
let manager = HookManager::new();
register(&manager).unwrap();
assert_eq!(manager.len(), 16);
}
#[test]
fn test_utf8_get_bytes_hook() {
let mut thread = create_test_thread();
let s = thread.heap_mut().alloc_string("Hello").unwrap();
let args = [EmValue::ObjectRef(s)];
let ctx = HookContext::new(
Token::new(0x0A000001),
"System.Text",
"UTF8Encoding",
"GetBytes",
PointerSize::Bit64,
)
.with_args(&args);
let result = utf8_get_bytes_pre(&ctx, &mut thread);
if let PreHookResult::Bypass(Some(EmValue::ObjectRef(handle))) = result {
assert_eq!(
thread.heap().get_byte_array(handle).unwrap(),
Some(b"Hello".to_vec())
);
} else {
panic!("Expected Bypass with ObjectRef");
}
}
#[test]
fn test_utf8_get_string_hook() {
let mut thread = create_test_thread();
let data = thread.heap_mut().alloc_byte_array(b"World").unwrap();
let args = [EmValue::ObjectRef(data)];
let ctx = HookContext::new(
Token::new(0x0A000001),
"System.Text",
"UTF8Encoding",
"GetString",
PointerSize::Bit64,
)
.with_args(&args);
let result = utf8_get_string_pre(&ctx, &mut thread);
if let PreHookResult::Bypass(Some(EmValue::ObjectRef(handle))) = result {
assert_eq!(&*thread.heap().get_string(handle).unwrap(), "World");
} else {
panic!("Expected Bypass with ObjectRef");
}
}
#[test]
fn test_ascii_get_bytes_hook() {
let mut thread = create_test_thread();
let s = thread.heap_mut().alloc_string("Hello").unwrap();
let args = [EmValue::ObjectRef(s)];
let ctx = HookContext::new(
Token::new(0x0A000001),
"System.Text",
"ASCIIEncoding",
"GetBytes",
PointerSize::Bit64,
)
.with_args(&args);
let result = ascii_get_bytes_pre(&ctx, &mut thread);
if let PreHookResult::Bypass(Some(EmValue::ObjectRef(handle))) = result {
assert_eq!(
thread.heap().get_byte_array(handle).unwrap(),
Some(b"Hello".to_vec())
);
} else {
panic!("Expected Bypass with ObjectRef");
}
}
#[test]
fn test_unicode_get_bytes_hook() {
let mut thread = create_test_thread();
let s = thread.heap_mut().alloc_string("AB").unwrap();
let args = [EmValue::ObjectRef(s)];
let ctx = HookContext::new(
Token::new(0x0A000001),
"System.Text",
"UnicodeEncoding",
"GetBytes",
PointerSize::Bit64,
)
.with_args(&args);
let result = unicode_get_bytes_pre(&ctx, &mut thread);
if let PreHookResult::Bypass(Some(EmValue::ObjectRef(handle))) = result {
assert_eq!(
thread.heap().get_byte_array(handle).unwrap(),
Some(vec![0x41, 0x00, 0x42, 0x00])
);
} else {
panic!("Expected Bypass with ObjectRef");
}
}
#[test]
fn test_unicode_get_string_hook() {
let mut thread = create_test_thread();
let data = thread
.heap_mut()
.alloc_byte_array(&[0x41, 0x00, 0x42, 0x00])
.unwrap();
let args = [EmValue::ObjectRef(data)];
let ctx = HookContext::new(
Token::new(0x0A000001),
"System.Text",
"UnicodeEncoding",
"GetString",
PointerSize::Bit64,
)
.with_args(&args);
let result = unicode_get_string_pre(&ctx, &mut thread);
if let PreHookResult::Bypass(Some(EmValue::ObjectRef(handle))) = result {
assert_eq!(&*thread.heap().get_string(handle).unwrap(), "AB");
} else {
panic!("Expected Bypass with ObjectRef");
}
}
#[test]
fn test_encoding_get_byte_count() {
let mut thread = create_test_thread();
let s = thread.heap_mut().alloc_string("Hello").unwrap();
let args = [EmValue::ObjectRef(s)];
let ctx = HookContext::new(
Token::new(0x0A000001),
"System.Text",
"Encoding",
"GetByteCount",
PointerSize::Bit64,
)
.with_args(&args);
let result = encoding_get_byte_count_pre(&ctx, &mut thread);
assert!(matches!(
result,
PreHookResult::Bypass(Some(EmValue::I32(5)))
));
}
#[test]
fn test_encoding_get_char_count() {
let mut thread = create_test_thread();
let data = thread.heap_mut().alloc_byte_array(b"Hello").unwrap();
let args = [EmValue::ObjectRef(data)];
let ctx = HookContext::new(
Token::new(0x0A000001),
"System.Text",
"Encoding",
"GetCharCount",
PointerSize::Bit64,
)
.with_args(&args);
let result = encoding_get_char_count_pre(&ctx, &mut thread);
assert!(matches!(
result,
PreHookResult::Bypass(Some(EmValue::I32(5)))
));
}
#[test]
fn test_encoding_get_string_with_offset() {
let mut thread = create_test_thread();
let data = thread.heap_mut().alloc_byte_array(b"Hello World").unwrap();
let args = [EmValue::ObjectRef(data), EmValue::I32(6), EmValue::I32(5)];
let ctx = HookContext::new(
Token::new(0x0A000001),
"System.Text",
"Encoding",
"GetString",
PointerSize::Bit64,
)
.with_args(&args);
let result = encoding_get_string_pre(&ctx, &mut thread);
if let PreHookResult::Bypass(Some(EmValue::ObjectRef(handle))) = result {
assert_eq!(&*thread.heap().get_string(handle).unwrap(), "World");
} else {
panic!("Expected Bypass with ObjectRef");
}
}
#[test]
fn test_encoding_get_unicode_property() {
let mut thread = create_test_thread();
let ctx = HookContext::new(
Token::new(0x0A000001),
"System.Text",
"Encoding",
"get_Unicode",
PointerSize::Bit64,
);
let result = encoding_get_unicode_pre(&ctx, &mut thread);
assert!(matches!(
result,
PreHookResult::Bypass(Some(EmValue::ObjectRef(_)))
));
}
#[test]
fn test_encoding_get_big_endian_unicode_property() {
let mut thread = create_test_thread();
let ctx = HookContext::new(
Token::new(0x0A000001),
"System.Text",
"Encoding",
"get_BigEndianUnicode",
PointerSize::Bit64,
);
let result = encoding_get_big_endian_unicode_pre(&ctx, &mut thread);
assert!(matches!(
result,
PreHookResult::Bypass(Some(EmValue::ObjectRef(_)))
));
}
#[test]
fn test_get_encoding_ascii_codepage() {
let mut thread = create_test_thread();
let args = [EmValue::I32(20127)];
let ctx = HookContext::new(
Token::new(0x0A000001),
"System.Text",
"Encoding",
"GetEncoding",
PointerSize::Bit64,
)
.with_args(&args);
let result = encoding_get_encoding_pre(&ctx, &mut thread);
assert!(matches!(
result,
PreHookResult::Bypass(Some(EmValue::ObjectRef(_)))
));
}
#[test]
fn test_get_encoding_hook() {
let mut thread = create_test_thread();
let args = [EmValue::I32(65001)];
let ctx = HookContext::new(
Token::new(0x0A000001),
"System.Text",
"Encoding",
"GetEncoding",
PointerSize::Bit64,
)
.with_args(&args);
let result = encoding_get_encoding_pre(&ctx, &mut thread);
assert!(matches!(
result,
PreHookResult::Bypass(Some(EmValue::ObjectRef(_)))
));
let args = [EmValue::I32(20127)];
let ctx = HookContext::new(
Token::new(0x0A000001),
"System.Text",
"Encoding",
"GetEncoding",
PointerSize::Bit64,
)
.with_args(&args);
let result = encoding_get_encoding_pre(&ctx, &mut thread);
assert!(matches!(
result,
PreHookResult::Bypass(Some(EmValue::ObjectRef(_)))
));
}
}