├── Cargo.toml ├── Image01.PNG ├── Image02.PNG ├── Readme.md └── src ├── appdomain.rs ├── assembly.rs ├── main.rs └── methodinfo.rs /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "clr_hosting" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | zeroize = "1.6.0" 8 | lazy_static = "1.4.0" 9 | 10 | [dependencies.windows-core] 11 | version = "0.51.0" 12 | features = [ 13 | "implement", 14 | ] 15 | 16 | [dependencies.windows] 17 | version = "0.51.0" 18 | features = [ 19 | "implement", 20 | "Win32_Foundation", 21 | "Win32_System_ClrHosting", 22 | "Win32_System_Memory", 23 | "Win32_System_Ole", 24 | "Win32_System_Variant", 25 | "Win32_System_Com", 26 | "Win32_System_LibraryLoader" 27 | ] 28 | -------------------------------------------------------------------------------- /Image01.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lap1nou/CLR_Heap_encryption/d8c4394cdbdbdf84e6718a29cb5694e37b97ed11/Image01.PNG -------------------------------------------------------------------------------- /Image02.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lap1nou/CLR_Heap_encryption/d8c4394cdbdbdf84e6718a29cb5694e37b97ed11/Image02.PNG -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # CLR Heap encryption 2 | This is a POC for a CLR sleep obfuscation attempt. It use `IHostMemoryManager` interface to control the memory allocated by the CLR. Turns out you can use both `ICorRuntimeHost` and `ICLRRuntimeHost` at the same time, so we can still use `ICorRuntimeHost` to run an assembly from memory while having all the benefits from `ICLRRuntimeHost`. 3 | 4 | Without CLR Heap encryption: 5 | 6 | ![](Image02.PNG) 7 | 8 | With: 9 | ![](Image01.PNG) 10 | 11 | Code is poorly written, this is just a POC for fun. 12 | 13 | ## References 14 | * https://github.com/yamakadi/clroxide 15 | * Konrad Kokosa, Pro .NET Memory Management 16 | * https://github.com/etormadiv/HostingCLR/tree/master/HostingCLR 17 | * https://github.com/HavocFramework/Havoc 18 | * https://www.mdsec.co.uk/2023/05/nighthawk-0-2-4-taking-out-the-trash/ 19 | * http://www.ahuwanya.net/blog/post/enumerating-appdomains-from-a-clr-host -------------------------------------------------------------------------------- /src/appdomain.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | ffi::{c_long, c_void}, 3 | io, 4 | ptr::null_mut, 5 | }; 6 | 7 | use windows::{ 8 | core::{IUnknown_Vtbl, BSTR, GUID, HRESULT}, 9 | Win32::System::Com::SAFEARRAY, 10 | }; 11 | 12 | use super::assembly::Assembly; 13 | 14 | #[repr(C)] 15 | pub struct AppDomain { 16 | pub vtable: *const AppDomainVtbl, 17 | } 18 | 19 | impl AppDomain { 20 | pub fn load_assembly(&self, safe_array: *mut SAFEARRAY) -> Result<*mut Assembly, String> { 21 | let mut assembly: *mut Assembly = null_mut(); 22 | let res = unsafe { 23 | ((*self.vtable).Load_3)(self as *const _ as *mut _, safe_array, &mut assembly) 24 | }; 25 | 26 | match res.0 { 27 | 0 => Ok(assembly), 28 | _ => Err(format!( 29 | "Couldn't load the assembly, {:?}", 30 | io::Error::last_os_error() 31 | )), 32 | } 33 | } 34 | 35 | pub fn load_library(&self, library: &str) -> Result<*mut Assembly, String> { 36 | let library_buffer = BSTR::from(library); 37 | 38 | let mut library_ptr: *mut Assembly = null_mut(); 39 | 40 | let hr = unsafe { 41 | ((*self.vtable).Load_2)( 42 | self as *const _ as *mut _, 43 | library_buffer.into_raw() as *mut _, 44 | &mut library_ptr, 45 | ) 46 | }; 47 | 48 | if hr.is_err() { 49 | return Err(format!("Could not retrieve `{}`: {:?}", library, hr)); 50 | } 51 | 52 | if library_ptr.is_null() { 53 | return Err(format!("Could not retrieve `{}`", library)); 54 | } 55 | 56 | Ok(library_ptr) 57 | } 58 | 59 | pub const IID: GUID = GUID::from_u128(0x05F696DC_2B29_3663_AD8B_C4389CF2A713); 60 | } 61 | 62 | #[repr(C)] 63 | #[allow(non_snake_case)] 64 | pub struct AppDomainVtbl { 65 | pub parent: IUnknown_Vtbl, 66 | pub GetTypeInfoCount: *const c_void, 67 | pub GetTypeInfo: *const c_void, 68 | pub GetIDsOfNames: *const c_void, 69 | pub Invoke: *const c_void, 70 | pub ToString: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, 71 | pub Equals: *const c_void, 72 | pub GetHashCode: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut c_long) -> HRESULT, 73 | pub GetType: *const c_void, 74 | pub InitializeLifetimeService: *const c_void, 75 | pub GetLifetimeService: *const c_void, 76 | pub get_Evidence: *const c_void, 77 | pub set_Evidence: *const c_void, 78 | pub get_DomainUnload: *const c_void, 79 | pub set_DomainUnload: *const c_void, 80 | pub get_AssemblyLoad: *const c_void, 81 | pub set_AssemblyLoad: *const c_void, 82 | pub get_ProcessExit: *const c_void, 83 | pub set_ProcessExit: *const c_void, 84 | pub get_TypeResolve: *const c_void, 85 | pub set_TypeResolve: *const c_void, 86 | pub get_ResourceResolve: *const c_void, 87 | pub set_ResourceResolve: *const c_void, 88 | pub get_AssemblyResolve: *const c_void, 89 | pub get_UnhandledException: *const c_void, 90 | pub set_UnhandledException: *const c_void, 91 | pub DefineDynamicAssembly: *const c_void, 92 | pub DefineDynamicAssembly_2: *const c_void, 93 | pub DefineDynamicAssembly_3: *const c_void, 94 | pub DefineDynamicAssembly_4: *const c_void, 95 | pub DefineDynamicAssembly_5: *const c_void, 96 | pub DefineDynamicAssembly_6: *const c_void, 97 | pub DefineDynamicAssembly_7: *const c_void, 98 | pub DefineDynamicAssembly_8: *const c_void, 99 | pub DefineDynamicAssembly_9: *const c_void, 100 | pub CreateInstance: *const c_void, 101 | pub CreateInstanceFrom: *const c_void, 102 | pub CreateInstance_2: *const c_void, 103 | pub CreateInstanceFrom_2: *const c_void, 104 | pub CreateInstance_3: *const c_void, 105 | pub CreateInstanceFrom_3: *const c_void, 106 | pub Load: *const c_void, 107 | pub Load_2: unsafe extern "system" fn( 108 | this: *mut c_void, 109 | assemblyString: *mut u16, 110 | pRetVal: *mut *mut Assembly, 111 | ) -> HRESULT, 112 | pub Load_3: unsafe extern "system" fn( 113 | this: *mut c_void, 114 | rawAssembly: *mut SAFEARRAY, 115 | pRetVal: *mut *mut Assembly, 116 | ) -> HRESULT, 117 | pub Load_4: *const c_void, 118 | pub Load_5: *const c_void, 119 | pub Load_6: *const c_void, 120 | pub Load_7: *const c_void, 121 | pub ExecuteAssembly: *const c_void, 122 | pub ExecuteAssembly_2: *const c_void, 123 | pub ExecuteAssembly_3: *const c_void, 124 | pub get_FriendlyName: *const c_void, 125 | pub get_BaseDirectory: *const c_void, 126 | pub get_RelativeSearchPath: *const c_void, 127 | pub get_ShadowCopyFiles: *const c_void, 128 | pub GetAssemblies: *const c_void, 129 | pub AppendPrivatePath: *const c_void, 130 | pub ClearPrivatePath: *const c_void, 131 | pub ClearShadowCopyPath: *const c_void, 132 | pub SetData: *const c_void, 133 | pub GetData: *const c_void, 134 | pub SetAppDomainPolicy: *const c_void, 135 | pub SetThreadPrincipal: *const c_void, 136 | pub SetPrincipalPolicy: *const c_void, 137 | pub DoCallBack: *const c_void, 138 | pub get_DynamicDirectory: *const c_void, 139 | } 140 | -------------------------------------------------------------------------------- /src/assembly.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | ffi::{c_long, c_void}, 3 | io, 4 | ptr::null_mut, 5 | }; 6 | 7 | use windows::{ 8 | core::{IUnknown_Vtbl, BSTR, HRESULT}, 9 | Win32::System::Com::SAFEARRAY, 10 | Win32::System::Variant::VARIANT, 11 | }; 12 | 13 | use crate::methodinfo::MethodInfo; 14 | 15 | #[repr(C)] 16 | pub struct Assembly { 17 | pub vtable: *const AssemblyVtbl, 18 | } 19 | 20 | impl Assembly { 21 | pub fn get_entrypoint(&self) -> Result<*mut MethodInfo, String> { 22 | let mut method_info: *mut MethodInfo = null_mut(); 23 | let res = unsafe { 24 | ((*self.vtable).get_EntryPoint)(self as *const _ as *mut _, &mut method_info) 25 | }; 26 | 27 | match res.0 { 28 | 0 => Ok(method_info), 29 | _ => Err(format!( 30 | "Couldn't find the entrypoint, {:?}", 31 | io::Error::last_os_error() 32 | )), 33 | } 34 | } 35 | 36 | //pub fn get_type(&self, name: &str) -> Result<*mut Type, String> { 37 | // let dw = BSTR::from(name); 38 | // 39 | // let mut type_ptr: *mut Type = null_mut(); 40 | // let hr = unsafe { 41 | // ((*self.vtable).GetType_2)( 42 | // self as *const _ as *mut _, 43 | // dw.into_raw() as *mut _, 44 | // &mut type_ptr, 45 | // ) 46 | // }; 47 | // 48 | // if hr.is_err() { 49 | // return Err(format!( 50 | // "Error while retrieving type `{}`: 0x{:x}", 51 | // name, hr.0 52 | // )); 53 | // } 54 | // 55 | // if type_ptr.is_null() { 56 | // return Err(format!("Could not retrieve type `{}`", name)); 57 | // } 58 | // 59 | // Ok(type_ptr) 60 | //} 61 | 62 | pub fn create_instance(&self, name: &str) -> Result { 63 | let dw = BSTR::from(name); 64 | 65 | let mut instance: VARIANT = VARIANT::default(); 66 | let hr = unsafe { 67 | ((*self.vtable).CreateInstance)( 68 | self as *const _ as *mut _, 69 | dw.into_raw() as *mut _, 70 | &mut instance, 71 | ) 72 | }; 73 | 74 | if hr.is_err() { 75 | return Err(format!( 76 | "Error while creating instance of `{}`: 0x{:x}", 77 | name, hr.0 78 | )); 79 | } 80 | 81 | Ok(instance) 82 | } 83 | 84 | //pub fn get_types(&self) -> Result, String> { 85 | // let mut results: Vec<*mut Type> = vec![]; 86 | // 87 | // let mut safe_array_ptr: *mut SAFEARRAY = unsafe { SafeArrayCreateVector(VT_UNKNOWN, 0, 0) }; 88 | // 89 | // let hr = unsafe { 90 | // ((*((*self).vtable)).GetTypes)(&self as *const _ as *mut _, &mut safe_array_ptr) 91 | // }; 92 | // 93 | // if hr.is_err() { 94 | // return Err(format!("Error while retrieving types: 0x{:x}", hr.0)); 95 | // } 96 | // 97 | // let ubound = unsafe { SafeArrayGetUBound(safe_array_ptr, 1) }.unwrap_or(0); 98 | // 99 | // for i in 0..ubound { 100 | // let indices: [i32; 1] = [i as _]; 101 | // let mut variant: *mut Type = null_mut(); 102 | // let pv = &mut variant as *mut _ as *mut c_void; 103 | // 104 | // match unsafe { SafeArrayGetElement(safe_array_ptr, indices.as_ptr(), pv) } { 105 | // Ok(_) => {} 106 | // Err(e) => return Err(format!("Could not access safe array: {:?}", e.code())), 107 | // } 108 | // 109 | // unsafe { dbg!((*variant).to_string().unwrap()) }; 110 | // 111 | // if !pv.is_null() { 112 | // results.push(variant) 113 | // } 114 | // } 115 | // 116 | // Ok(results) 117 | //} 118 | } 119 | 120 | #[repr(C)] 121 | #[allow(non_snake_case)] 122 | pub struct AssemblyVtbl { 123 | pub parent: IUnknown_Vtbl, 124 | pub GetTypeInfoCount: *const c_void, 125 | pub GetTypeInfo: *const c_void, 126 | pub GetIDsOfNames: *const c_void, 127 | pub Invoke: *const c_void, 128 | pub ToString: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, 129 | pub Equals: *const c_void, 130 | pub GetHashCode: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut c_long) -> HRESULT, 131 | pub GetType: *const c_void, 132 | pub get_CodeBase: 133 | unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, 134 | pub get_EscapedCodeBase: 135 | unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, 136 | pub GetName: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut c_void) -> HRESULT, 137 | pub GetName_2: *const c_void, 138 | pub get_FullName: 139 | unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, 140 | pub get_EntryPoint: 141 | unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut MethodInfo) -> HRESULT, 142 | pub GetType_2: unsafe extern "system" fn( 143 | this: *mut c_void, 144 | name: *mut u16, 145 | pRetVal: *mut *mut c_void, 146 | ) -> HRESULT, 147 | pub GetType_3: *const c_void, 148 | pub GetExportedTypes: *const c_void, 149 | pub GetTypes: 150 | unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut SAFEARRAY) -> HRESULT, 151 | pub GetManifestResourceStream: *const c_void, 152 | pub GetManifestResourceStream_2: *const c_void, 153 | pub GetFile: *const c_void, 154 | pub GetFiles: *const c_void, 155 | pub GetFiles_2: *const c_void, 156 | pub GetManifestResourceNames: *const c_void, 157 | pub GetManifestResourceInfo: *const c_void, 158 | pub get_Location: 159 | unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, 160 | pub get_Evidence: *const c_void, 161 | pub GetCustomAttributes: *const c_void, 162 | pub GetCustomAttributes_2: *const c_void, 163 | pub IsDefined: *const c_void, 164 | pub GetObjectData: *const c_void, 165 | pub add_ModuleResolve: *const c_void, 166 | pub remove_ModuleResolve: *const c_void, 167 | pub GetType_4: *const c_void, 168 | pub GetSatelliteAssembly: *const c_void, 169 | pub GetSatelliteAssembly_2: *const c_void, 170 | pub LoadModule: *const c_void, 171 | pub LoadModule_2: *const c_void, 172 | pub CreateInstance: unsafe extern "system" fn( 173 | this: *mut c_void, 174 | typeName: *mut u16, 175 | pRetVal: *mut VARIANT, 176 | ) -> HRESULT, 177 | pub CreateInstance_2: *const c_void, 178 | pub CreateInstance_3: *const c_void, 179 | pub GetLoadedModules: *const c_void, 180 | pub GetLoadedModules_2: *const c_void, 181 | pub GetModules: *const c_void, 182 | pub GetModules_2: *const c_void, 183 | pub GetModule: *const c_void, 184 | pub GetReferencedAssemblies: *const c_void, 185 | pub get_GlobalAssemblyCache: *const c_void, 186 | } 187 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use crate::appdomain::AppDomain; 2 | use lazy_static::lazy_static; 3 | use std::ffi::{c_int, c_void}; 4 | use std::mem::{self, ManuallyDrop}; 5 | use std::ptr::{addr_of, addr_of_mut, null_mut}; 6 | use std::sync::Mutex; 7 | use std::{env, fs, io, ptr}; 8 | use windows::core::implement; 9 | use windows::Win32::Foundation::{E_NOINTERFACE, E_OUTOFMEMORY, HANDLE, S_OK}; 10 | use windows::Win32::System::ClrHosting::{ 11 | CLRCreateInstance, CLRRuntimeHost, CLSID_CLRMetaHost, CorRuntimeHost, EMemoryCriticalLevel, 12 | ICLRMemoryNotificationCallback, ICLRMetaHost, ICLRRuntimeHost, ICLRRuntimeInfo, 13 | ICorRuntimeHost, IHostControl, IHostControl_Impl, IHostMalloc, IHostMalloc_Impl, 14 | IHostMemoryManager, IHostMemoryManager_Impl, MALLOC_EXECUTABLE, MALLOC_THREADSAFE, MALLOC_TYPE, 15 | }; 16 | use windows::Win32::System::Com::SAFEARRAY; 17 | use windows::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryA}; 18 | use windows::Win32::System::Memory::{ 19 | HeapAlloc, HeapCreate, HeapFree, VirtualAlloc, VirtualFree, VirtualProtect, VirtualQuery, 20 | HEAP_CREATE_ENABLE_EXECUTE, HEAP_FLAGS, HEAP_NO_SERIALIZE, MEMORY_BASIC_INFORMATION, 21 | MEM_RESERVE, PAGE_PROTECTION_FLAGS, VIRTUAL_ALLOCATION_TYPE, VIRTUAL_FREE_TYPE, 22 | }; 23 | use windows::Win32::System::Ole::{SafeArrayCreateVector, SafeArrayDestroy, SafeArrayPutElement}; 24 | use windows::Win32::System::Variant::{ 25 | VARENUM, VARIANT, VARIANT_0, VARIANT_0_0, VARIANT_0_0_0, VT_ARRAY, VT_BSTR, VT_UI1, VT_VARIANT, 26 | }; 27 | use windows_core::{w, ComInterface, IUnknown, Interface, BSTR, PCSTR}; 28 | use zeroize::Zeroize; 29 | 30 | mod appdomain; 31 | mod assembly; 32 | mod methodinfo; 33 | 34 | #[repr(C)] 35 | pub struct UString { 36 | pub length: u32, 37 | pub maximum_length: u32, 38 | pub buffer: *mut c_void, 39 | } 40 | 41 | type FnSystemFunction032 = unsafe extern "system" fn(*const UString, *const UString) -> c_int; 42 | 43 | #[implement(IHostControl)] 44 | pub struct MyHostControl; 45 | 46 | impl IHostControl_Impl for MyHostControl { 47 | fn GetHostManager( 48 | &self, 49 | _riid: *const ::windows_core::GUID, 50 | _ppobject: *mut *mut ::core::ffi::c_void, 51 | ) -> ::windows_core::Result<()> { 52 | if unsafe { _riid.as_ref().unwrap() } == &IHostMemoryManager::IID { 53 | let tmp = MyHostMemoryManager {}; 54 | let memory_manager: IHostMemoryManager = tmp.into(); 55 | unsafe { 56 | memory_manager 57 | .query(&*_riid, _ppobject as *mut *const c_void) 58 | .ok()? 59 | }; 60 | 61 | S_OK.ok() 62 | } else { 63 | unsafe { *_ppobject = null_mut() }; 64 | E_NOINTERFACE.ok() 65 | } 66 | } 67 | 68 | fn SetAppDomainManager( 69 | &self, 70 | _dwappdomainid: u32, 71 | _punkappdomainmanager: ::core::option::Option<&::windows_core::IUnknown>, 72 | ) -> ::windows_core::Result<()> { 73 | Ok(()) 74 | } 75 | } 76 | 77 | #[implement(IHostMemoryManager)] 78 | pub struct MyHostMemoryManager; 79 | 80 | impl IHostMemoryManager_Impl for MyHostMemoryManager { 81 | fn CreateMalloc(&self, dwmalloctype: u32) -> ::windows_core::Result { 82 | let my_host_malloc = MyHostMalloc { 83 | m_hMallocHeap: unsafe { HeapCreate(HEAP_NO_SERIALIZE, 0, 0).unwrap() }, 84 | }; 85 | let mut tmp1: IHostMalloc = my_host_malloc.into(); 86 | 87 | Ok(tmp1) 88 | } 89 | 90 | fn VirtualAlloc( 91 | &self, 92 | paddress: *const ::core::ffi::c_void, 93 | dwsize: usize, 94 | flallocationtype: u32, 95 | flprotect: u32, 96 | ecriticallevel: EMemoryCriticalLevel, 97 | ppmem: *mut *mut ::core::ffi::c_void, 98 | ) -> ::windows_core::Result<()> { 99 | unsafe { 100 | *ppmem = *ManuallyDrop::new(VirtualAlloc( 101 | Some(paddress), 102 | dwsize, 103 | VIRTUAL_ALLOCATION_TYPE(flallocationtype), 104 | PAGE_PROTECTION_FLAGS(flprotect), 105 | )) 106 | }; 107 | 108 | if flallocationtype == 8192 && dwsize > 65536 { 109 | HASHMAP 110 | .lock() 111 | .unwrap() 112 | .push((unsafe { *ppmem } as u64, dwsize)); 113 | } 114 | 115 | S_OK.ok() 116 | } 117 | 118 | fn VirtualFree( 119 | &self, 120 | lpaddress: *const ::core::ffi::c_void, 121 | dwsize: usize, 122 | dwfreetype: u32, 123 | ) -> ::windows_core::Result<()> { 124 | unsafe { 125 | VirtualFree( 126 | lpaddress as *mut c_void, 127 | dwsize, 128 | VIRTUAL_FREE_TYPE(dwfreetype), 129 | )?; 130 | }; 131 | 132 | S_OK.ok() 133 | } 134 | 135 | fn VirtualQuery( 136 | &self, 137 | lpaddress: *const ::core::ffi::c_void, 138 | lpbuffer: *mut ::core::ffi::c_void, 139 | dwlength: usize, 140 | presult: *mut usize, 141 | ) -> ::windows_core::Result<()> { 142 | unsafe { 143 | *presult = VirtualQuery( 144 | Some(lpaddress), 145 | lpbuffer as *mut _ as *mut MEMORY_BASIC_INFORMATION, 146 | dwlength, 147 | ); 148 | }; 149 | 150 | S_OK.ok() 151 | } 152 | 153 | fn VirtualProtect( 154 | &self, 155 | lpaddress: *const ::core::ffi::c_void, 156 | dwsize: usize, 157 | flnewprotect: u32, 158 | ) -> ::windows_core::Result { 159 | if flnewprotect == 0 { 160 | return Ok(S_OK.0 as u32); 161 | } 162 | 163 | let mut old = PAGE_PROTECTION_FLAGS(0); 164 | unsafe { 165 | VirtualProtect( 166 | lpaddress, 167 | dwsize, 168 | PAGE_PROTECTION_FLAGS(flnewprotect), 169 | &mut old, 170 | )?; 171 | }; 172 | 173 | Ok(S_OK.0 as u32) 174 | } 175 | 176 | fn GetMemoryLoad( 177 | &self, 178 | pmemoryload: *mut u32, 179 | pavailablebytes: *mut usize, 180 | ) -> ::windows_core::Result<()> { 181 | unsafe { 182 | *pmemoryload = 30; 183 | *pavailablebytes = 100 * 1024 * 1024; 184 | }; 185 | 186 | S_OK.ok() 187 | } 188 | 189 | fn RegisterMemoryNotificationCallback( 190 | &self, 191 | pcallback: ::core::option::Option<&ICLRMemoryNotificationCallback>, 192 | ) -> ::windows_core::Result<()> { 193 | S_OK.ok() 194 | } 195 | 196 | fn NeedsVirtualAddressSpace( 197 | &self, 198 | startaddress: *const ::core::ffi::c_void, 199 | size: usize, 200 | ) -> ::windows_core::Result<()> { 201 | S_OK.ok() 202 | } 203 | 204 | fn AcquiredVirtualAddressSpace( 205 | &self, 206 | startaddress: *const ::core::ffi::c_void, 207 | size: usize, 208 | ) -> ::windows_core::Result<()> { 209 | S_OK.ok() 210 | } 211 | 212 | fn ReleasedVirtualAddressSpace( 213 | &self, 214 | startaddress: *const ::core::ffi::c_void, 215 | ) -> ::windows_core::Result<()> { 216 | S_OK.ok() 217 | } 218 | } 219 | 220 | #[implement(IHostMalloc)] 221 | pub struct MyHostMalloc { 222 | pub m_hMallocHeap: HANDLE, 223 | } 224 | 225 | impl IHostMalloc_Impl for MyHostMalloc { 226 | fn Alloc( 227 | &self, 228 | cbsize: usize, 229 | ecriticallevel: EMemoryCriticalLevel, 230 | ppmem: *mut *mut ::core::ffi::c_void, 231 | ) -> ::windows_core::Result<()> { 232 | unsafe { *ppmem = HeapAlloc(self.m_hMallocHeap, HEAP_FLAGS(1 | 4), cbsize) }; 233 | 234 | if (unsafe { *ppmem }).is_null() { 235 | return E_OUTOFMEMORY.ok(); 236 | } 237 | 238 | S_OK.ok() 239 | } 240 | 241 | fn DebugAlloc( 242 | &self, 243 | cbsize: usize, 244 | ecriticallevel: EMemoryCriticalLevel, 245 | pszfilename: *const u8, 246 | ilineno: i32, 247 | ppmem: *mut *mut ::core::ffi::c_void, 248 | ) -> ::windows_core::Result<()> { 249 | self.Alloc(cbsize, ecriticallevel, ppmem) 250 | } 251 | 252 | fn Free(&self, pmem: *const ::core::ffi::c_void) -> ::windows_core::Result<()> { 253 | unsafe { 254 | HeapFree( 255 | self.m_hMallocHeap, 256 | windows::Win32::System::Memory::HEAP_FLAGS(0), 257 | Some(pmem), 258 | )? 259 | }; 260 | 261 | S_OK.ok() 262 | } 263 | } 264 | 265 | lazy_static! { 266 | static ref HASHMAP: Mutex> = Mutex::new(vec![]); 267 | } 268 | 269 | fn main() -> windows::core::Result<()> { 270 | let mut args: Vec = env::args().collect(); 271 | let mut assembly_contents = fs::read(args[1].clone()).expect("Unable to read file"); 272 | 273 | let mut arguments: Vec = vec![]; 274 | arguments = args.split_off(2); 275 | 276 | unsafe { 277 | let metahost: ICLRMetaHost = CLRCreateInstance(&CLSID_CLRMetaHost)?; 278 | let runtime: ICLRRuntimeInfo = metahost.GetRuntime(w!("v4.0.30319"))?; 279 | let runtimehost: ICLRRuntimeHost = runtime.GetInterface(&CLRRuntimeHost)?; 280 | 281 | let mut tmp = MyHostControl {}; 282 | let mut control: IHostControl = tmp.into(); 283 | runtimehost.SetHostControl(&control).unwrap(); 284 | runtimehost.Start()?; 285 | 286 | let runtimehost2: ICorRuntimeHost = runtime.GetInterface(&CorRuntimeHost)?; 287 | 288 | let default_domain: IUnknown = runtimehost2 289 | .GetDefaultDomain() 290 | .map_err(|e| format!("{}", e)) 291 | .unwrap(); 292 | 293 | let mut app_domain: *mut AppDomain = null_mut(); 294 | 295 | let tmp = &**(default_domain.as_raw() 296 | as *mut *mut ::Vtable); 297 | let _res = (tmp.QueryInterface)( 298 | default_domain.as_raw(), 299 | &AppDomain::IID, 300 | &mut app_domain as *mut *mut _ as *mut *const c_void, 301 | ); 302 | 303 | let safe_array = create_assembly_safearray(&mut assembly_contents).unwrap(); 304 | let safe_array_final = create_final_array(arguments).unwrap(); 305 | let assembly = (*app_domain).load_assembly(safe_array).unwrap(); 306 | let method_info = (*assembly).get_entrypoint().unwrap(); 307 | 308 | (*method_info).invoke_assembly(safe_array_final).unwrap(); 309 | 310 | unsafe { SafeArrayDestroy(safe_array) }; 311 | unsafe { SafeArrayDestroy(safe_array_final) }; 312 | } 313 | 314 | let test1 = HASHMAP.lock().unwrap()[2].0; 315 | let test2 = HASHMAP.lock().unwrap()[2].1; 316 | 317 | assembly_contents.zeroize(); 318 | 319 | unsafe { encrypt_heap(test1, test2) }; 320 | 321 | // Pause 322 | let mut buf = String::new(); 323 | std::io::stdin().read_line(&mut buf).unwrap(); 324 | 325 | Ok(()) 326 | } 327 | 328 | unsafe fn encrypt_heap(heap: u64, heap_size: usize) { 329 | let mut array: Vec = vec![]; 330 | let mut start_address = heap as *const c_void; 331 | let mut i = 0; 332 | 333 | while (start_address as u64) < (heap + heap_size as u64) { 334 | array.push(unsafe { mem::zeroed() }); 335 | VirtualQuery(Some(start_address), &mut array[i], 48); 336 | start_address = start_address.add(array.last().unwrap().RegionSize); 337 | i = i + 1; 338 | } 339 | 340 | let mut key_not: [i8; 16] = [ 341 | 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 342 | 0x66, 343 | ]; 344 | 345 | let mut key = UString { 346 | length: key_not.len() as u32, 347 | maximum_length: key_not.len() as u32, 348 | buffer: key_not.as_mut_ptr() as *mut c_void, 349 | }; 350 | 351 | let system_function032 = get_function_from_dll("Advapi32\0", "SystemFunction032\0").unwrap(); 352 | let system_function032_fn: FnSystemFunction032 = unsafe { mem::transmute(system_function032) }; 353 | 354 | for region in array { 355 | if region.State == MEM_RESERVE { 356 | continue; 357 | } 358 | 359 | let mut data = UString { 360 | length: region.RegionSize as u32, 361 | maximum_length: region.RegionSize as u32, 362 | buffer: region.BaseAddress, 363 | }; 364 | 365 | system_function032_fn(&mut data as *mut _, &mut key as *mut _); 366 | } 367 | } 368 | 369 | pub unsafe fn get_function_from_dll( 370 | dll_name: &str, 371 | function_name: &str, 372 | ) -> Result { 373 | let dll_handle = LoadLibraryA(PCSTR(String::from(dll_name).as_ptr())).unwrap(); 374 | 375 | let func_adress = GetProcAddress(dll_handle, PCSTR(String::from(function_name).as_ptr())); 376 | match func_adress { 377 | None => Err(io::Error::last_os_error()), 378 | _ => Ok(func_adress.unwrap() as usize), 379 | } 380 | } 381 | 382 | fn create_assembly_safearray( 383 | assembly_contents: &mut Vec, 384 | ) -> core::result::Result<*mut SAFEARRAY, String> { 385 | let safe_array = unsafe { SafeArrayCreateVector(VT_UI1, 0, assembly_contents.len() as u32) }; 386 | if safe_array.is_null() { 387 | return Err("SafeArrayCreate() got an error !".to_string()); 388 | } 389 | 390 | unsafe { 391 | unsafe { 392 | ptr::copy_nonoverlapping( 393 | assembly_contents.as_ptr(), 394 | (*safe_array).pvData.cast(), 395 | assembly_contents.len(), 396 | ) 397 | }; 398 | }; 399 | 400 | Ok(safe_array) 401 | } 402 | 403 | fn create_final_array(arguments: Vec) -> Result<*mut SAFEARRAY, String> { 404 | let safe_array_args = unsafe { SafeArrayCreateVector(VT_BSTR, 0, arguments.len() as u32) }; 405 | if safe_array_args.is_null() { 406 | return Err("SafeArrayCreate() got an error !".to_string()); 407 | } 408 | 409 | for (i, args) in arguments.iter().enumerate() { 410 | let res = unsafe { 411 | SafeArrayPutElement( 412 | safe_array_args, 413 | addr_of!(i) as *const i32, 414 | BSTR::from(args).into_raw() as *const c_void, 415 | ) 416 | }; 417 | 418 | match res { 419 | Err(e) => { 420 | return Err(format!("SafeArrayPutElement() error: {}", e)); 421 | } 422 | Ok(_) => {} 423 | } 424 | } 425 | 426 | let mut args_variant = VARIANT { 427 | Anonymous: VARIANT_0 { 428 | Anonymous: ManuallyDrop::new(VARIANT_0_0 { 429 | vt: VARENUM(VT_BSTR.0 | VT_ARRAY.0), 430 | wReserved1: 0, 431 | wReserved2: 0, 432 | wReserved3: 0, 433 | Anonymous: VARIANT_0_0_0 { 434 | parray: safe_array_args, 435 | }, 436 | }), 437 | }, 438 | }; 439 | 440 | let safe_array_final = unsafe { SafeArrayCreateVector(VT_VARIANT, 0, 1_u32) }; 441 | if safe_array_final.is_null() { 442 | return Err("SafeArrayCreate() got an error !".to_string()); 443 | } 444 | 445 | let idx = 0; 446 | let res = unsafe { 447 | SafeArrayPutElement( 448 | safe_array_final, 449 | addr_of!(idx), 450 | addr_of_mut!(args_variant) as *const c_void, 451 | ) 452 | }; 453 | 454 | match res { 455 | Err(e) => Err(format!("SafeArrayPutElement() error: {}", e)), 456 | Ok(_) => Ok(safe_array_final), 457 | } 458 | } 459 | -------------------------------------------------------------------------------- /src/methodinfo.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | ffi::{c_long, c_void}, 3 | io, 4 | }; 5 | 6 | use windows::{ 7 | core::{IUnknown_Vtbl, BSTR, HRESULT}, 8 | Win32::System::{ 9 | Com::SAFEARRAY, 10 | Ole::{SafeArrayCreateVector, SafeArrayGetLBound, SafeArrayGetUBound}, 11 | Variant::{VARIANT, VT_UNKNOWN, VT_VARIANT}, 12 | }, 13 | }; 14 | 15 | #[derive(Debug, Copy, Clone)] 16 | #[repr(C)] 17 | pub struct MethodInfo { 18 | pub vtable: *const MethodInfoVtbl, 19 | } 20 | 21 | impl MethodInfo { 22 | pub fn invoke_assembly(&self, safe_array_final: *mut SAFEARRAY) -> Result<(), String> { 23 | let object: VARIANT = unsafe { std::mem::zeroed() }; 24 | let mut return_value: VARIANT = unsafe { std::mem::zeroed() }; 25 | 26 | let res = unsafe { 27 | ((*self.vtable).Invoke_3)( 28 | self as *const _ as *mut _, 29 | object, 30 | safe_array_final, 31 | &mut return_value, 32 | ) 33 | }; 34 | 35 | match res.0 { 36 | 0 => Ok(()), 37 | _ => Err(format!( 38 | "Couldn't invoke the assembly, {:?}", 39 | io::Error::last_os_error() 40 | )), 41 | } 42 | } 43 | 44 | pub unsafe fn invoke_without_args(&self, instance: Option) -> Result { 45 | let method_args = unsafe { SafeArrayCreateVector(VT_VARIANT, 0, 0) }; 46 | 47 | self.invoke(method_args, instance) 48 | } 49 | 50 | pub unsafe fn invoke( 51 | &self, 52 | args: *mut SAFEARRAY, 53 | instance: Option, 54 | ) -> Result { 55 | let args_len = get_array_length(args); 56 | let parameter_count = (*self).get_parameter_count()?; 57 | 58 | if args_len != parameter_count { 59 | return Err(format!( 60 | "Arguments do not match method signature: {} given, {} expected", 61 | args_len, parameter_count 62 | )); 63 | } 64 | 65 | let mut return_value: VARIANT = unsafe { std::mem::zeroed() }; 66 | 67 | let object: VARIANT = match instance { 68 | None => unsafe { std::mem::zeroed() }, 69 | Some(i) => i, 70 | }; 71 | 72 | let hr = unsafe { 73 | ((*self.vtable).Invoke_3)(self as *const _ as *mut _, object, args, &mut return_value) 74 | }; 75 | 76 | if hr.is_err() { 77 | return Err(format!("Could not invoke method: {:?}", hr)); 78 | } 79 | 80 | Ok(return_value) 81 | } 82 | 83 | pub fn get_parameter_count(&self) -> Result { 84 | let mut safe_array_ptr: *mut SAFEARRAY = 85 | unsafe { SafeArrayCreateVector(VT_UNKNOWN, 0, 255) }; 86 | 87 | let hr = unsafe { 88 | ((*self.vtable).GetParameters)(self as *const _ as *mut _, &mut safe_array_ptr) 89 | }; 90 | 91 | if hr.is_err() { 92 | return Err(format!("Could not get parameter count: {:?}", hr)); 93 | } 94 | 95 | Ok(get_array_length(safe_array_ptr)) 96 | } 97 | 98 | pub fn to_string(&self) -> Result { 99 | let mut buffer = BSTR::new(); 100 | 101 | let hr = unsafe { (*self).ToString(&mut buffer as *mut _ as *mut *mut u16) }; 102 | 103 | if hr.is_err() { 104 | return Err(format!("Failed while running `ToString`: {:?}", hr)); 105 | } 106 | 107 | Ok(buffer.to_string()) 108 | } 109 | 110 | #[inline] 111 | pub unsafe fn ToString(&self, pRetVal: *mut *mut u16) -> HRESULT { 112 | ((*self.vtable).ToString)(self as *const _ as *mut _, pRetVal) 113 | } 114 | } 115 | 116 | #[repr(C)] 117 | #[allow(non_snake_case)] 118 | pub struct MethodInfoVtbl { 119 | pub parent: IUnknown_Vtbl, 120 | pub GetTypeInfoCount: *const c_void, 121 | pub GetTypeInfo: *const c_void, 122 | pub GetIDsOfNames: *const c_void, 123 | pub Invoke: *const c_void, 124 | pub ToString: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, 125 | pub Equals: *const c_void, 126 | pub GetHashCode: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut c_long) -> HRESULT, 127 | pub GetType: *const c_void, 128 | pub get_MemberType: *const c_void, 129 | pub get_name: unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut u16) -> HRESULT, 130 | pub get_DeclaringType: *const c_void, 131 | pub get_ReflectedType: *const c_void, 132 | pub GetCustomAttributes: *const c_void, 133 | pub GetCustomAttributes_2: *const c_void, 134 | pub IsDefined: *const c_void, 135 | pub GetParameters: 136 | unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut SAFEARRAY) -> HRESULT, 137 | pub GetMethodImplementationFlags: *const c_void, 138 | pub get_MethodHandle: *const c_void, 139 | pub get_Attributes: *const c_void, 140 | pub get_CallingConvention: *const c_void, 141 | pub Invoke_2: *const c_void, 142 | pub get_IsPublic: *const c_void, 143 | pub get_IsPrivate: *const c_void, 144 | pub get_IsFamily: *const c_void, 145 | pub get_IsAssembly: *const c_void, 146 | pub get_IsFamilyAndAssembly: *const c_void, 147 | pub get_IsFamilyOrAssembly: *const c_void, 148 | pub get_IsStatic: *const c_void, 149 | pub get_IsFinal: *const c_void, 150 | pub get_IsVirtual: *const c_void, 151 | pub get_IsHideBySig: *const c_void, 152 | pub get_IsAbstract: *const c_void, 153 | pub get_IsSpecialName: *const c_void, 154 | pub get_IsConstructor: *const c_void, 155 | pub Invoke_3: unsafe extern "system" fn( 156 | this: *mut c_void, 157 | obj: VARIANT, 158 | parameters: *mut SAFEARRAY, 159 | pRetVal: *mut VARIANT, 160 | ) -> HRESULT, 161 | pub get_returnType: *const c_void, 162 | pub get_ReturnTypeCustomAttributes: *const c_void, 163 | pub GetBaseDefinition: 164 | unsafe extern "system" fn(this: *mut c_void, pRetVal: *mut *mut MethodInfo) -> HRESULT, 165 | } 166 | 167 | pub fn get_array_length(array_ptr: *mut SAFEARRAY) -> i32 { 168 | let upper = unsafe { SafeArrayGetUBound(array_ptr, 1) }.unwrap_or(0); 169 | let lower = unsafe { SafeArrayGetLBound(array_ptr, 1) }.unwrap_or(0); 170 | 171 | match upper - lower { 172 | 0 => 0, 173 | delta => delta + 1, 174 | } 175 | } 176 | --------------------------------------------------------------------------------