commit 0b12d397ff55216c607bcb6e82c9d72e740536d3 Author: Kadir Yamamoto Date: Thu Mar 16 19:25:33 2023 +0900 Move everything to its own repo diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a34bea2 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "clroxide" +authors = ["KY "] +description = "A library that allows you to host the CLR and execute dotnet binaries." +edition = "2021" +homepage = "https://github.com/yamakadi/clroxide" +license = "MIT" +repository = "https://github.com/yamakadi/clroxide" +version = "1.0.0" + +[lib] +crate-type = ["lib", "staticlib"] + +[features] +default = ["default-loader"] +default-loader = ["windows/Win32_System_LibraryLoader"] +debug = [] + +[dependencies] +windows = { version = "0.44.0", features = ["Win32_System_Com", "Win32_Foundation", "Win32_System_Ole"] } + +[package.metadata.docs.rs] +all-features = false +default-target = "x86_64-pc-windows-gnu" +targets = ["x86_64-pc-windows-gnu", "x86_64-pc-windows-msvc"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..da3ec69 --- /dev/null +++ b/README.md @@ -0,0 +1,67 @@ +# ClrOxide +`ClrOxide` is a rust library that allows you to host the CLR and dynamically execute dotnet binaries. + +I wanted to call it `Kepler` for no particular reason, but there's already a package named `kepler` in cargo. :( + +I have been working on hosting CLR with rust on and off for 2 years now, and finally something clicked two weeks ago! + +This library wouldn't be possible without the following projects: + +- [NimPlant](https://github.com/chvancooten/NimPlant) and its [execute assembly](https://github.com/chvancooten/NimPlant/tree/main/client/commands/risky/executeAssembly.nim) implementation + - The elegance with which `winim/clr` allows overwriting the output buffer for `Console.Write` and gets the output! Striving for the same elegance is the only reason this library took two years. +How can I convince Cas to dabble with rust if he can't replicate this!? My work for a rust implant for `NimPlant` is also how I got into this rabbit hole in the first place. +- [go-clr](https://github.com/ropnop/go-clr) by [ropnop](https://github.com/ropnop) + - A very special thank you to ropnop here! This whole library is the result of 3 days of work thanks to something in `go-clr` that just made everything click for me! +- [dinvoke_rs](https://github.com/Kudaes/DInvoke_rs) by [Kudaes](https://github.com/Kudaes) + - Similar to `go-clr`, Kurosh's `dinvoke_rs` project also made some rust/win32 intricacies clearer and allowed the project to move forward. +- Various CLR-related rust libraries + - https://github.com/ZerothLaw/mscorlib-rs-sys + - https://github.com/ZerothLaw/mscoree-rs + - and likely a few more... + + +## Usage + +`ClrOxide` will load the CLR in the current process, resolve `mscorlib` and redirect the output for `System.Console`, finally loading and running your executable and returning its output as a string. + +Streaming the output is not currently supported, although I'm sure the CLR wrangling magic used for redirecting the output could be a good guide for anyone willing to implement it. + +```rust +use clroxide::clr::Clr; +use std::{env, fs, process::exit}; + +fn main() -> Result<(), String> { + let (path, args) = prepare_args(); + + let contents = fs::read(path).expect("Unable to read file"); + let mut context = Clr::new(contents, args)?; + + let results = context.run()?; + + println!("[*] Results:\n\n{}", results); + + Ok(()) +} + +fn prepare_args() -> (String, Vec) { + let mut args: Vec = env::args().collect(); + + if args.len() < 2 { + println!("Please provide a path to a dotnet executable"); + + exit(1) + } + + let mut command_args: Vec = vec![]; + + if args.len() > 2 { + command_args = args.split_off(2) + } + + let path = args[1].clone(); + + println!("[+] Running `{}` with given args: {:?}", path, command_args); + + return (path, command_args); +} +``` diff --git a/examples/execute_assembly.rs b/examples/execute_assembly.rs new file mode 100644 index 0000000..f9305fb --- /dev/null +++ b/examples/execute_assembly.rs @@ -0,0 +1,37 @@ +use clroxide::clr::Clr; +use std::{env, fs, process::exit}; + +fn main() -> Result<(), String> { + let (path, args) = prepare_args(); + + let contents = fs::read(path).expect("Unable to read file"); + let mut context = Clr::new(contents, args)?; + + let results = context.run()?; + + println!("[*] Results:\n\n{}", results); + + Ok(()) +} + +fn prepare_args() -> (String, Vec) { + let mut args: Vec = env::args().collect(); + + if args.len() < 2 { + println!("Please provide a path to a dotnet executable"); + + exit(1) + } + + let mut command_args: Vec = vec![]; + + if args.len() > 2 { + command_args = args.split_off(2) + } + + let path = args[1].clone(); + + println!("[+] Running `{}` with given args: {:?}", path, command_args); + + return (path, command_args); +} diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..16da4d5 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,6 @@ +imports_granularity = "Crate" +indent_style = "Block" +match_block_trailing_comma = true +reorder_impl_items = true +use_field_init_shorthand = true +use_try_shorthand = true \ No newline at end of file diff --git a/src/clr/mod.rs b/src/clr/mod.rs new file mode 100644 index 0000000..1398381 --- /dev/null +++ b/src/clr/mod.rs @@ -0,0 +1,225 @@ +use crate::primitives::{ + ICLRMetaHost, ICLRRuntimeInfo, ICorRuntimeHost, IUnknown, Interface, _AppDomain, _MethodInfo, + _StringWriter, wrap_method_arguments, GUID, HRESULT, +}; +use std::{ffi::c_void, ptr}; +use windows::Win32::System::Com::VARIANT; +#[cfg(feature = "default-loader")] +use windows::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA}; + +pub struct Clr { + contents: Vec, + arguments: Vec, + create_interface: isize, + context: Option, + output_context: Option, +} + +pub struct ClrContext { + pub has_started: bool, + pub host: *mut ICLRMetaHost, + pub runtime_info: *mut ICLRRuntimeInfo, + pub runtime_host: *mut ICorRuntimeHost, + pub app_domain: *mut _AppDomain, +} + +pub struct OutputContext { + pub set_out: *mut _MethodInfo, + pub original_stdout: VARIANT, + pub redirected_stdout: VARIANT, +} + +impl Clr { + #[cfg(feature = "default-loader")] + pub fn new(contents: Vec, arguments: Vec) -> Result { + let create_interface = load_function("mscoree.dll", "CreateInterface")?; + + Ok(Clr { + contents, + arguments, + create_interface, + context: None, + output_context: None, + }) + } + + #[cfg(not(feature = "default-loader"))] + pub fn new( + contents: Vec, + arguments: Vec, + load_function: fn(&str, &str) -> Result, + ) -> Result { + let create_interface = load_function("mscoree.dll", "CreateInterface")?; + + Ok(Clr { + contents, + arguments, + create_interface, + context: None, + output_context: None, + }) + } + + pub fn run(&mut self) -> Result { + self.redirect_output()?; + + let context = self.get_context()?; + let assembly = unsafe { (*(&context).app_domain).load_assembly(&self.contents)? }; + + unsafe { (*assembly).run_entrypoint(&self.arguments)? }; + + self.restore_output()?; + + self.get_redirected_output() + } + + pub fn redirect_output(&mut self) -> Result<(), String> { + let context = self.get_context()?; + + let assembly = unsafe { (*(&context).app_domain).load_library("mscorlib")? }; + + let console = unsafe { (*assembly).get_type("System.Console")? }; + + let get_out = unsafe { (*console).get_method("get_Out")? }; + + let set_out = unsafe { (*console).get_method("SetOut")? }; + + let old_console = unsafe { (*get_out).invoke_without_args()? }; + + let string_writer_instance = + unsafe { (*assembly).create_instance("System.IO.StringWriter")? }; + + let method_args = wrap_method_arguments(vec![string_writer_instance.clone()])?; + + unsafe { (*set_out).invoke(method_args)? }; + + self.output_context = Some(OutputContext { + set_out, + original_stdout: old_console, + redirected_stdout: string_writer_instance, + }); + + Ok(()) + } + + pub fn restore_output(&mut self) -> Result<(), String> { + if self.output_context.is_none() { + return Err("Output context has not been initialized".into()); + } + + let context = self.output_context.as_ref().unwrap(); + + let method_args = wrap_method_arguments(vec![context.original_stdout.clone()])?; + + unsafe { (*(&context).set_out).invoke(method_args)? }; + + Ok(()) + } + + pub fn get_redirected_output(&mut self) -> Result { + if self.output_context.is_none() { + return Err("Output context has not been initialized".into()); + } + + let context = self.output_context.as_ref().unwrap(); + + let mut 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, + ) + }; + + 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()? }) + } + + pub fn get_context(&mut self) -> Result<&ClrContext, String> { + if self.context.is_some() { + return Ok(self.context.as_ref().unwrap()); + } + + let host = self.get_clr_host()?; + let runtime_info = unsafe { (*host).get_first_available_runtime()? }; + let runtime_host = unsafe { (*runtime_info).get_runtime_host()? }; + + unsafe { (*runtime_host).start()? }; + + let app_domain = unsafe { (*runtime_host).get_default_domain()? }; + + self.context = Some(ClrContext { + has_started: true, + host, + runtime_info, + runtime_host, + app_domain, + }); + + Ok(self.context.as_ref().unwrap()) + } + + fn get_clr_host(&self) -> Result<*mut ICLRMetaHost, String> { + pub type CreateInterface = fn( + class_id: *const GUID, + interface_id: *const GUID, + interface: *mut *mut c_void, + ) -> HRESULT; + + let create_interface: CreateInterface = + unsafe { std::mem::transmute(self.create_interface) }; + + let mut host: *mut ICLRMetaHost = ICLRMetaHost::new(create_interface)?; + + return Ok(host); + } +} + +#[cfg(feature = "default-loader")] +fn load_function(library_name: &str, function_name: &str) -> Result { + let library = match unsafe { + LoadLibraryA(windows::core::PCSTR::from_raw( + format!("{}\0", library_name).as_ptr(), + )) + } { + Ok(hinstance) => hinstance, + Err(e) => return Err(format!("Error while loading `{}`: {}", library_name, e)), + }; + + return match unsafe { + GetProcAddress( + library, + windows::core::PCSTR::from_raw(format!("{}\0", function_name).as_ptr()), + ) + } { + None => Err(format!( + "Could not locate `{}` in `{}`", + function_name, library_name + )), + Some(f) => Ok(f as isize), + }; +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..aab06e9 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,2 @@ +pub mod clr; +pub mod primitives; diff --git a/src/primitives/helpers.rs b/src/primitives/helpers.rs new file mode 100644 index 0000000..14f5167 --- /dev/null +++ b/src/primitives/helpers.rs @@ -0,0 +1,116 @@ +use std::{ffi::c_void, mem::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_UI1, VT_VARIANT, + }, + Ole::{ + SafeArrayAccessData, SafeArrayCreate, SafeArrayCreateVector, SafeArrayGetUBound, + SafeArrayPutElement, SafeArrayUnaccessData, + }, + }, +}; + +pub fn prepare_assembly(bytes: &[u8]) -> Result<*mut SAFEARRAY, String> { + let mut bounds = SAFEARRAYBOUND { + cElements: bytes.len() as _, + lLbound: 0, + }; + + let mut safe_array_ptr: *mut SAFEARRAY = unsafe { SafeArrayCreate(VT_UI1, 1, &mut bounds) }; + let mut pv_data: *mut c_void = ptr::null_mut(); + + match unsafe { SafeArrayAccessData(safe_array_ptr, &mut pv_data) } { + Ok(_) => {}, + Err(e) => { + return Err(format!( + "Could not prepare assembly due to a safe array related error: {:?}", + e.code() + )) + }, + } + + unsafe { ptr::copy_nonoverlapping(bytes.as_ptr(), pv_data.cast(), bytes.len()) }; + + match unsafe { SafeArrayUnaccessData(safe_array_ptr) } { + Ok(_) => {}, + Err(e) => { + return Err(format!( + "Could not prepare assembly due to a safe array related error: {:?}", + e.code() + )) + }, + }; + + Ok(safe_array_ptr) +} + +pub fn get_array_length(array_ptr: *mut SAFEARRAY) -> i32 { + unsafe { SafeArrayGetUBound(array_ptr, 1) }.unwrap_or(0) +} + +pub fn empty_array() -> *mut SAFEARRAY { + unsafe { SafeArrayCreateVector(VT_VARIANT, 0, 0) } +} + +pub fn wrap_strings_in_array(strings: &[String]) -> Result { + let mut inner = vec![]; + + for string in strings.iter() { + inner.push(BSTR::from(string).into_raw()) + } + + let mut safe_array_ptr: *mut SAFEARRAY = + unsafe { SafeArrayCreateVector(VT_BSTR, 0, inner.len() as u32) }; + + for i in 0..inner.len() { + let indices: [i32; 1] = [i as _]; + let v_ref = &inner[i]; + match unsafe { SafeArrayPutElement(safe_array_ptr, indices.as_ptr(), *v_ref as *const _) } { + Ok(_) => {}, + Err(e) => { + return Err(format!( + "Could not create an array of strings: {:?}", + e.code() + )) + }, + } + } + + Ok(VARIANT { + Anonymous: VARIANT_0 { + Anonymous: ManuallyDrop::new(VARIANT_0_0 { + vt: VARENUM(VT_BSTR.0 | VT_ARRAY.0), + wReserved1: 0, + wReserved2: 0, + wReserved3: 0, + Anonymous: VARIANT_0_0_0 { + parray: safe_array_ptr, + }, + }), + }, + }) +} + +pub fn wrap_method_arguments(arguments: Vec) -> Result<*mut SAFEARRAY, String> { + let mut variant_array_ptr: *mut SAFEARRAY = unsafe { SafeArrayCreateVector(VT_VARIANT, 0, 1) }; + + for i in 0..arguments.len() { + let indices: [i32; 1] = [i as _]; + let v_ref: *const _ = &arguments[i]; + match unsafe { SafeArrayPutElement(variant_array_ptr, indices.as_ptr(), v_ref as *const _) } + { + Ok(_) => {}, + Err(e) => { + return Err(format!( + "Could not create an array of arguments: {:?}", + e.code() + )) + }, + } + } + + Ok(variant_array_ptr) +} diff --git a/src/primitives/iappdomain.rs b/src/primitives/iappdomain.rs new file mode 100644 index 0000000..7759166 --- /dev/null +++ b/src/primitives/iappdomain.rs @@ -0,0 +1,193 @@ +use crate::primitives::{ + itype::_Type, IUnknown, IUnknownVtbl, Interface, _Assembly, prepare_assembly, GUID, HRESULT, +}; +use std::{ + ffi::{c_long, c_void}, + ops::Deref, + ptr, +}; +use windows::{core::BSTR, Win32::System::Com::SAFEARRAY}; + +#[repr(C)] +pub struct _AppDomain { + pub vtable: *const _AppDomainVtbl, +} + +#[repr(C)] +pub struct _AppDomainVtbl { + pub parent: IUnknownVtbl, + pub GetTypeInfoCount: *const c_void, + pub GetTypeInfo: *const c_void, + pub GetIDsOfNames: *const c_void, + pub Invoke: *const c_void, + pub ToString: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, + pub Equals: *const c_void, + pub GetHashCode: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut c_long) -> HRESULT, + pub GetType: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut _Type) -> HRESULT, + pub InitializeLifetimeService: *const c_void, + pub GetLifetimeService: *const c_void, + pub get_Evidence: *const c_void, + pub set_Evidence: *const c_void, + pub get_DomainUnload: *const c_void, + pub set_DomainUnload: *const c_void, + pub get_AssemblyLoad: *const c_void, + pub set_AssemblyLoad: *const c_void, + pub get_ProcessExit: *const c_void, + pub set_ProcessExit: *const c_void, + pub get_TypeResolve: *const c_void, + pub set_TypeResolve: *const c_void, + pub get_ResourceResolve: *const c_void, + pub set_ResourceResolve: *const c_void, + pub get_AssemblyResolve: *const c_void, + pub get_UnhandledException: *const c_void, + pub set_UnhandledException: *const c_void, + pub DefineDynamicAssembly: *const c_void, + pub DefineDynamicAssembly_2: *const c_void, + pub DefineDynamicAssembly_3: *const c_void, + pub DefineDynamicAssembly_4: *const c_void, + pub DefineDynamicAssembly_5: *const c_void, + pub DefineDynamicAssembly_6: *const c_void, + pub DefineDynamicAssembly_7: *const c_void, + pub DefineDynamicAssembly_8: *const c_void, + pub DefineDynamicAssembly_9: *const c_void, + pub CreateInstance: *const c_void, + pub CreateInstanceFrom: *const c_void, + pub CreateInstance_2: *const c_void, + pub CreateInstanceFrom_2: *const c_void, + pub CreateInstance_3: *const c_void, + pub CreateInstanceFrom_3: *const c_void, + pub Load: *const c_void, + pub Load_2: unsafe extern "system" fn( + this: *mut c_void, + assemblyString: *mut u16, + pRetVal: *mut *mut _Assembly, + ) -> HRESULT, + pub Load_3: unsafe extern "system" fn( + this: *mut c_void, + rawAssembly: *mut SAFEARRAY, + pRetVal: *mut *mut _Assembly, + ) -> HRESULT, + pub Load_4: *const c_void, + pub Load_5: *const c_void, + pub Load_6: *const c_void, + pub Load_7: *const c_void, + pub ExecuteAssembly: *const c_void, + pub ExecuteAssembly_2: *const c_void, + pub ExecuteAssembly_3: *const c_void, + pub get_FriendlyName: *const c_void, + pub get_BaseDirectory: *const c_void, + pub get_RelativeSearchPath: *const c_void, + pub get_ShadowCopyFiles: *const c_void, + pub GetAssemblies: *const c_void, + pub AppendPrivatePath: *const c_void, + pub ClearPrivatePath: *const c_void, + pub ClearShadowCopyPath: *const c_void, + pub SetData: *const c_void, + pub GetData: *const c_void, + pub SetAppDomainPolicy: *const c_void, + pub SetThreadPrincipal: *const c_void, + pub SetPrincipalPolicy: *const c_void, + pub DoCallBack: *const c_void, + pub get_DynamicDirectory: *const c_void, +} + +impl _AppDomain { + pub fn load_library(&self, library: &str) -> Result<*mut _Assembly, String> { + let mut library_buffer = BSTR::from(library); + + let mut library_ptr: *mut _Assembly = ptr::null_mut(); + + let hr = unsafe { (*self).Load_2(library_buffer.into_raw() as *mut _, &mut library_ptr) }; + + if hr.is_err() { + return Err(format!("Could not retrieve `{}`: {:?}", library, hr)); + } + + if library_ptr.is_null() { + return Err(format!("Could not retrieve `{}`", library)); + } + + Ok(library_ptr) + } + + pub fn load_assembly(&self, bytes: &[u8]) -> Result<*mut _Assembly, String> { + let assembly_bytes = prepare_assembly(bytes)?; + + let mut assembly_ptr: *mut _Assembly = ptr::null_mut(); + + let hr = unsafe { (*self).Load_3(assembly_bytes, &mut assembly_ptr) }; + + if hr.is_err() { + return Err(format!("Could not retrieve assembly: {:?}", hr)); + } + + if assembly_ptr.is_null() { + return Err("Could not retrieve assembly".into()); + } + + Ok(assembly_ptr) + } + + pub fn to_string(&self) -> Result { + let mut buffer = BSTR::new(); + + let hr = unsafe { (*self).ToString(&mut buffer as *mut _ as *mut *mut u16) }; + + if hr.is_err() { + return Err(format!("Failed while running `ToString`: {:?}", hr)); + } + + Ok(buffer.to_string()) + } + + #[inline] + pub unsafe fn ToString(&self, pRetVal: *mut *mut u16) -> HRESULT { + ((*self.vtable).ToString)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn GetHashCode(&self, pRetVal: *mut c_long) -> HRESULT { + ((*self.vtable).GetHashCode)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn GetType(&self, pRetVal: *mut *mut _Type) -> HRESULT { + ((*self.vtable).GetType)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn Load_2(&self, assemblyString: *mut u16, pRetVal: *mut *mut _Assembly) -> HRESULT { + ((*self.vtable).Load_2)(self as *const _ as *mut _, assemblyString, pRetVal) + } + + #[inline] + pub unsafe fn Load_3( + &self, + rawAssembly: *mut SAFEARRAY, + pRetVal: *mut *mut _Assembly, + ) -> HRESULT { + ((*self.vtable).Load_3)(self as *const _ as *mut _, rawAssembly, pRetVal) + } +} + +impl Interface for _AppDomain { + const IID: GUID = GUID::from_values( + 0x05F696DC, + 0x2B29, + 0x3663, + [0xAD, 0x8B, 0xC4, 0x38, 0x9C, 0xF2, 0xA7, 0x13], + ); + + fn vtable(&self) -> *const c_void { + self.vtable as *const _ as *const c_void + } +} + +impl Deref for _AppDomain { + type Target = IUnknown; + + #[inline] + fn deref(&self) -> &IUnknown { + unsafe { &*(self as *const _AppDomain as *const IUnknown) } + } +} diff --git a/src/primitives/iassembly.rs b/src/primitives/iassembly.rs new file mode 100644 index 0000000..90fd785 --- /dev/null +++ b/src/primitives/iassembly.rs @@ -0,0 +1,288 @@ +use crate::primitives::{ + itype::_Type, IUnknown, IUnknownVtbl, Interface, _MethodInfo, empty_array, + wrap_method_arguments, wrap_strings_in_array, GUID, HRESULT, +}; +use std::{ + ffi::{c_long, c_void}, + ops::Deref, + ptr, +}; +use windows::{ + core::BSTR, + Win32::System::{ + Com::{SAFEARRAY, VARIANT, VT_UNKNOWN}, + Ole::{SafeArrayCreateVector, SafeArrayGetElement, SafeArrayGetLBound, SafeArrayGetUBound}, + }, +}; + +#[repr(C)] +pub struct _Assembly { + pub vtable: *const _AssemblyVtbl, +} + +#[repr(C)] +pub struct _AssemblyVtbl { + pub parent: IUnknownVtbl, + pub GetTypeInfoCount: *const c_void, + pub GetTypeInfo: *const c_void, + pub GetIDsOfNames: *const c_void, + pub Invoke: *const c_void, + pub ToString: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, + pub Equals: *const c_void, + pub GetHashCode: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut c_long) -> HRESULT, + pub GetType: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut _Type) -> HRESULT, + pub get_CodeBase: + unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, + pub get_EscapedCodeBase: + unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, + pub GetName: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut c_void) -> HRESULT, + pub GetName_2: *const c_void, + pub get_FullName: + unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, + pub get_EntryPoint: + unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut _MethodInfo) -> HRESULT, + pub GetType_2: unsafe extern "system" fn( + this: *mut c_void, + name: *mut u16, + pRetVal: *mut *mut _Type, + ) -> HRESULT, + pub GetType_3: *const c_void, + pub GetExportedTypes: *const c_void, + pub GetTypes: + unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut SAFEARRAY) -> HRESULT, + pub GetManifestResourceStream: *const c_void, + pub GetManifestResourceStream_2: *const c_void, + pub GetFile: *const c_void, + pub GetFiles: *const c_void, + pub GetFiles_2: *const c_void, + pub GetManifestResourceNames: *const c_void, + pub GetManifestResourceInfo: *const c_void, + pub get_Location: + unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, + pub get_Evidence: *const c_void, + pub GetCustomAttributes: *const c_void, + pub GetCustomAttributes_2: *const c_void, + pub IsDefined: *const c_void, + pub GetObjectData: *const c_void, + pub add_ModuleResolve: *const c_void, + pub remove_ModuleResolve: *const c_void, + pub GetType_4: *const c_void, + pub GetSatelliteAssembly: *const c_void, + pub GetSatelliteAssembly_2: *const c_void, + pub LoadModule: *const c_void, + pub LoadModule_2: *const c_void, + pub CreateInstance: unsafe extern "system" fn( + this: *mut c_void, + typeName: *mut u16, + pRetVal: *mut VARIANT, + ) -> HRESULT, + pub CreateInstance_2: *const c_void, + pub CreateInstance_3: *const c_void, + pub GetLoadedModules: *const c_void, + pub GetLoadedModules_2: *const c_void, + pub GetModules: *const c_void, + pub GetModules_2: *const c_void, + pub GetModule: *const c_void, + pub GetReferencedAssemblies: *const c_void, + pub get_GlobalAssemblyCache: *const c_void, +} + +impl _Assembly { + pub fn run_entrypoint(&self, args: &[String]) -> Result { + let entrypoint = unsafe { (*self).get_entrypoint()? }; + let signature = unsafe { (*entrypoint).to_string()? }; + + if signature.ends_with("Main()") { + return unsafe { (*entrypoint).invoke_without_args() }; + } + + if signature.ends_with("Main(System.String[])") { + let args_variant = wrap_strings_in_array(args)?; + let mut method_args = wrap_method_arguments(vec![args_variant])?; + + return unsafe { (*entrypoint).invoke(method_args) }; + } + + Err(format!( + "Cannot handle an entrypoint with this method signature: {}", + signature + )) + } + + pub fn get_entrypoint(&self) -> Result<*mut _MethodInfo, String> { + let mut method_info_ptr: *mut _MethodInfo = ptr::null_mut(); + + let hr = unsafe { (*self).get_EntryPoint(&mut method_info_ptr) }; + + if hr.is_err() { + return Err(format!("Could not retrieve entrypoint: {:?}", hr)); + } + + if method_info_ptr.is_null() { + return Err("Could not retrieve entrypoint".into()); + } + + Ok(method_info_ptr) + } + + pub fn to_string(&self) -> Result { + let mut buffer = BSTR::new(); + + let hr = unsafe { (*self).ToString(&mut buffer as *mut _ as *mut *mut u16) }; + + if hr.is_err() { + return Err(format!("Failed while running `ToString`: {:?}", hr)); + } + + Ok(buffer.to_string()) + } + + #[inline] + pub unsafe fn ToString(&self, pRetVal: *mut *mut u16) -> HRESULT { + ((*self.vtable).ToString)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn GetHashCode(&self, pRetVal: *mut c_long) -> HRESULT { + ((*self.vtable).GetHashCode)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn GetType(&self, pRetVal: *mut *mut _Type) -> HRESULT { + ((*self.vtable).GetType)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn get_CodeBase(&self, pRetVal: *mut *mut u16) -> HRESULT { + ((*self.vtable).get_CodeBase)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn get_EscapedCodeBase(&self, pRetVal: *mut *mut u16) -> HRESULT { + ((*self.vtable).get_EscapedCodeBase)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn GetName(&self, pRetVal: *mut *mut c_void) -> HRESULT { + ((*self.vtable).GetName)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn get_FullName(&self, pRetVal: *mut *mut u16) -> HRESULT { + ((*self.vtable).get_FullName)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn get_EntryPoint(&self, pRetVal: *mut *mut _MethodInfo) -> HRESULT { + ((*self.vtable).get_EntryPoint)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn GetType_2(&self, name: *mut u16, pRetVal: *mut *mut _Type) -> HRESULT { + ((*self.vtable).GetType_2)(self as *const _ as *mut _, name, pRetVal) + } + + #[inline] + pub unsafe fn GetTypes(&self, pRetVal: *mut *mut SAFEARRAY) -> HRESULT { + ((*self.vtable).GetTypes)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn get_Location(&self, pRetVal: *mut *mut u16) -> HRESULT { + ((*self.vtable).get_Location)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn CreateInstance(&self, typeName: *mut u16, pRetVal: *mut VARIANT) -> HRESULT { + ((*self.vtable).CreateInstance)(self as *const _ as *mut _, typeName, pRetVal) + } + + pub fn create_instance(&self, name: &str) -> Result { + let mut dw = BSTR::from(name); + + let mut instance: VARIANT = VARIANT::default(); + let hr = unsafe { (*self).CreateInstance(dw.into_raw() as *mut _, &mut instance) }; + + if hr.is_err() { + return Err(format!( + "Error while creating instance of `{}`: 0x{:x}", + name, hr.0 + )); + } + + Ok(instance) + } + + pub fn get_type(&self, name: &str) -> Result<*mut _Type, String> { + let mut dw = BSTR::from(name); + + let mut type_ptr: *mut _Type = ptr::null_mut(); + let hr = unsafe { (*self).GetType_2(dw.into_raw() as *mut _, &mut type_ptr) }; + + if hr.is_err() { + return Err(format!( + "Error while retrieving type `{}`: 0x{:x}", + name, hr.0 + )); + } + + if type_ptr.is_null() { + return Err(format!("Could not retrieve type `{}`", name)); + } + + Ok(type_ptr) + } + + pub fn get_types(&self) -> Result, String> { + let mut results: Vec<*mut _Type> = vec![]; + + let mut safe_array_ptr: *mut SAFEARRAY = unsafe { SafeArrayCreateVector(VT_UNKNOWN, 0, 0) }; + + let hr = unsafe { (*self).GetTypes(&mut safe_array_ptr) }; + + if hr.is_err() { + return Err(format!("Error while retrieving types: 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 _Type = 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) + } +} + +impl Interface for _Assembly { + const IID: GUID = GUID::from_values( + 0x17156360, + 0x2f1a, + 0x384a, + [0xbc, 0x52, 0xfd, 0xe9, 0x3c, 0x21, 0x5c, 0x5b], + ); + + fn vtable(&self) -> *const c_void { + self.vtable as *const _ as *const c_void + } +} + +impl Deref for _Assembly { + type Target = IUnknown; + + #[inline] + fn deref(&self) -> &IUnknown { + unsafe { &*(self as *const _Assembly as *const IUnknown) } + } +} diff --git a/src/primitives/iclrmetahost.rs b/src/primitives/iclrmetahost.rs new file mode 100644 index 0000000..5e787fb --- /dev/null +++ b/src/primitives/iclrmetahost.rs @@ -0,0 +1,177 @@ +use crate::primitives::{ + Class, ICLRRuntimeInfo, IEnumUnknown, IUnknown, IUnknownVtbl, Interface, GUID, HRESULT, +}; +use std::{ffi::c_void, ops::Deref, ptr}; + +#[repr(C)] +pub struct ICLRMetaHostVtbl { + pub parent: IUnknownVtbl, + pub GetRuntime: unsafe extern "system" fn( + this: *mut c_void, + pwzVersion: *mut u16, + riid: *const GUID, + ppRuntime: *mut *mut c_void, + ) -> HRESULT, + pub GetVersionFromFile: unsafe extern "system" fn(this: *mut c_void) -> HRESULT, + pub EnumerateInstalledRuntimes: unsafe extern "system" fn( + this: *mut c_void, + ppEnumerator: *mut *mut IEnumUnknown, + ) -> HRESULT, + pub EnumerateLoadedRuntimes: unsafe extern "system" fn(this: *mut c_void) -> HRESULT, + pub RequestRuntimeLoadedNotification: unsafe extern "system" fn(this: *mut c_void) -> HRESULT, + pub QueryLegacyV2RuntimeBinding: unsafe extern "system" fn(this: *mut c_void) -> HRESULT, + pub ExitProcess: unsafe extern "system" fn(this: *mut c_void) -> HRESULT, +} + +#[derive(Debug, Clone)] +#[repr(C)] +pub struct ICLRMetaHost { + pub vtable: *const ICLRMetaHostVtbl, +} + +pub type CLRCreateInstance = + fn(class_id: *const GUID, interface_id: *const GUID, interface: *mut *mut c_void) -> HRESULT; + +impl ICLRMetaHost { + pub fn new(clr_create_instance: CLRCreateInstance) -> Result<*mut ICLRMetaHost, String> { + let mut ppv: *mut ICLRMetaHost = ptr::null_mut(); + + let hr = unsafe { + clr_create_instance( + &ICLRMetaHost::CLSID, + &ICLRMetaHost::IID, + &mut ppv as *mut *mut _ as *mut *mut c_void, + ) + }; + + if hr.is_err() { + return Err(format!("{:?}", hr)); + } + + if ppv.is_null() { + return Err("Could not retrieve ICLRMetaHost".into()); + } + + return Ok(ppv); + } + + pub fn get_first_available_runtime(&self) -> Result<*mut ICLRRuntimeInfo, String> { + let runtimes = self.get_installed_runtimes()?; + + if runtimes.len() > 0 { + Ok(runtimes[0]) + } else { + Err("Could not find any runtimes".into()) + } + } + + pub fn get_runtime(&self, version: *mut u16) -> Result<*mut ICLRRuntimeInfo, String> { + let mut ppv: *mut ICLRRuntimeInfo = ptr::null_mut(); + + let hr = unsafe { + (*self).GetRuntime( + version, + &ICLRRuntimeInfo::IID, + &mut ppv as *mut *mut _ as *mut *mut c_void, + ) + }; + + return match hr.is_ok() { + true => Ok(ppv), + false => Err(format!("{:?}", hr)), + }; + } + + pub fn get_installed_runtimes(&self) -> Result, String> { + let mut ieu_ptr: *mut IEnumUnknown = ptr::null_mut(); + + let hr = unsafe { (*self).EnumerateInstalledRuntimes(&mut ieu_ptr) }; + + if hr.is_err() { + return Err(format!("{:?}", hr)); + } + + if ieu_ptr.is_null() { + return Err("Could not enumerate installed runtimes.".into()); + } + + let mut hmri: Vec<*mut ICLRRuntimeInfo> = vec![]; + + loop { + let mut iu_ptr: *mut IUnknown = ptr::null_mut(); + let mut cfetched: u32 = 0; + + let next_hr = unsafe { (*ieu_ptr).Next(1, &mut iu_ptr, &mut cfetched) }; + + if next_hr.is_err() || iu_ptr.is_null() { + break; + } + + let mut ri_ptr: *mut ICLRRuntimeInfo = ptr::null_mut(); + + let inner_hr = unsafe { + (*iu_ptr).QueryInterface( + &ICLRRuntimeInfo::IID, + &mut ri_ptr as *mut _ as *mut *mut c_void, + ) + }; + + if inner_hr.is_err() || ri_ptr.is_null() { + break; + } + + hmri.push(ri_ptr); + } + + Ok(hmri) + } + + #[inline] + pub unsafe fn GetRuntime( + &self, + pwzVersion: *mut u16, + riid: *const GUID, + ppRuntime: *mut *mut c_void, + ) -> HRESULT { + ((*self.vtable).GetRuntime)(self as *const _ as *mut _, pwzVersion, riid, ppRuntime) + } + + #[inline] + pub unsafe fn EnumerateInstalledRuntimes( + &self, + ppEnumerator: *mut *mut IEnumUnknown, + ) -> HRESULT { + ((*self.vtable).EnumerateInstalledRuntimes)(self as *const _ as *mut _, ppEnumerator) + } +} + +impl Interface for ICLRMetaHost { + const IID: GUID = GUID { + data1: 0xD332DB9E, + data2: 0xB9B3, + data3: 0x4125, + data4: [0x82, 0x07, 0xA1, 0x48, 0x84, 0xF5, 0x32, 0x16], + }; + + fn vtable(&self) -> *const c_void { + self.vtable as *const _ as *const c_void + } +} + +impl Class for ICLRMetaHost { + const CLSID: GUID = GUID { + data1: 0x9280188d, + data2: 0xe8e, + data3: 0x4867, + data4: [0xb3, 0xc, 0x7f, 0xa8, 0x38, 0x84, 0xe8, 0xde], + }; +} + +impl Deref for ICLRMetaHost { + type Target = IUnknown; + + #[inline] + fn deref(&self) -> &IUnknown { + unsafe { &*(self as *const ICLRMetaHost as *const IUnknown) } + } +} diff --git a/src/primitives/iclrruntimeinfo.rs b/src/primitives/iclrruntimeinfo.rs new file mode 100644 index 0000000..e66cc96 --- /dev/null +++ b/src/primitives/iclrruntimeinfo.rs @@ -0,0 +1,220 @@ +use crate::primitives::{ + Class, ICorRuntimeHost, IUnknown, IUnknownVtbl, Interface, BOOL, GUID, HANDLE, HRESULT, +}; +use std::{ffi::c_void, ops::Deref, ptr}; + +#[repr(C)] +pub struct ICLRRuntimeInfo { + pub vtable: *const ICLRRuntimeInfoVtbl, +} + +#[repr(C)] +pub struct ICLRRuntimeInfoVtbl { + pub parent: IUnknownVtbl, + pub GetVersionString: unsafe extern "system" fn( + This: *mut ICLRRuntimeInfo, + pwzBuffer: *mut u16, + pcchBuffer: *mut u32, + ) -> HRESULT, + pub GetRuntimeDirectory: unsafe extern "system" fn( + This: *mut ICLRRuntimeInfo, + pwzBuffer: *mut u16, + pcchBuffer: *mut u32, + ) -> HRESULT, + pub IsLoaded: unsafe extern "system" fn( + This: *mut ICLRRuntimeInfo, + hndProcess: HANDLE, + pbLoaded: *mut BOOL, + ) -> HRESULT, + pub LoadErrorString: unsafe extern "system" fn( + This: *mut ICLRRuntimeInfo, + iResourceID: u32, + pwzBuffer: *mut u16, + pcchBuffer: *mut u32, + iLocaleID: u32, + ) -> HRESULT, + pub LoadLibrary: unsafe extern "system" fn( + This: *mut ICLRRuntimeInfo, + pwzDllName: *const u16, + ppProc: *mut *mut c_void, + ) -> HRESULT, + pub GetProcAddress: unsafe extern "system" fn( + This: *mut ICLRRuntimeInfo, + pszProcName: *const i8, + ppProc: *mut *mut c_void, + ) -> HRESULT, + pub GetInterface: unsafe extern "system" fn( + This: *mut ICLRRuntimeInfo, + rclsid: *const GUID, + riid: *const GUID, + ppUnk: *mut *mut c_void, + ) -> HRESULT, + pub IsLoadable: + unsafe extern "system" fn(This: *mut ICLRRuntimeInfo, pbLoadable: *mut BOOL) -> HRESULT, + pub SetDefaultStartupFlags: unsafe extern "system" fn( + This: *mut ICLRRuntimeInfo, + dwStartupFlags: u32, + pwzHostConfigFile: *const u16, + ) -> HRESULT, + pub GetDefaultStartupFlags: unsafe extern "system" fn( + This: *mut ICLRRuntimeInfo, + pdwStartupFlags: *mut u32, + pwzHostConfigFile: *mut u16, + pcchHostConfigFile: *mut u32, + ) -> HRESULT, + pub BindAsLegacyV2Runtime: unsafe extern "system" fn(This: *mut ICLRRuntimeInfo) -> HRESULT, + pub IsStarted: unsafe extern "system" fn( + This: *mut ICLRRuntimeInfo, + pbStarted: *mut BOOL, + pdwStartupFlags: *mut u32, + ) -> HRESULT, +} + +impl ICLRRuntimeInfo { + pub fn get_runtime_host(&self) -> Result<*mut ICorRuntimeHost, String> { + let mut ppv: *mut ICorRuntimeHost = ptr::null_mut(); + + let hr = unsafe { + (*self).GetInterface( + &ICorRuntimeHost::CLSID, + &ICorRuntimeHost::IID, + &mut ppv as *mut *mut _ as *mut *mut c_void, + ) + }; + + if hr.is_err() { + return Err(format!("Could not retrieve ICorRuntimeHost: {:?}", hr)); + } + + if ppv.is_null() { + return Err("Could not retrieve ICorRuntimeHost".into()); + } + + return Ok(ppv); + } + + pub fn has_started() -> Result { + todo!() + } + + #[inline] + pub unsafe fn GetVersionString(&self, pwzBuffer: *mut u16, pcchBuffer: *mut u32) -> HRESULT { + ((*self.vtable).GetVersionString)(self as *const _ as *mut _, pwzBuffer, pcchBuffer) + } + + #[inline] + pub unsafe fn GetRuntimeDirectory(&self, pwzBuffer: *mut u16, pcchBuffer: *mut u32) -> HRESULT { + ((*self.vtable).GetRuntimeDirectory)(self as *const _ as *mut _, pwzBuffer, pcchBuffer) + } + + #[inline] + pub unsafe fn IsLoaded(&self, hndProcess: HANDLE, pbLoaded: *mut BOOL) -> HRESULT { + ((*self.vtable).IsLoaded)(self as *const _ as *mut _, hndProcess, pbLoaded) + } + + #[inline] + pub unsafe fn LoadErrorString( + &self, + iResourceID: u32, + pwzBuffer: *mut u16, + pcchBuffer: *mut u32, + iLocaleID: u32, + ) -> HRESULT { + ((*self.vtable).LoadErrorString)( + self as *const _ as *mut _, + iResourceID, + pwzBuffer, + pcchBuffer, + iLocaleID, + ) + } + + #[inline] + pub unsafe fn LoadLibrary(&self, pwzDllName: *const u16, ppProc: *mut *mut c_void) -> HRESULT { + ((*self.vtable).LoadLibrary)(self as *const _ as *mut _, pwzDllName, ppProc) + } + + #[inline] + pub unsafe fn GetProcAddress( + &self, + pszProcName: *const i8, + ppProc: *mut *mut c_void, + ) -> HRESULT { + ((*self.vtable).GetProcAddress)(self as *const _ as *mut _, pszProcName, ppProc) + } + + #[inline] + pub unsafe fn GetInterface( + &self, + rclsid: *const GUID, + riid: *const GUID, + ppUnk: *mut *mut c_void, + ) -> HRESULT { + ((*self.vtable).GetInterface)(self as *const _ as *mut _, rclsid, riid, ppUnk) + } + + #[inline] + pub unsafe fn IsLoadable(&self, pbLoadable: *mut BOOL) -> HRESULT { + ((*self.vtable).IsLoadable)(self as *const _ as *mut _, pbLoadable) + } + + #[inline] + pub unsafe fn SetDefaultStartupFlags( + &self, + dwStartupFlags: u32, + pwzHostConfigFile: *const u16, + ) -> HRESULT { + ((*self.vtable).SetDefaultStartupFlags)( + self as *const _ as *mut _, + dwStartupFlags, + pwzHostConfigFile, + ) + } + + #[inline] + pub unsafe fn GetDefaultStartupFlags( + &self, + pdwStartupFlags: *mut u32, + pwzHostConfigFile: *mut u16, + pcchHostConfigFile: *mut u32, + ) -> HRESULT { + ((*self.vtable).GetDefaultStartupFlags)( + self as *const _ as *mut _, + pdwStartupFlags, + pwzHostConfigFile, + pcchHostConfigFile, + ) + } + + #[inline] + pub unsafe fn BindAsLegacyV2Runtime(&self) -> HRESULT { + ((*self.vtable).BindAsLegacyV2Runtime)(self as *const _ as *mut _) + } + + #[inline] + pub unsafe fn IsStarted(&self, pbStarted: *mut BOOL, pdwStartupFlags: *mut u32) -> HRESULT { + ((*self.vtable).IsStarted)(self as *const _ as *mut _, pbStarted, pdwStartupFlags) + } +} + +impl Interface for ICLRRuntimeInfo { + const IID: GUID = GUID::from_values( + 0xBD39D1D2, + 0xBA2F, + 0x486a, + [0x89, 0xB0, 0xB4, 0xB0, 0xCB, 0x46, 0x68, 0x91], + ); + + fn vtable(&self) -> *const c_void { + self.vtable as *const _ as *const c_void + } +} + +impl Deref for ICLRRuntimeInfo { + type Target = IUnknown; + + #[inline] + fn deref(&self) -> &IUnknown { + unsafe { &*(self as *const ICLRRuntimeInfo as *const IUnknown) } + } +} diff --git a/src/primitives/icorruntimehost.rs b/src/primitives/icorruntimehost.rs new file mode 100644 index 0000000..58f5fae --- /dev/null +++ b/src/primitives/icorruntimehost.rs @@ -0,0 +1,266 @@ +use std::{ffi::c_void, ops::Deref, ptr}; + +use crate::primitives::{ + Class, IUnknown, IUnknownVtbl, Interface, _AppDomain, GUID, HANDLE, HINSTANCE, HRESULT, +}; + +#[repr(C)] +pub struct ICorRuntimeHost { + pub vtable: *const ICorRuntimeHostVtbl, +} + +#[repr(C)] +pub struct ICorRuntimeHostVtbl { + pub parent: IUnknownVtbl, + pub CreateLogicalThreadState: unsafe extern "system" fn(This: *mut ICorRuntimeHost) -> HRESULT, + pub DeleteLogicalThreadState: unsafe extern "system" fn(This: *mut ICorRuntimeHost) -> HRESULT, + pub SwitchInLogicalThreadState: + unsafe extern "system" fn(This: *mut ICorRuntimeHost, pFiberCookie: *mut u32) -> HRESULT, + pub SwitchOutLogicalThreadState: unsafe extern "system" fn( + This: *mut ICorRuntimeHost, + pFiberCookie: *mut *mut u32, + ) -> HRESULT, + pub LocksHeldByLogicalThread: + unsafe extern "system" fn(This: *mut ICorRuntimeHost, pCount: *mut u32) -> HRESULT, + pub MapFile: unsafe extern "system" fn( + This: *mut ICorRuntimeHost, + hFile: HANDLE, + hMapAddress: *mut HINSTANCE, + ) -> HRESULT, + pub GetConfiguration: unsafe extern "system" fn( + This: *mut ICorRuntimeHost, + pConfiguration: *mut *mut c_void, + ) -> HRESULT, + pub Start: unsafe extern "system" fn(This: *mut ICorRuntimeHost) -> HRESULT, + pub Stop: unsafe extern "system" fn(This: *mut ICorRuntimeHost) -> HRESULT, + pub CreateDomain: unsafe extern "system" fn( + This: *mut ICorRuntimeHost, + pwzFriendlyName: *const u16, + pIdentityArray: *mut IUnknown, + pAppDomain: *mut *mut IUnknown, + ) -> HRESULT, + pub GetDefaultDomain: unsafe extern "system" fn( + This: *mut ICorRuntimeHost, + pAppDomain: *mut *mut IUnknown, + ) -> HRESULT, + pub EnumDomains: + unsafe extern "system" fn(This: *mut ICorRuntimeHost, hEnum: *mut *mut c_void) -> HRESULT, + pub NextDomain: unsafe extern "system" fn( + This: *mut ICorRuntimeHost, + hEnum: *mut c_void, + pAppDomain: *mut *mut IUnknown, + ) -> HRESULT, + pub CloseEnum: + unsafe extern "system" fn(This: *mut ICorRuntimeHost, hEnum: *mut c_void) -> HRESULT, + pub CreateDomainEx: unsafe extern "system" fn( + This: *mut ICorRuntimeHost, + pwzFriendlyName: *const u16, + pSetup: *mut IUnknown, + pEvidence: *mut IUnknown, + pAppDomain: *mut *mut IUnknown, + ) -> HRESULT, + pub CreateDomainSetup: unsafe extern "system" fn( + This: *mut ICorRuntimeHost, + pAppDomain: *mut *mut IUnknown, + ) -> HRESULT, + pub CreateEvidence: unsafe extern "system" fn( + This: *mut ICorRuntimeHost, + pEvidence: *mut *mut IUnknown, + ) -> HRESULT, + pub UnloadDomain: + unsafe extern "system" fn(This: *mut ICorRuntimeHost, pAppDomain: *mut IUnknown) -> HRESULT, + pub CurrentDomain: unsafe extern "system" fn( + This: *mut ICorRuntimeHost, + pAppDomain: *mut *mut IUnknown, + ) -> HRESULT, +} + +impl ICorRuntimeHost { + pub fn start(&self) -> Result<(), String> { + return match unsafe { (*self).Start().ok() } { + Ok(_) => Ok(()), + Err(e) => Err(format!("Could not start runtime host: {:?}", e)), + }; + } + + pub fn get_default_domain(&self) -> Result<*mut _AppDomain, String> { + let mut unknown: *mut IUnknown = ptr::null_mut(); + + let hr = unsafe { (*self).GetDefaultDomain(&mut unknown) }; + + if hr.is_err() { + return Err(format!("Could not retrieve default app domain: {:?}", hr)); + } + + if unknown.is_null() { + return Err("Could not retrieve default app domain".into()); + } + + let mut app_domain: *mut _AppDomain = ptr::null_mut(); + + let hr = unsafe { + (*unknown).QueryInterface( + &_AppDomain::IID, + &mut app_domain as *mut *mut _ as *mut *mut c_void, + ) + }; + + if hr.is_err() { + return Err(format!("Could not retrieve default app domain: {:?}", hr)); + } + + if app_domain.is_null() { + return Err("Could not retrieve default app domain".into()); + } + + Ok(app_domain) + } + + #[inline] + pub unsafe fn CreateLogicalThreadState(&self) -> HRESULT { + ((*self.vtable).CreateLogicalThreadState)(self as *const _ as *mut _) + } + + #[inline] + pub unsafe fn DeleteLogicalThreadState(&self) -> HRESULT { + ((*self.vtable).DeleteLogicalThreadState)(self as *const _ as *mut _) + } + + #[inline] + pub unsafe fn SwitchInLogicalThreadState(&self, pFiberCookie: *mut u32) -> HRESULT { + ((*self.vtable).SwitchInLogicalThreadState)(self as *const _ as *mut _, pFiberCookie) + } + + #[inline] + pub unsafe fn SwitchOutLogicalThreadState(&self, pFiberCookie: *mut *mut u32) -> HRESULT { + ((*self.vtable).SwitchOutLogicalThreadState)(self as *const _ as *mut _, pFiberCookie) + } + + #[inline] + pub unsafe fn LocksHeldByLogicalThread(&self, pCount: *mut u32) -> HRESULT { + ((*self.vtable).LocksHeldByLogicalThread)(self as *const _ as *mut _, pCount) + } + + #[inline] + pub unsafe fn MapFile(&self, hFile: HANDLE, hMapAddress: *mut HINSTANCE) -> HRESULT { + ((*self.vtable).MapFile)(self as *const _ as *mut _, hFile, hMapAddress) + } + + #[inline] + pub unsafe fn GetConfiguration(&self, pConfiguration: *mut *mut c_void) -> HRESULT { + ((*self.vtable).GetConfiguration)(self as *const _ as *mut _, pConfiguration) + } + + #[inline] + pub unsafe fn Start(&self) -> HRESULT { + ((*self.vtable).Start)(self as *const _ as *mut _) + } + + #[inline] + pub unsafe fn Stop(&self) -> HRESULT { + ((*self.vtable).Stop)(self as *const _ as *mut _) + } + + #[inline] + pub unsafe fn CreateDomain( + &self, + pwzFriendlyName: *const u16, + pIdentityArray: *mut IUnknown, + pAppDomain: *mut *mut IUnknown, + ) -> HRESULT { + ((*self.vtable).CreateDomain)( + self as *const _ as *mut _, + pwzFriendlyName, + pIdentityArray, + pAppDomain, + ) + } + + #[inline] + pub unsafe fn GetDefaultDomain(&self, pAppDomain: *mut *mut IUnknown) -> HRESULT { + ((*self.vtable).GetDefaultDomain)(self as *const _ as *mut _, pAppDomain) + } + + #[inline] + pub unsafe fn EnumDomains(&self, hEnum: *mut *mut c_void) -> HRESULT { + ((*self.vtable).EnumDomains)(self as *const _ as *mut _, hEnum) + } + + #[inline] + pub unsafe fn NextDomain(&self, hEnum: *mut c_void, pAppDomain: *mut *mut IUnknown) -> HRESULT { + ((*self.vtable).NextDomain)(self as *const _ as *mut _, hEnum, pAppDomain) + } + + #[inline] + pub unsafe fn CloseEnum(&self, hEnum: *mut c_void) -> HRESULT { + ((*self.vtable).CloseEnum)(self as *const _ as *mut _, hEnum) + } + + #[inline] + pub unsafe fn CreateDomainEx( + &self, + pwzFriendlyName: *const u16, + pSetup: *mut IUnknown, + pEvidence: *mut IUnknown, + pAppDomain: *mut *mut IUnknown, + ) -> HRESULT { + ((*self.vtable).CreateDomainEx)( + self as *const _ as *mut _, + pwzFriendlyName, + pSetup, + pEvidence, + pAppDomain, + ) + } + + #[inline] + pub unsafe fn CreateDomainSetup(&self, pAppDomain: *mut *mut IUnknown) -> HRESULT { + ((*self.vtable).CreateDomainSetup)(self as *const _ as *mut _, pAppDomain) + } + + #[inline] + pub unsafe fn CreateEvidence(&self, pEvidence: *mut *mut IUnknown) -> HRESULT { + ((*self.vtable).CreateEvidence)(self as *const _ as *mut _, pEvidence) + } + + #[inline] + pub unsafe fn UnloadDomain(&self, pAppDomain: *mut IUnknown) -> HRESULT { + ((*self.vtable).UnloadDomain)(self as *const _ as *mut _, pAppDomain) + } + + #[inline] + pub unsafe fn CurrentDomain(&self, pAppDomain: *mut *mut IUnknown) -> HRESULT { + ((*self.vtable).CurrentDomain)(self as *const _ as *mut _, pAppDomain) + } +} + +impl Interface for ICorRuntimeHost { + const IID: GUID = GUID::from_values( + 0xCB2F6722, + 0xAB3A, + 0x11d2, + [0x9C, 0x40, 0x00, 0xC0, 0x4F, 0xA3, 0x0A, 0x3E], + ); + + fn vtable(&self) -> *const c_void { + self.vtable as *const _ as *const c_void + } +} + +impl Class for ICorRuntimeHost { + const CLSID: GUID = GUID::from_values( + 0xcb2f6723, + 0xab3a, + 0x11d2, + [0x9c, 0x40, 0x00, 0xc0, 0x4f, 0xa3, 0x0a, 0x3e], + ); +} + +impl Deref for ICorRuntimeHost { + type Target = IUnknown; + + #[inline] + fn deref(&self) -> &IUnknown { + unsafe { &*(self as *const ICorRuntimeHost as *const IUnknown) } + } +} diff --git a/src/primitives/ienumunknown.rs b/src/primitives/ienumunknown.rs new file mode 100644 index 0000000..a081345 --- /dev/null +++ b/src/primitives/ienumunknown.rs @@ -0,0 +1,71 @@ +use crate::primitives::{IUnknown, IUnknownVtbl, Interface, GUID, HRESULT}; +use std::{ffi::c_void, ops::Deref}; + +#[repr(C)] +pub struct IEnumUnknown { + pub vtable: *const IEnumUnknownVtbl, +} + +#[repr(C)] +pub struct IEnumUnknownVtbl { + pub parent: IUnknownVtbl, + pub Next: unsafe extern "system" fn( + this: *mut c_void, + celt: u32, + rgelt: *mut *mut IUnknown, + pceltFetched: *mut u32, + ) -> HRESULT, + pub Skip: unsafe extern "system" fn(this: *mut c_void, celt: u32) -> HRESULT, + pub Reset: unsafe extern "system" fn(this: *mut c_void) -> HRESULT, + pub Clone: + unsafe extern "system" fn(this: *mut c_void, ppenum: *mut *mut IEnumUnknown) -> HRESULT, +} + +impl IEnumUnknown { + #[inline] + pub unsafe fn Next( + &self, + celt: u32, + rgelt: *mut *mut IUnknown, + pceltFetched: *mut u32, + ) -> HRESULT { + ((*self.vtable).Next)(self as *const _ as *mut _, celt, rgelt, pceltFetched) + } + + #[inline] + pub unsafe fn Skip(&self, celt: u32) -> HRESULT { + ((*self.vtable).Skip)(self as *const _ as *mut _, celt) + } + + #[inline] + pub unsafe fn Reset(&self) -> HRESULT { + ((*self.vtable).Reset)(self as *const _ as *mut _) + } + + #[inline] + pub unsafe fn Clone(&self, ppenum: *mut *mut IEnumUnknown) -> HRESULT { + ((*self.vtable).Clone)(self as *const _ as *mut _, ppenum) + } +} + +impl Interface for IEnumUnknown { + const IID: GUID = GUID::from_values( + 0x00000100, + 0x0000, + 0x0000, + [0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46], + ); + + fn vtable(&self) -> *const c_void { + self.vtable as *const _ as *const c_void + } +} + +impl Deref for IEnumUnknown { + type Target = IUnknown; + + #[inline] + fn deref(&self) -> &IUnknown { + unsafe { &*(self as *const IEnumUnknown as *const IUnknown) } + } +} diff --git a/src/primitives/imethodinfo.rs b/src/primitives/imethodinfo.rs new file mode 100644 index 0000000..503d0f7 --- /dev/null +++ b/src/primitives/imethodinfo.rs @@ -0,0 +1,187 @@ +use crate::primitives::{ + empty_array, get_array_length, itype::_Type, IUnknown, IUnknownVtbl, Interface, GUID, HRESULT, +}; +use std::{ + ffi::{c_long, c_void}, + ops::Deref, +}; +use windows::{ + core::BSTR, + Win32::System::{ + Com::{SAFEARRAY, VARIANT, VT_UNKNOWN}, + Ole::{SafeArrayCreateVector, SafeArrayGetLBound, SafeArrayGetUBound}, + }, +}; + +#[repr(C)] +pub struct _MethodInfo { + pub vtable: *const _MethodInfoVtbl, +} + +#[repr(C)] +pub struct _MethodInfoVtbl { + pub parent: IUnknownVtbl, + pub GetTypeInfoCount: *const c_void, + pub GetTypeInfo: *const c_void, + pub GetIDsOfNames: *const c_void, + pub Invoke: *const c_void, + pub ToString: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, + pub Equals: *const c_void, + pub GetHashCode: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut c_long) -> HRESULT, + pub GetType: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut _Type) -> HRESULT, + pub get_MemberType: *const c_void, + pub get_name: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, + pub get_DeclaringType: *const c_void, + pub get_ReflectedType: *const c_void, + pub GetCustomAttributes: *const c_void, + pub GetCustomAttributes_2: *const c_void, + pub IsDefined: *const c_void, + pub GetParameters: + unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut SAFEARRAY) -> HRESULT, + pub GetMethodImplementationFlags: *const c_void, + pub get_MethodHandle: *const c_void, + pub get_Attributes: *const c_void, + pub get_CallingConvention: *const c_void, + pub Invoke_2: *const c_void, + pub get_IsPublic: *const c_void, + pub get_IsPrivate: *const c_void, + pub get_IsFamily: *const c_void, + pub get_IsAssembly: *const c_void, + pub get_IsFamilyAndAssembly: *const c_void, + pub get_IsFamilyOrAssembly: *const c_void, + pub get_IsStatic: *const c_void, + pub get_IsFinal: *const c_void, + pub get_IsVirtual: *const c_void, + pub get_IsHideBySig: *const c_void, + pub get_IsAbstract: *const c_void, + pub get_IsSpecialName: *const c_void, + pub get_IsConstructor: *const c_void, + pub Invoke_3: unsafe extern "system" fn( + this: *mut c_void, + obj: VARIANT, + parameters: *mut SAFEARRAY, + pRetVal: *mut VARIANT, + ) -> HRESULT, + pub get_returnType: *const c_void, + pub get_ReturnTypeCustomAttributes: *const c_void, + pub GetBaseDefinition: + unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut _MethodInfo) -> HRESULT, +} + +impl _MethodInfo { + pub fn invoke(&self, args: *mut SAFEARRAY) -> Result { + let args_len = get_array_length(args); + let parameter_count = unsafe { (*self).get_parameter_count()? }; + + if args_len != parameter_count { + return Err(format!( + "Arguments do not match method signature: {} given, {} expected", + args_len, parameter_count + )); + } + + let mut return_value: VARIANT = unsafe { std::mem::zeroed() }; + let object: VARIANT = unsafe { std::mem::zeroed() }; + + let hr = unsafe { (*self).Invoke_3(object, args, &mut return_value) }; + + if hr.is_err() { + return Err(format!("Could not invoke method: {:?}", hr)); + } + + Ok(return_value) + } + + pub fn invoke_without_args(&self) -> Result { + let method_args = empty_array(); + + unsafe { (*self).invoke(method_args) } + } + + pub fn get_parameter_count(&self) -> Result { + let mut safe_array_ptr: *mut SAFEARRAY = + unsafe { SafeArrayCreateVector(VT_UNKNOWN, 0, 255) }; + + let hr = unsafe { (*self).GetParameters(&mut safe_array_ptr) }; + + if hr.is_err() { + return Err(format!("Could not get parameter count: {:?}", hr)); + } + + Ok(get_array_length(safe_array_ptr)) + } + + pub fn to_string(&self) -> Result { + let mut buffer = BSTR::new(); + + let hr = unsafe { (*self).ToString(&mut buffer as *mut _ as *mut *mut u16) }; + + if hr.is_err() { + return Err(format!("Failed while running `ToString`: {:?}", hr)); + } + + Ok(buffer.to_string()) + } + + #[inline] + pub unsafe fn ToString(&self, pRetVal: *mut *mut u16) -> HRESULT { + ((*self.vtable).ToString)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn GetHashCode(&self, pRetVal: *mut c_long) -> HRESULT { + ((*self.vtable).GetHashCode)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn GetType(&self, pRetVal: *mut *mut _Type) -> HRESULT { + ((*self.vtable).GetType)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn get_name(&self, pRetVal: *mut *mut u16) -> HRESULT { + ((*self.vtable).get_name)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn GetParameters(&self, pRetVal: *mut *mut SAFEARRAY) -> HRESULT { + ((*self.vtable).GetParameters)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn Invoke_3( + &self, + obj: VARIANT, + parameters: *mut SAFEARRAY, + pRetVal: *mut VARIANT, + ) -> HRESULT { + ((*self.vtable).Invoke_3)(self as *const _ as *mut _, obj, parameters, pRetVal) + } + + #[inline] + pub unsafe fn GetBaseDefinition(&self, pRetVal: *mut *mut _MethodInfo) -> HRESULT { + ((*self.vtable).GetBaseDefinition)(self as *const _ as *mut _, pRetVal) + } +} + +impl Interface for _MethodInfo { + const IID: GUID = GUID::from_values( + 0xffcc1b5d, + 0xecb8, + 0x38dd, + [0x9b, 0x01, 0x3d, 0xc8, 0xab, 0xc2, 0xaa, 0x5f], + ); + + fn vtable(&self) -> *const c_void { + self.vtable as *const _ as *const c_void + } +} + +impl Deref for _MethodInfo { + type Target = IUnknown; + + #[inline] + fn deref(&self) -> &IUnknown { + unsafe { &*(self as *const _MethodInfo as *const IUnknown) } + } +} diff --git a/src/primitives/istringwriter.rs b/src/primitives/istringwriter.rs new file mode 100644 index 0000000..1a1d4a5 --- /dev/null +++ b/src/primitives/istringwriter.rs @@ -0,0 +1,59 @@ +use crate::primitives::{IUnknown, IUnknownVtbl, Interface, GUID, HRESULT}; +use std::{ffi::c_void, ops::Deref}; +use windows::core::BSTR; + +#[repr(C)] +pub struct _StringWriter { + pub vtable: *const _StringWriterVtbl, +} + +#[repr(C)] +pub struct _StringWriterVtbl { + pub parent: IUnknownVtbl, + pub GetTypeInfoCount: *const c_void, + pub GetTypeInfo: *const c_void, + pub GetIDsOfNames: *const c_void, + pub Invoke: *const c_void, + pub ToString: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, +} + +impl _StringWriter { + pub fn to_string(&self) -> Result { + let mut buffer = BSTR::new(); + + let hr = unsafe { (*self).ToString(&mut buffer as *mut _ as *mut *mut u16) }; + + if hr.is_err() { + return Err(format!("Failed while running `ToString`: {:?}", hr)); + } + + Ok(buffer.to_string()) + } + + #[inline] + pub unsafe fn ToString(&self, pRetVal: *mut *mut u16) -> HRESULT { + ((*self.vtable).ToString)(self as *const _ as *mut _, pRetVal) + } +} + +impl Interface for _StringWriter { + const IID: GUID = GUID::from_values( + 0xcb9f94c0, + 0xd691, + 0x3b62, + [0xb0, 0xb2, 0x3c, 0xe5, 0x30, 0x9c, 0xfa, 0x62], + ); + + fn vtable(&self) -> *const c_void { + self.vtable as *const _ as *const c_void + } +} + +impl Deref for _StringWriter { + type Target = IUnknown; + + #[inline] + fn deref(&self) -> &IUnknown { + unsafe { &*(self as *const _StringWriter as *const IUnknown) } + } +} diff --git a/src/primitives/itype.rs b/src/primitives/itype.rs new file mode 100644 index 0000000..dca2cce --- /dev/null +++ b/src/primitives/itype.rs @@ -0,0 +1,392 @@ +use crate::primitives::{IUnknown, IUnknownVtbl, Interface, _Assembly, _MethodInfo, GUID, HRESULT}; +use std::{ + ffi::{c_long, c_void}, + ops::Deref, + ptr, +}; +use windows::{ + core::BSTR, + Win32::System::{ + Com::{SAFEARRAY, VT_UNKNOWN}, + Ole::{SafeArrayCreateVector, SafeArrayGetElement, SafeArrayGetLBound, SafeArrayGetUBound}, + }, +}; + +#[repr(C)] +pub struct _Type { + pub vtable: *const _TypeVtbl, +} + +#[repr(C)] +pub struct _TypeVtbl { + pub parent: IUnknownVtbl, + pub GetTypeInfoCount: + unsafe extern "system" fn(this: *mut c_void, pctinfo: *mut u32) -> HRESULT, + pub GetTypeInfo: *const c_void, + pub GetIDsOfNames: *const c_void, + pub Invoke: *const c_void, + pub ToString: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, + pub Equals: *const c_void, + pub GetHashCode: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut c_long) -> HRESULT, + pub GetType: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut _Type) -> HRESULT, + pub get_MemberType: *const c_void, + pub get_Name: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, + pub get_DeclaringType: + unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut _Type) -> HRESULT, + pub get_ReflectedType: + unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut _Type) -> HRESULT, + pub GetCustomAttributes: *const c_void, + pub GetCustomAttributes_2: *const c_void, + pub IsDefined: *const c_void, + pub get_Guid: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut GUID) -> HRESULT, + pub get_Module: *const c_void, + pub get_Assembly: + unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut _Assembly) -> HRESULT, + pub get_TypeHandle: *const c_void, + pub get_FullName: + unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, + pub get_Namespace: + unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, + pub get_AssemblyQualifiedName: + unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, + 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 GetInterface: *const c_void, + pub GetInterfaces: *const c_void, + pub FindInterfaces: *const c_void, + pub GetEvent: *const c_void, + pub GetEvents: *const c_void, + pub GetEvents_2: *const c_void, + pub GetNestedTypes: *const c_void, + pub GetNestedType: *const c_void, + pub GetMember: *const c_void, + pub GetDefaultMembers: *const c_void, + pub FindMembers: *const c_void, + pub GetElementType: *const c_void, + pub IsSubclassOf: *const c_void, + pub IsInstanceOfType: *const c_void, + pub IsAssignableFrom: *const c_void, + pub GetInterfaceMap: *const c_void, + pub GetMethod: *const c_void, + pub GetMethod_2: unsafe extern "system" fn( + this: *mut c_void, + name: *mut u16, + bindingAttr: BindingFlags, + pRetVal: *mut *mut _MethodInfo, + ) -> HRESULT, + pub GetMethods: unsafe extern "system" fn( + this: *mut c_void, + bindingAttr: BindingFlags, + pRetVal: *mut *mut SAFEARRAY, + ) -> HRESULT, + pub GetField: *const c_void, + pub GetFields: *const c_void, + pub GetProperty: *const c_void, + pub GetProperty_2: *const c_void, + pub GetProperties: *const c_void, + pub GetMember_2: *const c_void, + pub GetMembers: *const c_void, + pub InvokeMember: *const c_void, + pub get_UnderlyingSystemType: *const c_void, + pub InvokeMember_2: *const c_void, + pub InvokeMember_3: *const c_void, + pub GetConstructor: *const c_void, + pub GetConstructor_2: *const c_void, + pub GetConstructor_3: *const c_void, + pub GetConstructors_2: *const c_void, + pub get_TypeInitializer: *const c_void, + pub GetMethod_3: *const c_void, + pub GetMethod_4: unsafe extern "system" fn( + this: *mut c_void, + name: *mut u16, + types: *mut SAFEARRAY, + modifiers: *mut SAFEARRAY, + pRetVal: *mut *mut _MethodInfo, + ) -> HRESULT, + pub GetMethod_5: unsafe extern "system" fn( + this: *mut c_void, + name: *mut u16, + types: *mut SAFEARRAY, + pRetVal: *mut *mut _MethodInfo, + ) -> HRESULT, + pub GetMethod_6: unsafe extern "system" fn( + this: *mut c_void, + name: *mut u16, + pRetVal: *mut *mut _MethodInfo, + ) -> HRESULT, + pub GetMethods_2: + unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut SAFEARRAY) -> HRESULT, + pub GetField_2: *const c_void, + pub GetFields_2: *const c_void, + pub GetInterface_2: *const c_void, + pub GetEvent_2: *const c_void, + pub GetProperty_3: *const c_void, + pub GetProperty_4: *const c_void, + pub GetProperty_5: *const c_void, + pub GetProperty_6: *const c_void, + pub GetProperty_7: *const c_void, + pub GetProperties_2: *const c_void, + pub GetNestedTypes_2: *const c_void, + pub GetNestedType_2: *const c_void, + pub GetMember_3: *const c_void, + pub GetMembers_2: *const c_void, + pub get_Attributes: *const c_void, + pub get_IsNotPublic: *const c_void, + pub get_IsPublic: *const c_void, + pub get_IsNestedPublic: *const c_void, + pub get_IsNestedPrivate: *const c_void, + pub get_IsNestedFamily: *const c_void, + pub get_IsNestedAssembly: *const c_void, + pub get_IsNestedFamANDAssem: *const c_void, + pub get_IsNestedFamORAssem: *const c_void, + pub get_IsAutoLayout: *const c_void, + pub get_IsLayoutSequential: *const c_void, + pub get_IsExplicitLayout: *const c_void, + pub get_IsClass: *const c_void, + pub get_IsInterface: *const c_void, + pub get_IsValueType: *const c_void, + pub get_IsAbstract: *const c_void, + pub get_IsSealed: *const c_void, + pub get_IsEnum: *const c_void, + pub get_IsSpecialName: *const c_void, + pub get_IsImport: *const c_void, + pub get_IsSerializable: *const c_void, + pub get_IsAnsiClass: *const c_void, + pub get_IsUnicodeClass: *const c_void, + pub get_IsAutoClass: *const c_void, + pub get_IsArray: *const c_void, + pub get_IsByRef: *const c_void, + pub get_IsPointer: *const c_void, + pub get_IsPrimitive: *const c_void, + pub get_IsCOMObject: *const c_void, + pub get_HasElementType: *const c_void, + pub get_IsContextful: *const c_void, + pub get_IsMarshalByRef: *const c_void, + pub Equals_2: *const c_void, +} + +impl _Type { + pub fn to_string(&self) -> Result { + let mut buffer = BSTR::new(); + + let hr = unsafe { (*self).ToString(&mut buffer as *mut _ as *mut *mut u16) }; + + if hr.is_err() { + return Err(format!("Failed while running `ToString`: {:?}", hr)); + } + + Ok(buffer.to_string()) + } + + #[inline] + pub unsafe fn GetTypeInfoCount(&self, pctinfo: *mut u32) -> HRESULT { + ((*self.vtable).GetTypeInfoCount)(self as *const _ as *mut _, pctinfo) + } + + #[inline] + pub unsafe fn ToString(&self, pRetVal: *mut *mut u16) -> HRESULT { + ((*self.vtable).ToString)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn GetHashCode(&self, pRetVal: *mut c_long) -> HRESULT { + ((*self.vtable).GetHashCode)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn GetType(&self, pRetVal: *mut *mut _Type) -> HRESULT { + ((*self.vtable).GetType)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn get_Name(&self, pRetVal: *mut *mut u16) -> HRESULT { + ((*self.vtable).get_Name)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn get_DeclaringType(&self, pRetVal: *mut *mut _Type) -> HRESULT { + ((*self.vtable).get_DeclaringType)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn get_ReflectedType(&self, pRetVal: *mut *mut _Type) -> HRESULT { + ((*self.vtable).get_ReflectedType)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn get_Guid(&self, pRetVal: *mut *mut GUID) -> HRESULT { + ((*self.vtable).get_Guid)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn get_Assembly(&self, pRetVal: *mut *mut _Assembly) -> HRESULT { + ((*self.vtable).get_Assembly)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn get_FullName(&self, pRetVal: *mut *mut u16) -> HRESULT { + ((*self.vtable).get_FullName)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn get_Namespace(&self, pRetVal: *mut *mut u16) -> HRESULT { + ((*self.vtable).get_Namespace)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn get_AssemblyQualifiedName(&self, pRetVal: *mut *mut u16) -> HRESULT { + ((*self.vtable).get_AssemblyQualifiedName)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn get_BaseType(&self, pRetVal: *mut *mut _Type) -> HRESULT { + ((*self.vtable).get_BaseType)(self as *const _ as *mut _, pRetVal) + } + + #[inline] + pub unsafe fn GetMethod_2( + &self, + name: *mut u16, + bindingAttr: BindingFlags, + pRetVal: *mut *mut _MethodInfo, + ) -> HRESULT { + ((*self.vtable).GetMethod_2)(self as *const _ as *mut _, name, bindingAttr, pRetVal) + } + + #[inline] + pub unsafe fn GetMethods( + &self, + bindingAttr: BindingFlags, + pRetVal: *mut *mut SAFEARRAY, + ) -> HRESULT { + ((*self.vtable).GetMethods)(self as *const _ as *mut _, bindingAttr, pRetVal) + } + + #[inline] + pub unsafe fn GetMethod_4( + &self, + name: *mut u16, + types: *mut SAFEARRAY, + modifiers: *mut SAFEARRAY, + pRetVal: *mut *mut _MethodInfo, + ) -> HRESULT { + ((*self.vtable).GetMethod_4)(self as *const _ as *mut _, name, types, modifiers, pRetVal) + } + + #[inline] + pub unsafe fn GetMethod_5( + &self, + name: *mut u16, + types: *mut SAFEARRAY, + pRetVal: *mut *mut _MethodInfo, + ) -> HRESULT { + ((*self.vtable).GetMethod_5)(self as *const _ as *mut _, name, types, pRetVal) + } + + #[inline] + pub unsafe fn GetMethod_6(&self, name: *mut u16, pRetVal: *mut *mut _MethodInfo) -> HRESULT { + ((*self.vtable).GetMethod_6)(self as *const _ as *mut _, name, pRetVal) + } + + #[inline] + pub unsafe fn GetMethods_2(&self, pRetVal: *mut *mut SAFEARRAY) -> HRESULT { + ((*self.vtable).GetMethods_2)(self as *const _ as *mut _, pRetVal) + } + + pub fn get_method(&self, name: &str) -> Result<*mut _MethodInfo, String> { + let mut dw = BSTR::from(name); + + let mut method_ptr: *mut _MethodInfo = ptr::null_mut(); + let hr = unsafe { (*self).GetMethod_6(dw.into_raw() as *mut _, &mut method_ptr) }; + + if hr.is_err() { + return Err(format!( + "Error while retrieving method `{}`: 0x{:x}", + name, hr.0 + )); + } + + if method_ptr.is_null() { + return Err(format!("Could not retrieve method `{}`", name)); + } + + Ok(method_ptr) + } + + pub fn get_methods(&self) -> Result, String> { + let mut results: Vec<*mut _MethodInfo> = vec![]; + + let mut safe_array_ptr: *mut SAFEARRAY = + unsafe { SafeArrayCreateVector(VT_UNKNOWN, 0, 255) }; + + let hr = unsafe { (*self).GetMethods_2(&mut safe_array_ptr) }; + + if hr.is_err() { + return Err(format!("Error while retrieving methods: 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 _MethodInfo = 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) + } +} + +impl Deref for _Type { + type Target = IUnknown; + + #[inline] + fn deref(&self) -> &IUnknown { + unsafe { &*(self as *const _Type as *const IUnknown) } + } +} +impl Interface for _Type { + const IID: GUID = GUID::from_values( + 0xbca8b44d, + 0xaad6, + 0x3a86, + [0x8a, 0xb7, 0x03, 0x34, 0x9f, 0x4f, 0x2d, 0xa2], + ); + + fn vtable(&self) -> *const c_void { + self.vtable as *const _ as *const c_void + } +} + +pub type BindingFlags = u32; +pub const BindingFlags_Default: BindingFlags = 0; +pub const BindingFlags_IgnoreCase: BindingFlags = 1; +pub const BindingFlags_DeclaredOnly: BindingFlags = 2; +pub const BindingFlags_Instance: BindingFlags = 4; +pub const BindingFlags_Static: BindingFlags = 8; +pub const BindingFlags_Public: BindingFlags = 16; +pub const BindingFlags_NonPublic: BindingFlags = 32; +pub const BindingFlags_FlattenHierarchy: BindingFlags = 64; +pub const BindingFlags_InvokeMethod: BindingFlags = 256; +pub const BindingFlags_CreateInstance: BindingFlags = 512; +pub const BindingFlags_GetField: BindingFlags = 1024; +pub const BindingFlags_SetField: BindingFlags = 2048; +pub const BindingFlags_GetProperty: BindingFlags = 4096; +pub const BindingFlags_SetProperty: BindingFlags = 8192; +pub const BindingFlags_PutDispProperty: BindingFlags = 16384; +pub const BindingFlags_PutRefDispProperty: BindingFlags = 32768; +pub const BindingFlags_ExactBinding: BindingFlags = 65536; +pub const BindingFlags_SuppressChangeType: BindingFlags = 131072; +pub const BindingFlags_OptionalParamBinding: BindingFlags = 262144; +pub const BindingFlags_IgnoreReturn: BindingFlags = 16777216; diff --git a/src/primitives/iunknown.rs b/src/primitives/iunknown.rs new file mode 100644 index 0000000..9efb466 --- /dev/null +++ b/src/primitives/iunknown.rs @@ -0,0 +1,67 @@ +use crate::primitives::{Interface, GUID, HRESULT}; +use std::{ffi::c_void, mem::transmute_copy}; + +#[repr(C)] +pub struct IUnknown { + pub vtable: *const IUnknownVtbl, +} + +#[repr(C)] +pub struct IUnknownVtbl { + pub QueryInterface: unsafe extern "system" fn( + this: *mut c_void, + iid: *const GUID, + interface: *mut *mut c_void, + ) -> HRESULT, + pub AddRef: unsafe extern "system" fn(this: *mut c_void) -> u32, + pub Release: unsafe extern "system" fn(this: *mut c_void) -> u32, +} + +impl IUnknown { + #[inline] + pub unsafe fn QueryInterface(&self, riid: *const GUID, ppvObject: *mut *mut c_void) -> HRESULT { + ((*self.vtable).QueryInterface)(self as *const _ as *mut _, riid, ppvObject) + } + + #[inline] + pub unsafe fn AddRef(&self) -> u32 { + ((*self.vtable).AddRef)(self as *const _ as *mut _) + } + + #[inline] + pub unsafe fn Release(&self) -> u32 { + ((*self.vtable).Release)(self as *const _ as *mut _) + } +} + +impl Interface for IUnknown { + const IID: GUID = GUID::from_u128(0x00000000_0000_0000_c000_000000000046); + + fn vtable(&self) -> *const c_void { + self.vtable as *const _ as *const c_void + } +} + +impl Clone for IUnknown { + fn clone(&self) -> Self { + unsafe { + self.AddRef(); + } + + unsafe { transmute_copy(self) } + } +} + +impl Drop for IUnknown { + fn drop(&mut self) { + unsafe { + self.Release(); + } + } +} + +impl std::fmt::Debug for IUnknown { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("IUnknown").field(&self).finish() + } +} diff --git a/src/primitives/mod.rs b/src/primitives/mod.rs new file mode 100644 index 0000000..6c3234b --- /dev/null +++ b/src/primitives/mod.rs @@ -0,0 +1,35 @@ +mod helpers; +mod iappdomain; +mod iassembly; +mod iclrmetahost; +mod iclrruntimeinfo; +mod icorruntimehost; +mod ienumunknown; +mod imethodinfo; +mod istringwriter; +mod itype; +mod iunknown; +mod types; + +pub use helpers::*; +pub use iappdomain::*; +pub use iassembly::*; +pub use iclrmetahost::*; +pub use iclrruntimeinfo::*; +pub use icorruntimehost::*; +pub use ienumunknown::*; +pub use imethodinfo::*; +pub use istringwriter::*; +pub use itype::*; +pub use iunknown::*; +pub use types::*; + +pub trait Interface: Sized { + const IID: GUID; + + fn vtable(&self) -> *const std::ffi::c_void; +} + +pub trait Class: Sized { + const CLSID: GUID; +} diff --git a/src/primitives/types.rs b/src/primitives/types.rs new file mode 100644 index 0000000..2ea51ab --- /dev/null +++ b/src/primitives/types.rs @@ -0,0 +1,4 @@ +pub use windows::{ + core::{GUID, HRESULT}, + Win32::Foundation::{BOOL, HANDLE, HINSTANCE}, +};