#binaries #execute #dotnet #hosting #year #host #clr

clroxide

一个允许您托管 CLR 和执行 dotnet 二进制的库

7 个稳定版本

1.1.1 2023 年 3 月 31 日
1.0.9 2023 年 3 月 25 日
1.0.6 2023 年 3 月 24 日
1.0.5 2023 年 3 月 22 日
1.0.4 2023 年 3 月 17 日

196日期和时间

Download history 4/week @ 2024-03-13 18/week @ 2024-03-20 5/week @ 2024-03-27 4/week @ 2024-04-03 12/week @ 2024-04-24 21/week @ 2024-05-01 24/week @ 2024-05-08 10/week @ 2024-05-15 6/week @ 2024-05-22 8/week @ 2024-05-29 34/week @ 2024-06-05 24/week @ 2024-06-12 4/week @ 2024-06-19 18/week @ 2024-06-26

86 每月下载量

MIT 许可证

315KB
3K SLoC

ClrOxide

ClrOxide 是一个 Rust 库,允许您托管 CLR 并动态执行 dotnet 二进制文件。

我本来想叫它 Kepler,但 Cargo 中已经有一个名为 kepler 的包。:(

我已经断断续续地用 Rust 托管 CLR 两年了,但直到两周前才有所突破!

这个库的实现离不开以下项目

  • NimPlant 及其 执行汇编 实现
    • winim/clear 允许重写 Console.Write 的输出缓冲区并获取输出,其优雅性是本库用了两年才达到的原因。我如何让 Cas 玩 Rust,如果他不复制这个!?我的 Rust 实现为 NimPlant 的工作也是我最初进入这个迷宫的原因。
  • go-clr by ropnop
    • 在此特别感谢 ropnop!这个库完全是由于 go-clr 中的一个功能,使我一切都豁然开朗,三天内完成了整个库!
  • dinvoke_rs by Kudaes
    • 类似于 go-clr,Kurosh 的 dinvoke_rs 项目也让 Rust/win32 的复杂性变得清晰,并使项目得以继续进行。
  • 各种 CLR 相关的 Rust 库

架构限制

ClrOxide

ClrOxide 仅在编译为 x86_64-pc-windows-gnux86_64-pc-windows-msvc 时才能正常工作。

针对 i686-pc-windows-gnu 的编译失败,因为存在已知的 rust panic unwinding 问题。它可能使用 i686-pc-windows-msvc 可以工作,但我自己还没有尝试。

汇编

尽管我自己没有遇到过这个问题,但可能存在需要将汇编专门编译为 x64 而不是 Any CPU 的情况。

使用

您可以在 examples/ 文件夹中找到更多示例。

运行汇编并捕获其输出

assembly_arch

ClrOxide 将在当前进程中加载 CLR,解析 mscorlib 并重定向 System.Console 的输出,最后加载并运行您的可执行文件,并以字符串的形式返回其输出。

目前不支持输出流,尽管我相信用于重定向输出的 CLR 策略魔法可以为愿意实现它的人提供很好的指导。

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 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);
}

使用自定义应用域

assembly_arch

您可以通过更新上下文来使用自定义应用域。如果您想避免 DefaultDomain,这可能会很有用。有关更多详细信息,请参阅 examples/custom_app_domain.rs

...

  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)?;

...

mscoree.dll 使用自定义加载器

我们需要从 mscoree.dll 加载 CreateInterface 函数以启动 CLR。您可以通过禁用默认功能来提供自定义加载器。

首先,将 default-features = false 添加到您的依赖声明中。

clroxide = { version = "1.0.6", default-features = false }

然后提供一个具有以下签名的函数,它返回在创建 Clr 实例时指向 CreateInterface 函数的指针:fn() -> Result<isize, String>

litcrypt::use_litcrypt!();

fn load_function() -> Result<isize, String> {
  let library = custom_load_library_a(lc!("mscoree.dll\0"));

  if library == 0 {
    return Err("Failed".into());
  }
  
  let function = custom_get_process_address(library, lc!("CreateInterface\0"));
  
  if function == 0 {
    return Err("Failed".into());
  }
  
  Ok(function)
}

fn main() -> Result<(), String> {
 
  // ...

  let mut context = Clr::new(contents, args, load_function)?;

  // ...
  
}

修改 System.Environment.Exit 以不退出

assembly_arch

您可以使用 ClrOxide 提供的构建块来修改 System.Environment.Exit,如 MDSec 在 Massaging your CLR: Preventing Environment.Exit in In-Process .NET Assemblies 中所述。

您可以在 examples/patch_exit.rs 中查看参考实现。由于这需要使用 VirtualProtectNtProtectVirtualMemory,所以我打算不将其添加为 ClrOxide 的功能。

依赖项

~130MB
~2M SLoC