use dotscope::{
metadata::{
signatures::{encode_method_signature, SignatureMethod, SignatureParameter, TypeSignature},
tables::{CodedIndex, CodedIndexType, TableId},
token::Token,
},
prelude::*,
};
use std::{env, path::Path};
fn main() -> Result<()> {
let args: Vec<String> = env::args().collect();
if args.len() != 3 {
eprintln!("Usage: {} <input-assembly> <output-assembly>", args[0]);
eprintln!();
eprintln!("This example demonstrates .NET assembly code injection:");
eprintln!(" - Finding or creating external assembly references");
eprintln!(" - Creating type and member references for BCL types");
eprintln!(" - Adding user strings for string literals");
eprintln!(" - Injecting new static methods with CIL implementation");
eprintln!(" - Finding suitable injection targets in existing types");
eprintln!(" - Complete workflow with validation and PE generation");
eprintln!();
eprintln!("Example:");
eprintln!(" {} input.dll injected.dll", args[0]);
eprintln!();
eprintln!("The injected method will be:");
eprintln!(" public static void PrintHelloWorld()");
eprintln!(" {{");
eprintln!(" System.Console.WriteLine(\"Hello World from dotscope!\");");
eprintln!(" }}");
return Ok(());
}
let input_path = Path::new(&args[1]);
let output_path = Path::new(&args[2]);
println!(".NET Assembly Code Injection Tool");
println!("Input: {}", input_path.display());
println!("Output: {}", output_path.display());
println!();
println!("Loading assembly for modification...");
let view = CilAssemblyView::from_path(input_path).map_err(|e| {
eprintln!("x Failed to load assembly: {e}");
eprintln!(" Make sure the file is a valid .NET assembly");
e
})?;
let mut assembly = CilAssembly::new(view);
println!(" Assembly loaded successfully");
println!();
println!("Finding suitable injection target...");
let target_type_token = find_injection_target(&assembly)?;
println!(
" Selected injection target: TypeDef token {:#08X}",
target_type_token.value()
);
println!();
println!("Adding user string for hello world message...");
let hello_ref = assembly.userstring_add("Hello World from dotscope!")?;
let hello_placeholder = hello_ref.placeholder();
let hello_string_token = Token::new(0x70000000 | hello_placeholder);
println!(" User string queued for addition");
println!();
println!("Creating external references for System.Console.WriteLine...");
let mscorlib_ref = AssemblyRefBuilder::new()
.name("System.Runtime")
.version(8, 0, 0, 0) .public_key_token(&[
0xb0, 0x3f, 0x5f, 0x7f, 0x11, 0xd5, 0x0a, 0x3a, ])
.build(&mut assembly)?;
let console_typeref = TypeRefBuilder::new()
.name("Console")
.namespace("System")
.resolution_scope(CodedIndex::new(
TableId::AssemblyRef,
mscorlib_ref.placeholder(),
CodedIndexType::ResolutionScope,
))
.build(&mut assembly)?;
let writeline_signature = create_writeline_signature()?;
let console_writeline_ref = MemberRefBuilder::new()
.name("WriteLine")
.class(CodedIndex::new(
TableId::TypeRef,
console_typeref.placeholder(),
CodedIndexType::MemberRefParent,
))
.signature(&writeline_signature)
.build(&mut assembly)?;
let console_writeline_token = console_writeline_ref
.placeholder_token()
.expect("Console.WriteLine ChangeRef should be a table row");
println!(" Created mscorlib reference");
println!(" Created Console.WriteLine reference");
println!();
println!("Injecting PrintHelloWorld method...");
MethodBuilder::new("PrintHelloWorld")
.public()
.static_method()
.returns(TypeSignature::Void)
.implementation(move |body| {
body.implementation(move |asm| {
asm.ldstr(hello_string_token)? .call(console_writeline_token)? .ret()?; Ok(())
})
})
.build(&mut assembly)?;
println!(" Method definition created");
println!();
println!("Writing modified assembly...");
assembly.to_file(output_path).map_err(|e| {
eprintln!("x Failed to write assembly: {e}");
e
})?;
println!(
" Successfully wrote modified assembly to {}",
output_path.display()
);
println!();
println!("Summary:");
if let Some(offset) = hello_ref.offset() {
println!(" - User string at heap offset: {:#x}", offset);
}
println!(" - Injected static method: PrintHelloWorld()");
println!(" - Created external references to System.Console.WriteLine");
println!(" - Generated valid PE file with proper metadata");
println!();
println!("You can now call the injected method from other .NET code:");
println!(" YourAssembly.YourType.PrintHelloWorld();");
Ok(())
}
fn find_injection_target(_assembly: &CilAssembly) -> Result<Token> {
let first_typedef_token = Token::new(0x02000001);
println!(
" Using TypeDef token: {:#08X}",
first_typedef_token.value()
);
println!(" (In a real implementation, use CilObject to find ideal injection targets)");
Ok(first_typedef_token)
}
fn create_writeline_signature() -> Result<Vec<u8>> {
let signature = SignatureMethod {
has_this: false, explicit_this: false,
default: true, vararg: false,
cdecl: false,
stdcall: false,
thiscall: false,
fastcall: false,
param_count_generic: 0,
param_count: 1, return_type: SignatureParameter {
modifiers: Vec::new(),
by_ref: false,
base: TypeSignature::Void, },
params: vec![SignatureParameter {
modifiers: Vec::new(),
by_ref: false,
base: TypeSignature::String, }],
varargs: Vec::new(),
};
encode_method_signature(&signature)
}