From 59987cb4d371930fa592a67a191cf5bf1c30696b Mon Sep 17 00:00:00 2001 From: Kadir Yamamoto Date: Sat, 25 Mar 2023 11:51:10 +0900 Subject: [PATCH] update string writer logic, add a few methods, capture stderr as well --- src/clr/mod.rs | 83 ++++++++++++------------- src/primitives/helpers.rs | 77 ++++++++++++++++++++--- src/primitives/iappdomain.rs | 3 + src/primitives/ipropertyinfo.rs | 22 +++++++ src/primitives/itype.rs | 104 ++++++++++++++++++++++++++++++-- src/primitives/mod.rs | 3 +- 6 files changed, 232 insertions(+), 60 deletions(-) diff --git a/src/clr/mod.rs b/src/clr/mod.rs index b52cdb9..bb41f0a 100644 --- a/src/clr/mod.rs +++ b/src/clr/mod.rs @@ -1,7 +1,4 @@ -use crate::primitives::{ - ICLRMetaHost, ICLRRuntimeInfo, ICorRuntimeHost, IUnknown, Interface, _AppDomain, _MethodInfo, - _StringWriter, wrap_method_arguments, GUID, HRESULT, -}; +use crate::primitives::{ICLRMetaHost, ICLRRuntimeInfo, ICorRuntimeHost, IUnknown, Interface, _AppDomain, _MethodInfo, wrap_method_arguments, GUID, HRESULT, empty_variant_array}; use std::{ffi::c_void, ptr}; use windows::Win32::System::Com::VARIANT; #[cfg(feature = "default-loader")] @@ -25,8 +22,12 @@ pub struct ClrContext { pub struct OutputContext { pub set_out: *mut _MethodInfo, + pub set_err: *mut _MethodInfo, + pub to_string: *mut _MethodInfo, pub original_stdout: VARIANT, + pub original_stderr: VARIANT, pub redirected_stdout: VARIANT, + pub redirected_stderr: VARIANT, } impl Clr { @@ -124,27 +125,41 @@ impl Clr { pub fn redirect_output(&mut self) -> Result<(), String> { let context = self.get_context()?; - let assembly = unsafe { (*(&context).app_domain).load_library("mscorlib")? }; + // Get mscorlib assembly + let mscorlib = unsafe { (*(&context).app_domain).load_library("mscorlib")? }; - let console = unsafe { (*assembly).get_type("System.Console")? }; + // Sort out console related types/functions + let console = unsafe { (*mscorlib).get_type("System.Console")? }; let get_out = unsafe { (*console).get_method("get_Out")? }; - let set_out = unsafe { (*console).get_method("SetOut")? }; + let get_err = unsafe { (*console).get_method("get_Error")? }; + let set_err = unsafe { (*console).get_method("SetError")? }; - let old_console = unsafe { (*get_out).invoke_without_args(None)? }; + let old_out = unsafe { (*get_out).invoke_without_args(None)? }; + let old_err = unsafe { (*get_err).invoke_without_args(None)? }; + + // Sort out string writer related types/functions + let string_writer = unsafe { (*mscorlib).get_type("System.IO.StringWriter")? }; + let to_string = unsafe { (*string_writer).get_method("ToString")? }; let string_writer_instance = - unsafe { (*assembly).create_instance("System.IO.StringWriter")? }; + unsafe { (*mscorlib).create_instance("System.IO.StringWriter")? }; let method_args = wrap_method_arguments(vec![string_writer_instance.clone()])?; + // Replace stdout and stderr with the same StringWriter instance unsafe { (*set_out).invoke(method_args, None)? }; + unsafe { (*set_err).invoke(method_args, None)? }; self.output_context = Some(OutputContext { set_out, - original_stdout: old_console, - redirected_stdout: string_writer_instance, + set_err, + to_string, + original_stdout: old_out, + original_stderr: old_err, + redirected_stdout: string_writer_instance.clone(), + redirected_stderr: string_writer_instance.clone(), }); Ok(()) @@ -157,9 +172,15 @@ impl Clr { let context = self.output_context.as_ref().unwrap(); - let method_args = wrap_method_arguments(vec![context.original_stdout.clone()])?; + unsafe { (*(&context).set_out).invoke( + wrap_method_arguments(vec![context.original_stdout.clone()])?, + None + )? }; - unsafe { (*(&context).set_out).invoke(method_args, None)? }; + unsafe { (*(&context).set_err).invoke( + wrap_method_arguments(vec![context.original_stderr.clone()])?, + None + )? }; Ok(()) } @@ -170,41 +191,13 @@ impl Clr { } let context = self.output_context.as_ref().unwrap(); + let instance = context.redirected_stdout.clone(); - let dispatchable: &*mut IUnknown = unsafe { - std::mem::transmute( - context - .redirected_stdout - .Anonymous - .Anonymous - .Anonymous - .pdispVal - .as_ref() - .unwrap(), - ) - }; - let mut string_writer_ptr: *mut _StringWriter = ptr::null_mut(); - - if dispatchable.is_null() { - return Err("Could not retrieve StringWriter".into()); - } - - let hr = unsafe { - (**dispatchable).QueryInterface( - &_StringWriter::IID, - &mut string_writer_ptr as *mut *mut _ as *mut *mut c_void, - ) + let result = unsafe { + (*(&context).to_string).invoke(empty_variant_array(), Some(instance.clone()))? }; - if hr.is_err() { - return Err(format!("Error while retrieving StringWriter: 0x{:x}", hr.0)); - } - - if string_writer_ptr.is_null() { - return Err("Could not retrieve StringWriter".into()); - } - - Ok(unsafe { (*string_writer_ptr).to_string()? }) + Ok(unsafe { result.Anonymous.Anonymous.Anonymous.bstrVal.to_string() }) } pub fn get_context(&mut self) -> Result<&ClrContext, String> { diff --git a/src/primitives/helpers.rs b/src/primitives/helpers.rs index 5f81183..7ca1c29 100644 --- a/src/primitives/helpers.rs +++ b/src/primitives/helpers.rs @@ -1,14 +1,21 @@ -use std::{ffi::c_void, mem::ManuallyDrop, ptr}; +use std::{ + ffi::c_void, + mem::{size_of, ManuallyDrop}, + ptr, +}; use windows::{ core::BSTR, - Win32::System::{ - Com::{ - SAFEARRAY, SAFEARRAYBOUND, VARENUM, VARIANT, VARIANT_0, VARIANT_0_0, VARIANT_0_0_0, - VT_ARRAY, VT_BSTR, VT_EMPTY, VT_UI1, VT_UNKNOWN, VT_VARIANT, - }, - Ole::{ - SafeArrayAccessData, SafeArrayCreate, SafeArrayCreateVector, SafeArrayGetLBound, - SafeArrayGetUBound, SafeArrayPutElement, SafeArrayUnaccessData, + Win32::{ + Foundation::VARIANT_BOOL, + System::{ + Com::{ + SAFEARRAY, SAFEARRAYBOUND, VARENUM, VARIANT, VARIANT_0, VARIANT_0_0, VARIANT_0_0_0, + VT_ARRAY, VT_BOOL, VT_BSTR, VT_EMPTY, VT_I8, VT_UI1, VT_UNKNOWN, VT_VARIANT, + }, + Ole::{ + SafeArrayAccessData, SafeArrayCreate, SafeArrayCreateVector, SafeArrayGetElement, + SafeArrayGetLBound, SafeArrayGetUBound, SafeArrayPutElement, SafeArrayUnaccessData, + }, }, }, }; @@ -83,6 +90,36 @@ pub fn wrap_unknown_ptr_in_variant(unknown_ptr: *mut c_void) -> VARIANT { } } +pub fn wrap_bool_in_variant(value: bool) -> VARIANT { + VARIANT { + Anonymous: VARIANT_0 { + Anonymous: ManuallyDrop::new(VARIANT_0_0 { + vt: VT_BOOL, + wReserved1: 0, + wReserved2: 0, + wReserved3: 0, + Anonymous: VARIANT_0_0_0 { + boolVal: VARIANT_BOOL::from(value), + }, + }), + }, + } +} + +pub fn wrap_i64_in_variant(value: i64) -> VARIANT { + VARIANT { + Anonymous: VARIANT_0 { + Anonymous: ManuallyDrop::new(VARIANT_0_0 { + vt: VT_I8, + wReserved1: 0, + wReserved2: 0, + wReserved3: 0, + Anonymous: VARIANT_0_0_0 { llVal: value }, + }), + }, + } +} + pub fn wrap_string_in_variant(string: &str) -> VARIANT { let inner = BSTR::from(string); @@ -161,3 +198,25 @@ pub fn wrap_method_arguments(arguments: Vec) -> Result<*mut SAFEARRAY, Ok(variant_array_ptr) } + +pub fn unpack_byte_array(safe_array_ptr: *mut SAFEARRAY) -> Result, String> { + let ubound = unsafe { SafeArrayGetUBound(safe_array_ptr, 1) }.unwrap_or(0); + let mut results: Vec = vec![]; + + for i in 0..ubound { + let indices: [i32; 1] = [i as _]; + let mut variant: u8 = 0; + let pv = &mut variant as *mut _ as *mut c_void; + + match unsafe { SafeArrayGetElement(safe_array_ptr, indices.as_ptr(), pv) } { + Ok(_) => {}, + Err(e) => return Err(format!("Could not access safe array: {:?}", e.code())), + } + + if !pv.is_null() { + results.push(variant); + } + } + + Ok(results) +} diff --git a/src/primitives/iappdomain.rs b/src/primitives/iappdomain.rs index c525ae9..18cd793 100644 --- a/src/primitives/iappdomain.rs +++ b/src/primitives/iappdomain.rs @@ -13,6 +13,9 @@ pub struct _AppDomain { pub vtable: *const _AppDomainVtbl, } +unsafe impl Sync for _AppDomain {} +unsafe impl Send for _AppDomain {} + #[repr(C)] pub struct _AppDomainVtbl { pub parent: IUnknownVtbl, diff --git a/src/primitives/ipropertyinfo.rs b/src/primitives/ipropertyinfo.rs index c161791..d9e8b0e 100644 --- a/src/primitives/ipropertyinfo.rs +++ b/src/primitives/ipropertyinfo.rs @@ -152,6 +152,23 @@ impl _PropertyInfo { Ok(return_value) } + pub fn set_value(&self, value: VARIANT, instance: Option) -> Result<(), String> { + let object: VARIANT = match instance { + None => unsafe { std::mem::zeroed() }, + Some(i) => i, + }; + + let index = empty_array(); + + let hr = unsafe { (*self).SetValue(object, value, index) }; + + if hr.is_err() { + return Err(format!("Could not invoke method: {:?}", hr)); + } + + Ok(()) + } + #[inline] pub unsafe fn ToString(&self, pRetVal: *mut *mut u16) -> HRESULT { ((*self.vtable).ToString)(self as *const _ as *mut _, pRetVal) @@ -166,6 +183,11 @@ impl _PropertyInfo { ) -> HRESULT { ((*self.vtable).GetValue)(self as *const _ as *mut _, obj, index, pRetVal) } + + #[inline] + pub unsafe fn SetValue(&self, obj: VARIANT, val: VARIANT, index: *mut SAFEARRAY) -> HRESULT { + ((*self.vtable).SetValue)(self as *const _ as *mut _, obj, val, index) + } } impl Interface for _PropertyInfo { diff --git a/src/primitives/itype.rs b/src/primitives/itype.rs index aa4a0e7..25abc90 100644 --- a/src/primitives/itype.rs +++ b/src/primitives/itype.rs @@ -1,6 +1,6 @@ use crate::primitives::{ - IUnknown, IUnknownVtbl, Interface, _Assembly, _MethodInfo, _PropertyInfo, empty_variant_array, - GUID, HRESULT, + IUnknown, IUnknownVtbl, Interface, _Assembly, _ConstructorInfo, _MethodInfo, _PropertyInfo, + empty_variant_array, wrap_method_arguments, GUID, HRESULT, }; use std::{ ffi::{c_long, c_void}, @@ -55,7 +55,11 @@ pub struct _TypeVtbl { pub GetArrayRank: *const c_void, pub get_BaseType: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut _Type) -> HRESULT, - pub GetConstructors: *const c_void, + pub GetConstructors: unsafe extern "system" fn( + this: *mut c_void, + bindingAttr: BindingFlags, + pRetVal: *mut *mut SAFEARRAY, + ) -> HRESULT, pub GetInterface: *const c_void, pub GetInterfaces: *const c_void, pub FindInterfaces: *const c_void, @@ -115,8 +119,13 @@ pub struct _TypeVtbl { ) -> HRESULT, pub GetConstructor: *const c_void, pub GetConstructor_2: *const c_void, - pub GetConstructor_3: *const c_void, - pub GetConstructors_2: *const c_void, + pub GetConstructor_3: unsafe extern "system" fn( + this: *mut c_void, + types: *mut SAFEARRAY, + pRetVal: *mut *mut _ConstructorInfo, + ) -> HRESULT, + pub GetConstructors_2: + unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut SAFEARRAY) -> HRESULT, pub get_TypeInitializer: *const c_void, pub GetMethod_3: *const c_void, pub GetMethod_4: unsafe extern "system" fn( @@ -211,6 +220,77 @@ impl _Type { Ok(buffer.to_string()) } + pub fn get_constructor( + &self, + parameter_types: Vec, + ) -> Result<*mut _ConstructorInfo, String> { + let mut constructor_ptr: *mut _ConstructorInfo = ptr::null_mut(); + let mut type_array = wrap_method_arguments(parameter_types)?; + let hr = unsafe { (*self).GetConstructor_3(type_array, &mut constructor_ptr) }; + + if hr.is_err() { + return Err(format!("Error while retrieving constructor: 0x{:x}", hr.0)); + } + + if constructor_ptr.is_null() { + return Err("Could not retrieve constructor".into()); + } + + Ok(constructor_ptr) + } + + pub fn get_constructor_with_signature( + &self, + signature: &str, + ) -> Result<*mut _ConstructorInfo, String> { + let constructors = self.get_constructors()?; + + for constructor in constructors { + let constructor_name = unsafe { (*constructor).to_string()? }; + + if &constructor_name == signature { + return Ok(constructor); + } + } + + Err(format!( + "Could not find a constructor with the given signature: {}", + signature + )) + } + + pub fn get_constructors(&self) -> Result, String> { + let mut results: Vec<*mut _ConstructorInfo> = vec![]; + + let mut safe_array_ptr: *mut SAFEARRAY = + unsafe { SafeArrayCreateVector(VT_UNKNOWN, 0, 255) }; + + let hr = unsafe { (*self).GetConstructors_2(&mut safe_array_ptr) }; + + if hr.is_err() { + return Err(format!("Error while retrieving constructors: 0x{:x}", hr.0)); + } + + let ubound = unsafe { SafeArrayGetUBound(safe_array_ptr, 1) }.unwrap_or(0); + + for i in 0..ubound { + let indices: [i32; 1] = [i as _]; + let mut variant: *mut _ConstructorInfo = ptr::null_mut(); + let pv = &mut variant as *mut _ as *mut c_void; + + match unsafe { SafeArrayGetElement(safe_array_ptr, indices.as_ptr(), pv) } { + Ok(_) => {}, + Err(e) => return Err(format!("Could not access safe array: {:?}", e.code())), + } + + if !pv.is_null() { + results.push(variant) + } + } + + Ok(results) + } + pub fn get_method(&self, name: &str) -> Result<*mut _MethodInfo, String> { let dw = BSTR::from(name); @@ -556,6 +636,20 @@ impl _Type { pub unsafe fn GetProperties_2(&self, pRetVal: *mut *mut SAFEARRAY) -> HRESULT { ((*self.vtable).GetProperties_2)(self as *const _ as *mut _, pRetVal) } + + #[inline] + pub unsafe fn GetConstructor_3( + &self, + types: *mut SAFEARRAY, + pRetVal: *mut *mut _ConstructorInfo, + ) -> HRESULT { + ((*self.vtable).GetConstructor_3)(self as *const _ as *mut _, types, pRetVal) + } + + #[inline] + pub unsafe fn GetConstructors_2(&self, pRetVal: *mut *mut SAFEARRAY) -> HRESULT { + ((*self.vtable).GetConstructors_2)(self as *const _ as *mut _, pRetVal) + } } impl Deref for _Type { diff --git a/src/primitives/mod.rs b/src/primitives/mod.rs index 274f0dc..3aaa70f 100644 --- a/src/primitives/mod.rs +++ b/src/primitives/mod.rs @@ -4,11 +4,11 @@ mod iappdomain; mod iassembly; mod iclrmetahost; mod iclrruntimeinfo; +mod iconstructorinfo; mod icorruntimehost; mod ienumunknown; mod imethodinfo; mod ipropertyinfo; -mod istringwriter; mod itype; mod iunknown; mod types; @@ -18,6 +18,7 @@ pub use iappdomain::*; pub use iassembly::*; pub use iclrmetahost::*; pub use iclrruntimeinfo::*; +pub use iconstructorinfo::*; pub use icorruntimehost::*; pub use ienumunknown::*; pub use imethodinfo::*;