Inject code into Notepad
This commit is contained in:
		
							
								
								
									
										19
									
								
								runner/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										19
									
								
								runner/Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -284,6 +284,7 @@ dependencies = [ | |||||||
|  "serde_json", |  "serde_json", | ||||||
|  "serde_with", |  "serde_with", | ||||||
|  "windows-sys", |  "windows-sys", | ||||||
|  |  "winres", | ||||||
| ] | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| @@ -423,6 +424,15 @@ dependencies = [ | |||||||
|  "time-core", |  "time-core", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "toml" | ||||||
|  | version = "0.5.11" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" | ||||||
|  | dependencies = [ | ||||||
|  |  "serde", | ||||||
|  | ] | ||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "unicode-ident" | name = "unicode-ident" | ||||||
| version = "1.0.18" | version = "1.0.18" | ||||||
| @@ -618,3 +628,12 @@ name = "windows_x86_64_msvc" | |||||||
| version = "0.52.6" | version = "0.52.6" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" | ||||||
|  |  | ||||||
|  | [[package]] | ||||||
|  | name = "winres" | ||||||
|  | version = "0.1.12" | ||||||
|  | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
|  | checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c" | ||||||
|  | dependencies = [ | ||||||
|  |  "toml", | ||||||
|  | ] | ||||||
|   | |||||||
| @@ -8,4 +8,7 @@ serde = {version = "1.0.219", features=["derive"]} | |||||||
| serde_bytes = "0.11.17" | serde_bytes = "0.11.17" | ||||||
| serde_json = "1.0.140" | serde_json = "1.0.140" | ||||||
| serde_with = {version="3.12.0", features=["base64"]} | 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" | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								runner/build.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								runner/build.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | extern crate winres; | ||||||
|  |  | ||||||
|  | fn main() { | ||||||
|  |     /* | ||||||
|  |     let mut res = winres::WindowsResource::new(); | ||||||
|  |     res.set_manifest( | ||||||
|  |         r#" | ||||||
|  |     <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> | ||||||
|  |     <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> | ||||||
|  |         <security> | ||||||
|  |             <requestedPrivileges> | ||||||
|  |                 <requestedExecutionLevel level="requireAdministrator" uiAccess="false" /> | ||||||
|  |             </requestedPrivileges> | ||||||
|  |         </security> | ||||||
|  |     </trustInfo> | ||||||
|  |     </assembly> | ||||||
|  |     "#, | ||||||
|  |     ); | ||||||
|  |     res.compile() | ||||||
|  |         .expect("expected to be able to build resources"); | ||||||
|  |     */ | ||||||
|  | } | ||||||
| @@ -1,4 +1,5 @@ | |||||||
| use std::ffi::c_void; | use std::ffi::{c_void, CStr}; | ||||||
|  | use std::mem::MaybeUninit; | ||||||
| use std::ptr; | use std::ptr; | ||||||
| use std::{fs::File, io::Read}; | use std::{fs::File, io::Read}; | ||||||
|  |  | ||||||
| @@ -7,9 +8,15 @@ use serde_with::base64::{Base64, Standard}; | |||||||
| use serde_with::formats::Padded; | use serde_with::formats::Padded; | ||||||
| use serde_with::serde_as; | use serde_with::serde_as; | ||||||
|  |  | ||||||
| use windows_sys::Win32::Foundation::HMODULE; | use windows_sys::Win32::Foundation::{GetLastError, PROC}; | ||||||
| use windows_sys::Win32::System::LibraryLoader; | 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::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] | #[serde_as] | ||||||
| #[derive(Debug, Deserialize)] | #[derive(Debug, Deserialize)] | ||||||
| @@ -32,54 +39,286 @@ struct Binary { | |||||||
|     entry_point: usize, |     entry_point: usize, | ||||||
| } | } | ||||||
| fn main() { | 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 input = File::open("binaries/main.json").unwrap(); | ||||||
|     let mut buf: Vec<u8> = vec![]; |     let mut buf: Vec<u8> = vec![]; | ||||||
|     let _ = input.read_to_end(&mut buf).unwrap(); |     let _ = input.read_to_end(&mut buf).unwrap(); | ||||||
|  |  | ||||||
|     let binary = serde_json::from_slice::<Binary>(&buf).unwrap(); |     let binary = serde_json::from_slice::<Binary>(&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(), |         ptr::null(), | ||||||
|         binary.starting_state.len(), |         total_size, | ||||||
|         Memory::MEM_COMMIT | Memory::MEM_RESERVE, |         Memory::MEM_COMMIT | Memory::MEM_RESERVE, | ||||||
|         Memory::PAGE_EXECUTE_READWRITE, |         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<u32> { | ||||||
|  |     let snapshot = ToolHelp::CreateToolhelp32Snapshot(ToolHelp::TH32CS_SNAPPROCESS, 0); | ||||||
|  |     let mut entry: MaybeUninit<PROCESSENTRY32> = MaybeUninit::uninit(); | ||||||
|  |     entry.assume_init_mut().dwSize = std::mem::size_of::<PROCESSENTRY32>() 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<u8> = vec![]; | ||||||
|  |     let _ = input.read_to_end(&mut buf).unwrap(); | ||||||
|  |  | ||||||
|  |     let binary = serde_json::from_slice::<Binary>(&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( |     address_space.copy_from( | ||||||
|         binary.starting_state.as_ptr() as *const c_void, |         binary.starting_state.as_ptr() as *const c_void, | ||||||
|         binary.starting_state.len(), |         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![]; |     let strings = address_space.byte_add(binary.starting_state.len()); | ||||||
|         lpcstr_procname.extend(&i.symbol); |     let prelude = strings.byte_add(string_measurer.count); | ||||||
|         lpcstr_procname.push(0); |     let mut strings_writer = LocalWriter { | ||||||
|  |         binary: address_space as *mut u8, | ||||||
|         println!("handling import"); |         ptr: strings as *mut u8, | ||||||
|         let dll: HMODULE = LibraryLoader::LoadLibraryA(lcpstr_dll.as_ptr()); |     }; | ||||||
|  |     let mut prelude_writer = LocalWriter { | ||||||
|         // TODO: Free anything? No real need to. |         binary: address_space as *mut u8, | ||||||
|         let proc = LibraryLoader::GetProcAddress(dll, lpcstr_procname.as_ptr()).unwrap(); |         ptr: prelude as *mut u8, | ||||||
|         let proc_ptr = proc as *mut c_void; |     }; | ||||||
|         println!( |     write_imports( | ||||||
|             "writing at {} out of {}", |         &mut strings_writer, | ||||||
|             i.address, |         &mut prelude_writer, | ||||||
|             binary.starting_state.len() |         binary.entry_point, | ||||||
|  |         &binary.imports, | ||||||
|     ); |     ); | ||||||
|         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(); |     let function: unsafe extern "C" fn() -> *const c_void = std::mem::transmute(prelude); | ||||||
|     println!("returned"); |  | ||||||
|  |     let result = function(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | */ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user