From cc2d7dc9bfacccd413787f1f06e969e303f61611 Mon Sep 17 00:00:00 2001 From: Nyeogmi Date: Fri, 25 Apr 2025 19:28:24 -0700 Subject: [PATCH] Inject code into Notepad --- runner/Cargo.lock | 19 +++ runner/Cargo.toml | 5 +- runner/build.rs | 22 ++++ runner/src/main.rs | 309 ++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 319 insertions(+), 36 deletions(-) create mode 100644 runner/build.rs diff --git a/runner/Cargo.lock b/runner/Cargo.lock index 779e0a7..7c446b7 100644 --- a/runner/Cargo.lock +++ b/runner/Cargo.lock @@ -284,6 +284,7 @@ dependencies = [ "serde_json", "serde_with", "windows-sys", + "winres", ] [[package]] @@ -423,6 +424,15 @@ dependencies = [ "time-core", ] +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + [[package]] name = "unicode-ident" version = "1.0.18" @@ -618,3 +628,12 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winres" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c" +dependencies = [ + "toml", +] diff --git a/runner/Cargo.toml b/runner/Cargo.toml index 486d383..281d72a 100644 --- a/runner/Cargo.toml +++ b/runner/Cargo.toml @@ -8,4 +8,7 @@ serde = {version = "1.0.219", features=["derive"]} serde_bytes = "0.11.17" serde_json = "1.0.140" serde_with = {version="3.12.0", features=["base64"]} -windows-sys = {version="0.59.0", features=["Win32_System_Memory", "Win32_System_LibraryLoader"]} +windows-sys = {version="0.59.0", features=["Win32_System_Memory", "Win32_System_LibraryLoader", "Win32_System_Diagnostics_ToolHelp", "Win32_System_Threading", "Win32_System_Diagnostics_Debug", "Win32_Security"]} + +[build-dependencies] +winres = "0.1.12" diff --git a/runner/build.rs b/runner/build.rs new file mode 100644 index 0000000..6752c62 --- /dev/null +++ b/runner/build.rs @@ -0,0 +1,22 @@ +extern crate winres; + +fn main() { + /* + let mut res = winres::WindowsResource::new(); + res.set_manifest( + r#" + + + + + + + + + + "#, + ); + res.compile() + .expect("expected to be able to build resources"); + */ +} diff --git a/runner/src/main.rs b/runner/src/main.rs index 3df214b..0163354 100644 --- a/runner/src/main.rs +++ b/runner/src/main.rs @@ -1,4 +1,5 @@ -use std::ffi::c_void; +use std::ffi::{c_void, CStr}; +use std::mem::MaybeUninit; use std::ptr; use std::{fs::File, io::Read}; @@ -7,9 +8,15 @@ use serde_with::base64::{Base64, Standard}; use serde_with::formats::Padded; use serde_with::serde_as; -use windows_sys::Win32::Foundation::HMODULE; -use windows_sys::Win32::System::LibraryLoader; +use windows_sys::Win32::Foundation::{GetLastError, PROC}; +use windows_sys::Win32::System::Diagnostics::Debug; +use windows_sys::Win32::System::Diagnostics::ToolHelp::{self, PROCESSENTRY32}; use windows_sys::Win32::System::Memory; +use windows_sys::Win32::System::Threading::PROCESS_ALL_ACCESS; +use windows_sys::Win32::System::{LibraryLoader, Threading}; + +const LOAD_LIBRARY_A: *const c_void = LibraryLoader::LoadLibraryA as *const c_void; +const GET_PROC_ADDRESS: *const c_void = LibraryLoader::GetProcAddress as *const c_void; #[serde_as] #[derive(Debug, Deserialize)] @@ -32,54 +39,286 @@ struct Binary { entry_point: usize, } fn main() { - unsafe { _main() } + unsafe { _main_remote() } } -unsafe fn _main() { + +struct LocalWriter { + binary: *mut u8, + base: *mut u8, + ptr: *mut u8, +} +struct Measurer { + count: usize, +} + +impl Measurer {} + +trait CodeGen { + unsafe fn write(&mut self, u8s: &[u8]) -> *const c_void; + + unsafe fn write_ptr(&mut self, ptr: *const c_void) -> *const c_void { + let bytes = unsafe { std::mem::transmute::<*const c_void, [u8; 8]>(ptr) }; + self.write(&bytes) + } + + unsafe fn address_in_binary(&mut self, offset: usize) -> *const c_void; +} + +impl Measurer { + fn round_up(&mut self) { + self.count = (self.count + 15) / 16 * 16; + } +} + +impl CodeGen for LocalWriter { + unsafe fn write(&mut self, u8s: &[u8]) -> *const c_void { + let addr = self.ptr; + for u in u8s { + *self.ptr = *u; + self.ptr = self.ptr.byte_add(1); + } + self.binary.offset(addr.offset_from(self.base)) as *const c_void + } + + unsafe fn address_in_binary(&mut self, offset: usize) -> *const c_void { + self.binary.byte_add(offset) as *const c_void + } +} + +impl CodeGen for Measurer { + unsafe fn write(&mut self, u8s: &[u8]) -> *const c_void { + self.count += u8s.len(); + return ptr::null(); + } + + unsafe fn address_in_binary(&mut self, offset: usize) -> *const c_void { + return ptr::null(); + } +} + +unsafe fn write_imports( + strings: &mut impl CodeGen, + code: &mut impl CodeGen, + original_entry_point: usize, + imports: &[Import], +) { + // push rbp + code.write(&[0x55]); + // mov rbp, rsp + code.write(&[0x48, 0x89, 0xe5]); + // nop + code.write(&[0x90]); + // sub rsp, 0x20 + code.write(&[0x48, 0x83, 0xec, 0x20]); + // windows needs 32 bytes to clobber: https://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_x64_calling_convention + for import in imports { + let library_addr = strings.write(&import.dll); + strings.write(&[0]); + let symbol_addr = strings.write(&import.symbol); + strings.write(&[0]); + + // mov rcx, library name + code.write(&[0x48, 0xb9]); + code.write_ptr(library_addr); + // mov rax, LoadLibraryA + code.write(&[0x48, 0xb8]); + code.write_ptr(LOAD_LIBRARY_A); + // call rax + code.write(&[0xff, 0xd0]); + + // mov rcx, rax + code.write(&[0x48, 0x89, 0xc1]); + // mov rdx, symbol name + code.write(&[0x48, 0xba]); + code.write_ptr(symbol_addr); + // mov rax, GetProcAddress + code.write(&[0x48, 0xb8]); + code.write_ptr(GET_PROC_ADDRESS); + // call rax + code.write(&[0xff, 0xd0]); + // mov [import address], rax + code.write(&[0x48, 0xa3]); + let addr = code.address_in_binary(import.address); + code.write_ptr(addr); + } + // mov rax, original entry point + code.write(&[0x48, 0xb8]); + let addr = code.address_in_binary(original_entry_point); + // let addr = load_library_a; + code.write_ptr(addr); + // call rax + code.write(&[0xff, 0xd0]); + // leave, ret + code.write(&[0xc9, 0xc3]); +} + +unsafe fn _main_remote() { let mut input = File::open("binaries/main.json").unwrap(); let mut buf: Vec = vec![]; let _ = input.read_to_end(&mut buf).unwrap(); let binary = serde_json::from_slice::(&buf).unwrap(); - println!("Binary: {:?}", binary); - let address_space = Memory::VirtualAlloc( + let mut string_measurer = Measurer { count: 0 }; + let mut code_measurer = Measurer { count: 0 }; + write_imports( + &mut string_measurer, + &mut code_measurer, + binary.entry_point, + &binary.imports, + ); + // string_measurer.round_up(); + // code_measurer.round_up(); + + // TODO: Alignment + let Some(victim) = identify_victim() else { + println!("couldn't find victim"); + return; + }; + let remote = Threading::OpenProcess(PROCESS_ALL_ACCESS, 0, victim); + if remote == ptr::null_mut() { + println!("got null"); + return; + } + let total_size = binary.starting_state.len() + string_measurer.count + code_measurer.count; + let remote_address_space = Memory::VirtualAllocEx( + remote, ptr::null(), - binary.starting_state.len(), + total_size, Memory::MEM_COMMIT | Memory::MEM_RESERVE, Memory::PAGE_EXECUTE_READWRITE, ); + let local_address_space = Memory::VirtualAlloc( + ptr::null(), + total_size, + Memory::MEM_COMMIT | Memory::MEM_RESERVE, + Memory::PAGE_EXECUTE_READWRITE, + ); + + local_address_space.copy_from( + binary.starting_state.as_ptr() as *const c_void, + binary.starting_state.len(), + ); + + let strings = local_address_space.byte_add(binary.starting_state.len()); + let prelude = strings.byte_add(string_measurer.count); + let mut strings_writer = LocalWriter { + binary: remote_address_space as *mut u8, + base: local_address_space as *mut u8, + ptr: strings as *mut u8, + }; + let mut prelude_writer = LocalWriter { + binary: remote_address_space as *mut u8, + base: local_address_space as *mut u8, + ptr: prelude as *mut u8, + }; + write_imports( + &mut strings_writer, + &mut prelude_writer, + binary.entry_point, + &binary.imports, + ); + println!("error: {}", GetLastError()); + println!( + "Copying memory to foreign process (at {:?} from {:?} get {:?})", + remote_address_space, local_address_space, total_size + ); + Debug::WriteProcessMemory( + remote, + remote_address_space, + local_address_space, + total_size, + ptr::null_mut(), + ); + println!("error: {}", GetLastError()); + println!("Creating remote thread"); + + let entry_point = + remote_address_space.byte_add(binary.starting_state.len() + string_measurer.count); + println!("Entry point: {:?}", entry_point); + Threading::CreateRemoteThread( + remote, + ptr::null(), + 0, + Some(std::mem::transmute::< + *mut c_void, + unsafe extern "system" fn(*mut c_void) -> u32, + >(entry_point)), + ptr::null(), + 0, + ptr::null_mut(), + ); + println!("error: {}", GetLastError()); + println!("Done!"); +} + +unsafe fn identify_victim() -> Option { + let snapshot = ToolHelp::CreateToolhelp32Snapshot(ToolHelp::TH32CS_SNAPPROCESS, 0); + let mut entry: MaybeUninit = MaybeUninit::uninit(); + entry.assume_init_mut().dwSize = std::mem::size_of::() as u32; + let mut process = ToolHelp::Process32First(snapshot, entry.as_mut_ptr()); + println!("error: {}; process: {}", GetLastError(), process); + while process != 0 { + let entry_ref = entry.assume_init_ref(); + let name = CStr::from_ptr(entry_ref.szExeFile.as_ptr()); + println!("peeking: {:?}", name); + if name.eq(CStr::from_bytes_with_nul_unchecked(b"notepad.exe\x00")) { + return Some(entry_ref.th32ProcessID); + } + process = ToolHelp::Process32Next(snapshot, entry.as_mut_ptr()); + } + return None; +} +/* +unsafe fn _main_local() { + let mut input = File::open("binaries/main.json").unwrap(); + let mut buf: Vec = vec![]; + let _ = input.read_to_end(&mut buf).unwrap(); + + let binary = serde_json::from_slice::(&buf).unwrap(); + + let mut string_measurer = Measurer { count: 0 }; + let mut code_measurer = Measurer { count: 0 }; + write_imports( + &mut string_measurer, + &mut code_measurer, + binary.entry_point, + &binary.imports, + ); + + // TODO: Alignment + let address_space = Memory::VirtualAlloc( + ptr::null(), + binary.starting_state.len() + string_measurer.count + code_measurer.count, + Memory::MEM_COMMIT | Memory::MEM_RESERVE, + Memory::PAGE_EXECUTE_READWRITE, + ); + address_space.copy_from( binary.starting_state.as_ptr() as *const c_void, binary.starting_state.len(), ); - for i in binary.imports.iter() { - let mut lcpstr_dll = vec![]; - lcpstr_dll.extend(&i.dll); - lcpstr_dll.push(0); - let mut lpcstr_procname = vec![]; - lpcstr_procname.extend(&i.symbol); - lpcstr_procname.push(0); + let strings = address_space.byte_add(binary.starting_state.len()); + let prelude = strings.byte_add(string_measurer.count); + let mut strings_writer = LocalWriter { + binary: address_space as *mut u8, + ptr: strings as *mut u8, + }; + let mut prelude_writer = LocalWriter { + binary: address_space as *mut u8, + ptr: prelude as *mut u8, + }; + write_imports( + &mut strings_writer, + &mut prelude_writer, + binary.entry_point, + &binary.imports, + ); - println!("handling import"); - let dll: HMODULE = LibraryLoader::LoadLibraryA(lcpstr_dll.as_ptr()); + let function: unsafe extern "C" fn() -> *const c_void = std::mem::transmute(prelude); - // TODO: Free anything? No real need to. - let proc = LibraryLoader::GetProcAddress(dll, lpcstr_procname.as_ptr()).unwrap(); - let proc_ptr = proc as *mut c_void; - println!( - "writing at {} out of {}", - i.address, - binary.starting_state.len() - ); - let location = address_space.byte_add(i.address) as *mut *mut c_void; - *location = proc_ptr; - println!("handled import"); - } - println!("done with imports"); - let fn_start = address_space.byte_add(binary.entry_point); - let function: unsafe fn() -> () = std::mem::transmute(fn_start); - - function(); - println!("returned"); + let result = function(); } + +*/