Compare commits

...

5 Commits

32 changed files with 69 additions and 191 deletions

View File

@ -1,3 +0,0 @@
{
"python.analysis.typeCheckingMode": "standard"
}

File diff suppressed because one or more lines are too long

BIN
arrows.xcf Normal file

Binary file not shown.

BIN
diagram.xcf Normal file

Binary file not shown.

View File

@ -2,78 +2,13 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "runner"
version = "0.1.0"
dependencies = [
"windows-sys",
"winres",
]
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "2.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "windows-sys"
version = "0.59.0"
@ -146,12 +81,3 @@ 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",
]

View File

@ -3,8 +3,16 @@ name = "runner"
version = "0.1.0"
edition = "2021"
[profile.dev]
panic = "abort"
[profile.release]
# source: https://github.com/johnthagen/min-sized-rust
panic = "abort"
opt-level = "z"
strip = true
lto = true
codegen-units = 1
[dependencies]
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"

3
injector/build.rs Normal file
View File

@ -0,0 +1,3 @@
fn main() {
println!("cargo:rustc-link-arg=-s")
}

BIN
injector/squeak.bat Normal file

Binary file not shown.

View File

@ -1,11 +1,13 @@
use core::str;
use std::ffi::{c_void, CStr};
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::ptr;
use std::{fs::File, io::Read};
#![no_std]
#![no_main]
use core::ffi::{c_void, CStr};
use core::marker::PhantomData;
use core::mem::MaybeUninit;
#[cfg(not(test))]
use core::panic::PanicInfo;
use core::ptr;
use windows_sys::Win32::Foundation::GetLastError;
use windows_sys::Win32::System::Diagnostics::Debug;
use windows_sys::Win32::System::Diagnostics::ToolHelp::{self, PROCESSENTRY32};
use windows_sys::Win32::System::Memory;
@ -15,8 +17,10 @@ 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;
fn main() {
#[no_mangle]
pub extern "C" fn WinMain() -> i32 {
unsafe { _main_remote() }
0
}
struct Reader<'a> {
@ -40,11 +44,11 @@ impl<'a> Reader<'a> {
}
self.ptr = self.ptr.byte_add(1);
n += 1; // include the null terminator
return std::slice::from_raw_parts(start, n);
return core::slice::from_raw_parts(start, n);
}
}
struct LocalWriter {
struct Writer {
binary: *mut u8,
base: *mut u8,
ptr: *mut u8,
@ -53,20 +57,18 @@ 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) };
let bytes = unsafe { core::mem::transmute::<*const c_void, [u8; 8]>(ptr) };
self.write(&bytes)
}
unsafe fn address_in_binary(&mut self, offset: usize) -> *const c_void;
}
impl CodeGen for LocalWriter {
impl CodeGen for Writer {
unsafe fn write(&mut self, u8s: &[u8]) -> *const c_void {
let addr = self.ptr;
for u in u8s {
@ -98,7 +100,6 @@ unsafe fn write_imports(
reader: &mut Reader,
) {
let entry_point = reader.read::<u32>();
println!("entry point: {}", entry_point);
// push rbp
prelude.write(&[0x55]);
// mov rbp, rsp
@ -114,7 +115,6 @@ unsafe fn write_imports(
if dll.len() == 1 {
break;
}
println!("import dll: {:?}", str::from_utf8(dll).unwrap());
let library_addr = strings.write(dll);
strings.write(&[0]);
@ -123,7 +123,6 @@ unsafe fn write_imports(
if symbol.len() == 1 {
break;
}
println!("import symbol: {:?}", str::from_utf8(symbol).unwrap());
let dest = reader.read::<u32>();
let symbol_addr = strings.write(symbol);
@ -173,7 +172,7 @@ unsafe fn write_starting_state(mut out: *mut u8, reader: &mut Reader) {
if code > 0 {
// RLE: repeat
let byte = reader.read::<u8>();
for i in 0..code {
for _ in 0..code {
*out = byte;
out = out.byte_add(1);
}
@ -204,18 +203,12 @@ unsafe fn _main_remote() {
&mut measuring_reader,
);
let starting_state_len = measuring_reader.read::<u32>() as usize;
println!("starting state len: {}", starting_state_len);
// 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 = starting_state_len as usize + string_measurer.count + code_measurer.count;
@ -240,30 +233,22 @@ unsafe fn _main_remote() {
let strings = local_address_space.byte_add(starting_state_len);
let prelude = strings.byte_add(string_measurer.count);
let mut strings_writer = LocalWriter {
let mut strings_writer = Writer {
binary: remote_address_space as *mut u8,
base: local_address_space as *mut u8,
ptr: strings as *mut u8,
};
let mut prelude_writer = LocalWriter {
let mut prelude_writer = Writer {
binary: remote_address_space as *mut u8,
base: local_address_space as *mut u8,
ptr: prelude as *mut u8,
};
println!("writing imports");
write_imports(
&mut strings_writer,
&mut prelude_writer,
&mut loading_reader,
);
println!("writing starting state");
write_starting_state(local_address_space as *mut u8, &mut loading_reader);
println!("done!");
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,
@ -271,17 +256,13 @@ unsafe fn _main_remote() {
total_size,
ptr::null_mut(),
);
println!("error: {}", GetLastError());
println!("Creating remote thread");
let entry_point =
remote_address_space.byte_offset(prelude.byte_offset_from(local_address_space));
println!("Entry point: {:?}", entry_point);
Threading::CreateRemoteThread(
remote,
ptr::null(),
0,
Some(std::mem::transmute::<
Some(core::mem::transmute::<
*mut c_void,
unsafe extern "system" fn(*mut c_void) -> u32,
>(entry_point)),
@ -289,20 +270,16 @@ unsafe fn _main_remote() {
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;
entry.assume_init_mut().dwSize = core::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);
}
@ -310,3 +287,9 @@ unsafe fn identify_victim() -> Option<u32> {
}
return None;
}
#[cfg(not(test))]
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}

View File

@ -1,22 +0,0 @@
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");
*/
}

View File

@ -4,4 +4,4 @@
[number of bytes in starting state as a uint32_t]
00 [00-ff] <bytes>: Use the following 00-ff bytes literally
[01-ff] <byte>: Repeat the next byte 02 to ff times
[01-ff] <byte>: Repeat the next byte 01 to ff times

View File

@ -1,9 +1,7 @@
import base64
from dataclasses import dataclass
from io import BytesIO
import json
from typing import Generator
import pefile
from pefile import PE, Structure
@dataclass
class Import:
@ -36,41 +34,40 @@ def _single[T](ts: Generator[T]) -> T:
raise ValueError(f"expected 1, got {len(items)}")
def _create_binary(subject: pefile.PE) -> Binary:
def _create_binary(subject: PE) -> Binary:
optional_header = subject.OPTIONAL_HEADER
assert isinstance(optional_header, pefile.Structure)
text_section = _single(i for i in subject.sections if i.Name == b".text\0\0\0")
data_section = _single_or_none(i for i in subject.sections if i.Name == b".data\0\0\0")
rdata_section = _single_or_none(i for i in subject.sections if i.Name == b".rdata\0\0")
assert isinstance(optional_header, Structure)
text_section: Structure = _single(i for i in subject.sections if i.Name == b".text\0\0\0")
data_section: Structure | None = _single_or_none(i for i in subject.sections if i.Name == b".data\0\0\0")
rdata_section: Structure | None = _single_or_none(i for i in subject.sections if i.Name == b".rdata\0\0")
relevant_sections = [section for section in (text_section, data_section, rdata_section) if section is not None]
relevant_sections: list[Structure] = [section for section in (text_section, data_section, rdata_section) if section is not None]
if len(relevant_sections) == 0:
raise ValueError("no sections to plot")
print([(i.VirtualAddress, i) for i in relevant_sections])
min_address = min(i.VirtualAddress for i in relevant_sections)
max_address = max(_round_up_to_page(i.VirtualAddress + i.SizeOfRawData) for i in relevant_sections)
min_address = min(getattr(i, "VirtualAddress") for i in relevant_sections)
max_address = max(getattr(i, "VirtualAddress") + getattr(i, "SizeOfRawData") for i in relevant_sections)
buffer = bytearray(max_address - min_address)
for section in relevant_sections:
data = section.get_data() # TODO: De-pad the text section from 0xccs
start = section.VirtualAddress - min_address
data = getattr(section, "get_data")()
start = getattr(section, "VirtualAddress") - min_address
buffer[start:start+len(data)] = data
starting_state = bytes(buffer)
entry_point_rva = getattr(optional_header, "AddressOfEntryPoint")
print(entry_point_rva)
entry_point = (entry_point_rva - min_address)
entry_point = entry_point_rva - min_address
imports: list[Import] = []
for entry in getattr(subject, "DIRECTORY_ENTRY_IMPORT"):
library: bytes = entry.dll
entry: Structure
library: bytes = getattr(entry, "dll")
procedures: list[tuple[bytes, int]] = []
for imp in entry.imports:
# print(dir(imp))
import_address_rva = imp.address - getattr(optional_header, "ImageBase")
for imp in getattr(entry, "imports"):
imp: Structure
import_address_rva = getattr(imp, "address") - getattr(optional_header, "ImageBase")
import_address = import_address_rva - min_address
procedures.append((imp.name, import_address))
procedures.append((getattr(imp, "name"), import_address))
imports.append(Import(library, procedures))
@ -96,9 +93,7 @@ def _encode_binary(binary: Binary) -> bytes:
_write_u32(binary.entry_point)
for i in binary.imports:
print(i.library)
_write_zt(i.library)
print(i.procedures)
for (procedure, address) in i.procedures:
_write_zt(procedure)
_write_u32(address)
@ -156,16 +151,12 @@ def _encode_binary(binary: Binary) -> bytes:
return out.getbuffer()
def main():
subject = pefile.PE("subjects\\main.exe")
subject = PE("subjects\\main.exe")
binary = _create_binary(subject)
code = _encode_binary(binary)
with open("binaries\\main.dat", "wb") as f:
f.write(code)
def _round_up_to_page(x: int):
# TODO: Is this the page size on x64? I think it is
return ((x + 0x1000 - 1) // 0x1000) * 0x1000
if __name__ == "__main__":
main()

BIN
thumbnail.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 KiB

8
thumbnail.txt Normal file
View File

@ -0,0 +1,8 @@
.\ /.
'. \_ _/ .'
'o. .o'
'"'

BIN
thumbnail.xcf Normal file

Binary file not shown.

BIN
vampiric.xcf Normal file

Binary file not shown.