allow creating and using a custom app domain

This commit is contained in:
Kadir Yamamoto
2023-03-24 15:43:22 +09:00
parent e024248be0
commit 11061d8da3
8 changed files with 140 additions and 0 deletions
+22
View File
@@ -85,6 +85,28 @@ fn prepare_args() -> (String, Vec<String>) {
}
```
### Use a custom app domain
<img width="563" alt="assembly_arch" src="./docs/images/using_custom_app_domain.png">
You can update the context to use a custom app domain. This can be useful if you want to avoid `DefaultDomain`. Check out [`examples/custom_app_domain.rs`](examples/custom_app_domain.rs) for more details.
```rust
...
let app_domain = clr.using_runtime_host(|host| {
let app_domain = unsafe { (*host).create_domain("CustomDomain")? };
Ok(app_domain)
})?;
clr.use_app_domain(app_domain)?;
...
```
### Use a custom loader for `mscoree.dll`
We need to load the `CreateInterface` function from `mscoree.dll` to kickstart the CLR. You can provide a custom loader by disabling default features.
Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

+45
View File
@@ -0,0 +1,45 @@
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 clr = Clr::new(contents, args)?;
let app_domain = clr.using_runtime_host(|host| {
let app_domain = unsafe { (*host).create_domain("CustomDomain")? };
Ok(app_domain)
})?;
clr.use_app_domain(app_domain)?;
let results = clr.run()?;
println!("[*] Results:\n\n{}", results);
Ok(())
}
fn prepare_args() -> (String, Vec<String>) {
let mut args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Please provide a path to a dotnet executable");
exit(1)
}
let mut command_args: Vec<String> = 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);
}
+22
View File
@@ -86,6 +86,28 @@ impl Clr {
})
}
pub fn using_runtime_host<T>(
&mut self,
callback: fn(*mut ICorRuntimeHost) -> Result<T, String>,
) -> Result<T, String> {
let context = self.get_context()?;
let runtime_host = context.runtime_host;
callback(runtime_host)
}
pub fn use_app_domain(&mut self, app_domain: *mut _AppDomain) -> Result<(), String> {
if self.context.is_none() {
return Err("CLR Context has not been initialized".into());
}
let context = self.context.as_mut().unwrap();
context.app_domain = app_domain;
Ok(())
}
pub fn run(&mut self) -> Result<String, String> {
self.redirect_output()?;
+48
View File
@@ -1,4 +1,5 @@
use std::{ffi::c_void, ops::Deref, ptr};
use windows::core::BSTR;
use crate::primitives::{
Class, IUnknown, IUnknownVtbl, Interface, _AppDomain, GUID, HANDLE, HINSTANCE, HRESULT,
@@ -116,6 +117,53 @@ impl ICorRuntimeHost {
Ok(app_domain)
}
pub fn create_domain(&self, domain: &str) -> Result<*mut _AppDomain, String> {
let domain_name = BSTR::from(domain);
let mut unknown_array: *mut IUnknown = ptr::null_mut();
let mut unknown: *mut IUnknown = ptr::null_mut();
let hr = unsafe {
(*self).CreateDomain(
domain_name.into_raw() as *const _ as *const u16,
unknown_array,
&mut unknown,
)
};
if hr.is_err() {
return Err(format!(
"Could not create app domain `{}`: {:?}",
domain, hr
));
}
if unknown.is_null() {
return Err(format!("Could not create app domain `{}`", domain));
}
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 create app domain `{}`: {:?}",
domain, hr
));
}
if app_domain.is_null() {
return Err(format!("Could not create app domain `{}`", domain));
}
Ok(app_domain)
}
#[inline]
pub unsafe fn CreateLogicalThreadState(&self) -> HRESULT {
((*self.vtable).CreateLogicalThreadState)(self as *const _ as *mut _)
+1
View File
@@ -9,6 +9,7 @@ namespace WithArgs
public static void Main(string[] args)
{
Console.WriteLine($"[*] Hello World from `{Assembly.GetExecutingAssembly().FullName}`!");
Console.WriteLine($"[*] I am running in `{AppDomain.CurrentDomain}`!");
Console.WriteLine($"[*] I was given {args.Length} arguments");
foreach (var s in args)
+1
View File
@@ -8,6 +8,7 @@ namespace WithExit
public static void Main(string[] args)
{
Console.WriteLine($"[*] Hello World from `{Assembly.GetExecutingAssembly().FullName}`!");
Console.WriteLine($"[*] I am running in `{AppDomain.CurrentDomain}`!");
Console.WriteLine("[*] I very much hope to live past `System.Environment.Exit`!");
Console.WriteLine();
+1
View File
@@ -9,6 +9,7 @@ namespace WithoutArgs
public static void Main()
{
Console.WriteLine($"[*] Hello World from `{Assembly.GetExecutingAssembly().FullName}`!");
Console.WriteLine($"[*] I am running in `{AppDomain.CurrentDomain}`!");
Console.WriteLine("[*] I have no arguments and live a happy life!");
Console.WriteLine("[*] Bye!");