mirror of
https://github.com/Ben-Lichtman/reloader
synced 2026-06-08 10:27:31 +00:00
initial commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
/target
|
||||
/Cargo.lock
|
||||
@@ -0,0 +1,12 @@
|
||||
edition = "2021"
|
||||
fn_single_line = true
|
||||
hard_tabs = true
|
||||
trailing_semicolon = true
|
||||
use_field_init_shorthand = true
|
||||
use_try_shorthand = true
|
||||
struct_lit_single_line = true
|
||||
condense_wildcard_suffixes = true
|
||||
control_brace_style = "ClosingNextLine"
|
||||
overflow_delimited_expr = false
|
||||
use_small_heuristics = "Default"
|
||||
imports_granularity = "Crate"
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "reloader"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
default = ["debug"]
|
||||
debug = []
|
||||
|
||||
[dependencies]
|
||||
cstr_core = "0.2.5"
|
||||
ntapi = "0.3.7"
|
||||
object = { version = "0.28.3", default_features = false, features = ["read"] }
|
||||
wchar = "0.11.0"
|
||||
|
||||
[dependencies.windows-sys]
|
||||
version = "0.34.0"
|
||||
features = ["Win32_Foundation", "Win32_System_Memory", "Win32_System_LibraryLoader"]
|
||||
@@ -0,0 +1,21 @@
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
|
||||
#[repr(u16)]
|
||||
pub enum Error {
|
||||
SelfFind,
|
||||
ModuleByHash,
|
||||
ModuleByAscii,
|
||||
ExportVaByHash,
|
||||
ExportVaByAscii,
|
||||
PeHeaders,
|
||||
ExportTable,
|
||||
ImportTable,
|
||||
Allocation,
|
||||
Protect,
|
||||
Flush,
|
||||
RelocationType,
|
||||
SplitString,
|
||||
ParseNumber,
|
||||
SyscallNumber,
|
||||
LdrLoadDll,
|
||||
}
|
||||
+315
@@ -0,0 +1,315 @@
|
||||
use crate::{
|
||||
error::{Error, Result},
|
||||
structures::{ExportTable, PeHeaders},
|
||||
};
|
||||
use core::{
|
||||
mem::MaybeUninit,
|
||||
ptr::null_mut,
|
||||
slice,
|
||||
sync::atomic::{compiler_fence, Ordering},
|
||||
};
|
||||
use cstr_core::CStr;
|
||||
use ntapi::{ntldr::LDR_DATA_TABLE_ENTRY, ntpsapi::PEB_LDR_DATA};
|
||||
use windows_sys::Win32::Foundation::UNICODE_STRING;
|
||||
|
||||
const SYSCALL_TABLE_SIZE: usize = 512;
|
||||
const LIBRARY_CONVERSION_BUFFER_SIZE: usize = 64;
|
||||
|
||||
#[inline(never)]
|
||||
pub const fn fnv1a_hash_32_wstr(wchars: &[u16]) -> u32 {
|
||||
const FNV_OFFSET_BASIS_32: u32 = 0x811c9dc5;
|
||||
const FNV_PRIME_32: u32 = 0x01000193;
|
||||
|
||||
let mut hash = FNV_OFFSET_BASIS_32;
|
||||
|
||||
let mut i = 0;
|
||||
while i < wchars.len() {
|
||||
let c = unsafe { char::from_u32_unchecked(wchars[i] as u32).to_ascii_lowercase() };
|
||||
hash ^= c as u32;
|
||||
hash = hash.wrapping_mul(FNV_PRIME_32);
|
||||
i += 1;
|
||||
}
|
||||
hash
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub const fn fnv1a_hash_32(chars: &[u8]) -> u32 {
|
||||
const FNV_OFFSET_BASIS_32: u32 = 0x811c9dc5;
|
||||
const FNV_PRIME_32: u32 = 0x01000193;
|
||||
|
||||
let mut hash = FNV_OFFSET_BASIS_32;
|
||||
|
||||
let mut i = 0;
|
||||
while i < chars.len() {
|
||||
let c = unsafe { char::from_u32_unchecked(chars[i] as u32).to_ascii_lowercase() };
|
||||
hash ^= c as u32;
|
||||
hash = hash.wrapping_mul(FNV_PRIME_32);
|
||||
i += 1;
|
||||
}
|
||||
hash
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn simple_memcpy(dest: *mut u8, src: *mut u8, len: usize) {
|
||||
let n_bytes = len; // Iterate backwards to avoid optimizing..?
|
||||
for i in (0..n_bytes).rev() {
|
||||
compiler_fence(Ordering::Acquire);
|
||||
unsafe { *dest.add(i) = *src.add(i) };
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn simple_memset_u32(dest: &mut [MaybeUninit<u32>], value: u32) {
|
||||
let n_bytes = dest.len(); // Iterate backwards to avoid optimizing..?
|
||||
for i in (0..n_bytes).rev() {
|
||||
compiler_fence(Ordering::Acquire);
|
||||
unsafe { dest.get_unchecked_mut(i).write(value) };
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn ascii_wstr_eq(ascii: &CStr, wstr: &[u16]) -> bool {
|
||||
// Check if the lengths are equal
|
||||
if wstr.len() != ascii.to_bytes().len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if they are equal
|
||||
if wstr
|
||||
.iter()
|
||||
.copied()
|
||||
.zip(ascii.to_bytes().iter().copied())
|
||||
.map(|(a, b)| ((a as u8).to_ascii_lowercase(), b.to_ascii_lowercase()))
|
||||
.any(|(a, b)| a != b)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn ascii_ascii_eq(a: &[u8], b: &[u8]) -> bool {
|
||||
// Check if the lengths are equal
|
||||
if a.len() != b.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if they are equal
|
||||
if a.iter()
|
||||
.copied()
|
||||
.zip(b.iter().copied())
|
||||
.map(|(a, b)| (a.to_ascii_lowercase(), b.to_ascii_lowercase()))
|
||||
.any(|(a, b)| a != b)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn find_pe(start: usize) -> Result<(*mut u8, PeHeaders)> {
|
||||
let mut page_aligned = start & !0xfff;
|
||||
loop {
|
||||
if page_aligned == 0 {
|
||||
return Err(Error::SelfFind);
|
||||
}
|
||||
|
||||
match PeHeaders::parse(page_aligned as _) {
|
||||
Ok(pe) => break Ok((page_aligned as _, pe)),
|
||||
Err(_) => page_aligned -= 0x1000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn find_loaded_module_by_hash(ldr: &PEB_LDR_DATA, hash: u32) -> Result<*mut u8> {
|
||||
// Get initial entry in the list
|
||||
let mut ldr_data_ptr = ldr.InLoadOrderModuleList.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
||||
while !ldr_data_ptr.is_null() {
|
||||
let ldr_data = unsafe { &*ldr_data_ptr };
|
||||
|
||||
// Make a slice of wchars from the base name
|
||||
let dll_name = ldr_data.BaseDllName;
|
||||
let buffer = dll_name.Buffer;
|
||||
if buffer.is_null() {
|
||||
break;
|
||||
}
|
||||
let dll_name_wstr = unsafe { slice::from_raw_parts(buffer, dll_name.Length as usize / 2) };
|
||||
|
||||
if fnv1a_hash_32_wstr(dll_name_wstr) != hash {
|
||||
// Go to the next entry
|
||||
ldr_data_ptr = ldr_data.InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Return the base address for this DLL
|
||||
return Ok(ldr_data.DllBase as _);
|
||||
}
|
||||
Err(Error::ModuleByHash)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn find_loaded_module_by_ascii(ldr: &PEB_LDR_DATA, ascii: *const i8) -> Result<*mut u8> {
|
||||
let ascii = unsafe { CStr::from_ptr(ascii) };
|
||||
|
||||
// Get initial entry in the list
|
||||
let mut ldr_data_ptr = ldr.InLoadOrderModuleList.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
||||
while !ldr_data_ptr.is_null() {
|
||||
let ldr_data = unsafe { &*ldr_data_ptr };
|
||||
|
||||
// Make a slice of wchars from the base name
|
||||
let dll_name = ldr_data.BaseDllName;
|
||||
let buffer = dll_name.Buffer;
|
||||
if buffer.is_null() {
|
||||
break;
|
||||
}
|
||||
let dll_name_wstr = unsafe { slice::from_raw_parts(buffer, dll_name.Length as usize / 2) };
|
||||
|
||||
if ascii_wstr_eq(ascii, dll_name_wstr) {
|
||||
return Ok(ldr_data.DllBase as _);
|
||||
}
|
||||
|
||||
// Go to the next entry
|
||||
ldr_data_ptr = ldr_data.InLoadOrderLinks.Flink as *mut LDR_DATA_TABLE_ENTRY;
|
||||
}
|
||||
Err(Error::ModuleByAscii)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn find_export_by_hash(exports: &ExportTable, base: *mut u8, hash: u32) -> Result<*mut u8> {
|
||||
exports
|
||||
.iter_string_addr(base)
|
||||
.find(|(name, _)| fnv1a_hash_32(name.to_bytes()) == hash)
|
||||
.map(|(_, addr)| addr)
|
||||
.ok_or(Error::ExportVaByHash)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn find_export_by_ascii(
|
||||
exports: &ExportTable,
|
||||
base: *mut u8,
|
||||
string: &CStr,
|
||||
) -> Result<*mut u8> {
|
||||
exports
|
||||
.iter_string_addr(base)
|
||||
.find(|(name, _)| ascii_ascii_eq(name.to_bytes(), string.to_bytes()))
|
||||
.map(|(_, addr)| addr)
|
||||
.ok_or(Error::ExportVaByAscii)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn get_library_base(
|
||||
peb_ldr: &PEB_LDR_DATA,
|
||||
library_name: *const u8,
|
||||
ldrloaddll: unsafe extern "system" fn(
|
||||
DllPath: *const u16,
|
||||
DllCharacteristics: *const u32,
|
||||
DllName: *const UNICODE_STRING,
|
||||
DllHandle: *mut *mut u8,
|
||||
) -> i32,
|
||||
) -> Result<*mut u8> {
|
||||
let loaded_library_base = match find_loaded_module_by_ascii(peb_ldr, library_name as _) {
|
||||
Ok(base) => base,
|
||||
Err(_) => {
|
||||
let name_ascii = unsafe { CStr::from_ptr(library_name as _) };
|
||||
|
||||
let mut buffer_space = [MaybeUninit::uninit(); LIBRARY_CONVERSION_BUFFER_SIZE];
|
||||
buffer_space
|
||||
.iter_mut()
|
||||
.zip(name_ascii.to_bytes_with_nul().iter())
|
||||
.for_each(|(wchar, &ascii)| {
|
||||
wchar.write(ascii as u16);
|
||||
});
|
||||
|
||||
let unicode_string = UNICODE_STRING {
|
||||
Length: (name_ascii.to_bytes().len() * 2) as _,
|
||||
MaximumLength: (name_ascii.to_bytes_with_nul().len() * 2) as _,
|
||||
Buffer: MaybeUninit::slice_as_ptr(&buffer_space) as _,
|
||||
};
|
||||
|
||||
// Now load the library
|
||||
let mut module_handle = null_mut::<u8>();
|
||||
|
||||
unsafe {
|
||||
ldrloaddll(
|
||||
null_mut(),
|
||||
null_mut(),
|
||||
&unicode_string as _,
|
||||
&mut module_handle,
|
||||
)
|
||||
};
|
||||
if module_handle.is_null() {
|
||||
return Err(Error::LdrLoadDll);
|
||||
}
|
||||
module_handle
|
||||
}
|
||||
};
|
||||
if loaded_library_base.is_null() {
|
||||
return Err(Error::LdrLoadDll);
|
||||
}
|
||||
Ok(loaded_library_base)
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn syscall_table(exports: &ExportTable, base: *mut u8) -> [u32; SYSCALL_TABLE_SIZE] {
|
||||
let mut scratch_table = [MaybeUninit::<(u32, *mut u8)>::uninit(); SYSCALL_TABLE_SIZE];
|
||||
let mut num_syscalls = 0;
|
||||
|
||||
// Iterate through exports which match the names of syscalls
|
||||
exports
|
||||
.iter_string_addr(base)
|
||||
.filter(|(name, _)| {
|
||||
// Our condition is - name must start with zW
|
||||
let name = name.to_bytes();
|
||||
let name_0 = match name.get(0) {
|
||||
Some(&x) => x,
|
||||
None => return false,
|
||||
};
|
||||
let name_1 = match name.get(1) {
|
||||
Some(&x) => x,
|
||||
None => return false,
|
||||
};
|
||||
if name_0 != b'Z' {
|
||||
return false;
|
||||
}
|
||||
if name_1 != b'w' {
|
||||
return false;
|
||||
}
|
||||
true
|
||||
})
|
||||
.enumerate()
|
||||
.for_each(|(n, (name, addr))| {
|
||||
// Turn each function name into a hash
|
||||
let name_hash = fnv1a_hash_32(name.to_bytes());
|
||||
|
||||
unsafe { scratch_table.get_unchecked_mut(n).write((name_hash, addr)) };
|
||||
num_syscalls += 1;
|
||||
});
|
||||
|
||||
let working_slice = unsafe {
|
||||
MaybeUninit::slice_assume_init_mut(scratch_table.get_unchecked_mut(..num_syscalls))
|
||||
};
|
||||
// Sort the filled entries by address
|
||||
working_slice.sort_unstable_by_key(|(_, addr)| *addr);
|
||||
|
||||
let mut output = [MaybeUninit::uninit(); SYSCALL_TABLE_SIZE];
|
||||
|
||||
simple_memset_u32(&mut output, 0);
|
||||
|
||||
let mut output = unsafe { MaybeUninit::array_assume_init(output) };
|
||||
|
||||
// Copy hashes over to output slice
|
||||
for i in 0..num_syscalls {
|
||||
unsafe { *output.get_unchecked_mut(i) = working_slice.get_unchecked(i).0 };
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn find_syscall_by_hash(table: &[u32; SYSCALL_TABLE_SIZE], hash: u32) -> Result<u32> {
|
||||
table
|
||||
.iter()
|
||||
.position(|&table_hash| table_hash == hash)
|
||||
.map(|x| x as u32)
|
||||
.ok_or(Error::SyscallNumber)
|
||||
}
|
||||
+510
@@ -0,0 +1,510 @@
|
||||
#![no_std]
|
||||
#![feature(slice_split_at_unchecked)]
|
||||
#![feature(maybe_uninit_slice)]
|
||||
#![feature(maybe_uninit_array_assume_init)]
|
||||
#![feature(const_char_convert)]
|
||||
#![feature(asm_const)]
|
||||
|
||||
mod error;
|
||||
mod helpers;
|
||||
mod structures;
|
||||
mod syscall;
|
||||
|
||||
use crate::{
|
||||
error::{Error, Result},
|
||||
helpers::{
|
||||
find_export_by_ascii, find_export_by_hash, find_loaded_module_by_hash, find_pe,
|
||||
find_syscall_by_hash, fnv1a_hash_32, fnv1a_hash_32_wstr, get_library_base, simple_memcpy,
|
||||
syscall_table,
|
||||
},
|
||||
structures::PeHeaders,
|
||||
syscall::{syscall3, syscall5, syscall6},
|
||||
};
|
||||
use core::{
|
||||
arch::asm,
|
||||
mem::{size_of, transmute},
|
||||
ptr::null_mut,
|
||||
slice,
|
||||
str::from_utf8_unchecked,
|
||||
};
|
||||
use cstr_core::CStr;
|
||||
use ntapi::{
|
||||
ntpebteb::TEB,
|
||||
winapi::um::winnt::{DLL_PROCESS_ATTACH, PAGE_NOACCESS},
|
||||
};
|
||||
use object::{
|
||||
pe::{
|
||||
ImageThunkData64, IMAGE_DIRECTORY_ENTRY_BASERELOC, IMAGE_REL_BASED_ABSOLUTE,
|
||||
IMAGE_REL_BASED_DIR64, IMAGE_REL_BASED_HIGHLOW, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ,
|
||||
IMAGE_SCN_MEM_WRITE,
|
||||
},
|
||||
read::pe::{ImageNtHeaders, ImageOptionalHeader, ImageThunkData},
|
||||
LittleEndian,
|
||||
};
|
||||
use wchar::wch;
|
||||
use windows_sys::Win32::{
|
||||
Foundation::{STATUS_SUCCESS, UNICODE_STRING},
|
||||
System::Memory::{
|
||||
MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE,
|
||||
PAGE_EXECUTE_WRITECOPY, PAGE_READONLY, PAGE_READWRITE, PAGE_WRITECOPY,
|
||||
},
|
||||
};
|
||||
|
||||
// Abusing fnv1a hash to find the strings we're looking for
|
||||
// Can't just do a string comparison because the segments aren't properly loaded yet
|
||||
|
||||
const NTDLL_HASH: u32 = fnv1a_hash_32_wstr(wch!("ntdll.dll"));
|
||||
|
||||
const ZWFLUSHINSTRUCTIONCACHE_HASH: u32 = fnv1a_hash_32("ZwFlushInstructionCache".as_bytes());
|
||||
const ZWALLOCATEVIRTUALMEMORY_HASH: u32 = fnv1a_hash_32("ZwAllocateVirtualMemory".as_bytes());
|
||||
const ZWPROTECTVIRTUALMEMORY_HASH: u32 = fnv1a_hash_32("ZwProtectVirtualMemory".as_bytes());
|
||||
|
||||
const LDRLOADDLL_HASH: u32 = fnv1a_hash_32("LdrLoadDll".as_bytes());
|
||||
|
||||
#[inline(never)]
|
||||
fn load() -> Result<(*mut u8, *mut u8)> {
|
||||
let rip: usize;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
unsafe {
|
||||
asm!("lea {rip}, [rip]", rip = out(reg) rip)
|
||||
};
|
||||
#[cfg(target_arch = "x86")]
|
||||
unsafe {
|
||||
asm!("lea {eip}, [eip]", rip = out(reg) rip)
|
||||
};
|
||||
let (pe_base, pe) = find_pe(rip)?;
|
||||
|
||||
// Locate other important data structures
|
||||
let teb: *mut TEB;
|
||||
unsafe {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
asm!("mov {teb}, gs:[0x30]", teb = out(reg) teb);
|
||||
#[cfg(target_arch = "x86")]
|
||||
asm!("mov {teb}, fs:[0x18]", teb = out(reg) teb);
|
||||
}
|
||||
let teb = unsafe { &mut *teb };
|
||||
let peb = unsafe { &mut *teb.ProcessEnvironmentBlock };
|
||||
|
||||
let peb_ldr = unsafe { &*peb.Ldr };
|
||||
|
||||
// Traverse loaded modules to find ntdll.dll
|
||||
let ntdll_base = find_loaded_module_by_hash(peb_ldr, NTDLL_HASH)?;
|
||||
let ntdll = PeHeaders::parse(ntdll_base)?;
|
||||
|
||||
// Locate the export table for ntdll.dll
|
||||
let ntdll_export_table = ntdll.export_table_mem(ntdll_base)?;
|
||||
|
||||
let syscall_table = syscall_table(&ntdll_export_table, ntdll_base);
|
||||
|
||||
// Find some important syscall numbers
|
||||
let sys_no_zwflushinstructioncache =
|
||||
find_syscall_by_hash(&syscall_table, ZWFLUSHINSTRUCTIONCACHE_HASH)?;
|
||||
let sys_no_zwallocatevirtualmemory =
|
||||
find_syscall_by_hash(&syscall_table, ZWALLOCATEVIRTUALMEMORY_HASH)?;
|
||||
let sys_no_zwprotectvirtualmemory =
|
||||
find_syscall_by_hash(&syscall_table, ZWPROTECTVIRTUALMEMORY_HASH)?;
|
||||
|
||||
let ldrloaddll = find_export_by_hash(&ntdll_export_table, ntdll_base, LDRLOADDLL_HASH)?;
|
||||
let ldrloaddll = unsafe {
|
||||
transmute::<
|
||||
_,
|
||||
unsafe extern "system" fn(
|
||||
DllPath: *const u16,
|
||||
DllCharacteristics: *const u32,
|
||||
DllName: *const UNICODE_STRING,
|
||||
DllHandle: *mut *mut u8,
|
||||
) -> i32,
|
||||
>(ldrloaddll)
|
||||
};
|
||||
|
||||
// Allocate space to map the PE into
|
||||
let size_of_image = pe
|
||||
.nt_header
|
||||
.optional_header()
|
||||
.size_of_image
|
||||
.get(LittleEndian) as usize;
|
||||
|
||||
let mut allocated_ptr = null_mut::<u8>();
|
||||
let mut region_size = size_of_image as usize;
|
||||
|
||||
let nt_status = unsafe {
|
||||
syscall6(
|
||||
sys_no_zwallocatevirtualmemory,
|
||||
-1i64 as _,
|
||||
&mut allocated_ptr as *mut _ as _,
|
||||
0,
|
||||
&mut region_size as *mut _ as _,
|
||||
(MEM_RESERVE | MEM_COMMIT) as _,
|
||||
PAGE_READWRITE as _,
|
||||
)
|
||||
};
|
||||
if nt_status != STATUS_SUCCESS as _ {
|
||||
return Err(Error::Allocation);
|
||||
}
|
||||
|
||||
if allocated_ptr.is_null() {
|
||||
return Err(Error::Allocation);
|
||||
}
|
||||
|
||||
// Copy over header data
|
||||
let header_size = pe
|
||||
.nt_header
|
||||
.optional_header()
|
||||
.size_of_headers
|
||||
.get(LittleEndian) as _;
|
||||
simple_memcpy(allocated_ptr, pe_base, header_size);
|
||||
|
||||
// Map sections
|
||||
pe.section_headers.iter().for_each(|section| {
|
||||
let dest = unsafe { allocated_ptr.add(section.virtual_address.get(LittleEndian) as _) };
|
||||
let src = unsafe { pe_base.add(section.pointer_to_raw_data.get(LittleEndian) as _) };
|
||||
let size = section.size_of_raw_data.get(LittleEndian) as _;
|
||||
simple_memcpy(dest, src, size);
|
||||
});
|
||||
|
||||
// Process import table
|
||||
let import_table = pe.import_table_mem(allocated_ptr)?;
|
||||
for idt in import_table.import_descriptors {
|
||||
// Load the library
|
||||
let name_rva = idt.name.get(LittleEndian) as usize;
|
||||
let library_name = unsafe { allocated_ptr.add(name_rva) };
|
||||
|
||||
// Load library if it is not already loaded and get the base
|
||||
let loaded_library_base = get_library_base(peb_ldr, library_name as _, ldrloaddll)?;
|
||||
|
||||
// Find the exports of the loaded library
|
||||
let loaded_library = PeHeaders::parse(loaded_library_base)?;
|
||||
let loaded_library_exports = loaded_library.export_table_mem(loaded_library_base)?;
|
||||
|
||||
let ilt_rva = idt.original_first_thunk.get(LittleEndian) as usize;
|
||||
let iat_rva = idt.first_thunk.get(LittleEndian) as usize;
|
||||
|
||||
let mut ilt_ptr = unsafe { allocated_ptr.add(ilt_rva).cast::<ImageThunkData64>() };
|
||||
let mut iat_ptr = unsafe { allocated_ptr.add(iat_rva).cast::<usize>() };
|
||||
|
||||
// Look through each entry in the ILT until we find a null entry
|
||||
while unsafe { ilt_ptr.read().raw() != 0 } {
|
||||
let ilt_entry = unsafe { ilt_ptr.read() };
|
||||
|
||||
// Resolve the function VA - taking forwarding into account
|
||||
|
||||
// First step - get an address from direct dependency
|
||||
let mut resolved_address = match ilt_entry.is_ordinal() {
|
||||
true => {
|
||||
// Load from ordinal
|
||||
let ordinal = ilt_entry.ordinal();
|
||||
|
||||
// Find matching function in loaded library
|
||||
let export_rva = unsafe {
|
||||
*loaded_library_exports
|
||||
.address_table
|
||||
.get_unchecked(ordinal as usize)
|
||||
};
|
||||
|
||||
unsafe { loaded_library_base.add(export_rva as _) }
|
||||
}
|
||||
false => {
|
||||
// Load from name
|
||||
let address_rva = ilt_entry.address() as _;
|
||||
|
||||
// Get the name of the function
|
||||
let string_va = unsafe { allocated_ptr.add(address_rva).add(size_of::<u16>()) };
|
||||
let string = unsafe { CStr::from_ptr(string_va as _) };
|
||||
|
||||
// Find matching function in loaded library
|
||||
find_export_by_ascii(&loaded_library_exports, loaded_library_base, string)?
|
||||
}
|
||||
};
|
||||
|
||||
// Forwarding
|
||||
let mut current_export_start = loaded_library_exports.start_address;
|
||||
let mut current_export_end =
|
||||
unsafe { current_export_start.add(loaded_library_exports.size as _) };
|
||||
let function_va = loop {
|
||||
let mut buffer = [0u8; 64];
|
||||
|
||||
if !(current_export_start <= resolved_address
|
||||
&& resolved_address < current_export_end)
|
||||
{
|
||||
// The pointer is not located in the exports section and therefore has no more forwarders
|
||||
break resolved_address;
|
||||
}
|
||||
|
||||
// We have a pointer to a null-terminated string of the form "MYDLL.expfunc" or "MYDLL.#27"
|
||||
let string = unsafe { CStr::from_ptr(resolved_address as _) };
|
||||
let string = string.to_bytes_with_nul();
|
||||
|
||||
// Copy string to buffer
|
||||
for i in 0..string.len() {
|
||||
unsafe {
|
||||
let c = *string.get_unchecked(i);
|
||||
*buffer.get_unchecked_mut(i) = c;
|
||||
}
|
||||
}
|
||||
|
||||
// Give an extra 4 bytes for adding "dll\0"
|
||||
let working_buffer = unsafe { buffer.get_unchecked_mut(..string.len() + 4) };
|
||||
|
||||
// Find the dot
|
||||
let dot_index = working_buffer
|
||||
.iter()
|
||||
.position(|&b| b == b'.')
|
||||
.ok_or(Error::SplitString)?;
|
||||
|
||||
// Move things after the dot forwards
|
||||
for i in (dot_index + 1..working_buffer.len()).rev() {
|
||||
unsafe {
|
||||
let c = *working_buffer.get_unchecked(i);
|
||||
*working_buffer.get_unchecked_mut(i + 4) = c;
|
||||
};
|
||||
}
|
||||
|
||||
// Write DLL
|
||||
unsafe {
|
||||
*working_buffer.get_unchecked_mut(dot_index + 1) = b'd';
|
||||
*working_buffer.get_unchecked_mut(dot_index + 2) = b'l';
|
||||
*working_buffer.get_unchecked_mut(dot_index + 3) = b'l';
|
||||
*working_buffer.get_unchecked_mut(dot_index + 4) = b'\0';
|
||||
}
|
||||
|
||||
let (dll_name, rest) =
|
||||
unsafe { working_buffer.split_at_mut_unchecked(dot_index + 5) };
|
||||
|
||||
// Load library if it is not already loaded and get the base
|
||||
let loaded_library_base = get_library_base(peb_ldr, dll_name.as_ptr(), ldrloaddll)?;
|
||||
|
||||
// Find the exports of the loaded library
|
||||
let loaded_library = PeHeaders::parse(loaded_library_base)?;
|
||||
let loaded_library_exports =
|
||||
loaded_library.export_table_mem(loaded_library_base)?;
|
||||
|
||||
// Set resolved address for next loop
|
||||
resolved_address = if unsafe { *rest.get_unchecked(0) } == b'#' {
|
||||
// Load from ordinal
|
||||
let number_string = unsafe { from_utf8_unchecked(rest.get_unchecked(1..)) };
|
||||
let ordinal =
|
||||
u16::from_str_radix(number_string, 10).map_err(|_| Error::ParseNumber)?;
|
||||
|
||||
// Find matching function in loaded library
|
||||
let export_rva = unsafe {
|
||||
*loaded_library_exports
|
||||
.address_table
|
||||
.get_unchecked(ordinal as usize)
|
||||
};
|
||||
|
||||
unsafe { loaded_library_base.add(export_rva as _) }
|
||||
}
|
||||
else {
|
||||
// Load from name
|
||||
|
||||
// Get the name of the function
|
||||
let string = unsafe { CStr::from_ptr(rest.as_ptr() as _) };
|
||||
|
||||
// Find matching function in loaded library
|
||||
find_export_by_ascii(&loaded_library_exports, loaded_library_base, string)?
|
||||
};
|
||||
|
||||
// Set the new export range
|
||||
current_export_start = loaded_library_exports.start_address;
|
||||
current_export_end =
|
||||
unsafe { current_export_start.add(loaded_library_exports.size as _) };
|
||||
};
|
||||
|
||||
// Write function VA into IAT
|
||||
unsafe { *iat_ptr = function_va as _ };
|
||||
|
||||
// Advance to the next entry
|
||||
ilt_ptr = unsafe { ilt_ptr.add(1) };
|
||||
iat_ptr = unsafe { iat_ptr.add(1) };
|
||||
}
|
||||
}
|
||||
|
||||
// Process relocations
|
||||
let image_base_in_file = pe.nt_header.optional_header().image_base();
|
||||
let calculated_offset = allocated_ptr as isize - image_base_in_file as isize;
|
||||
|
||||
let relocations = unsafe {
|
||||
pe.data_directories
|
||||
.get_unchecked(IMAGE_DIRECTORY_ENTRY_BASERELOC)
|
||||
};
|
||||
|
||||
// Iterate through the relocation table
|
||||
let reloc_start_address =
|
||||
unsafe { allocated_ptr.add(relocations.virtual_address.get(LittleEndian) as _) };
|
||||
let reloc_size_bytes = relocations.size.get(LittleEndian) as _;
|
||||
|
||||
let mut reloc_byte_slice =
|
||||
unsafe { slice::from_raw_parts(reloc_start_address, reloc_size_bytes) };
|
||||
|
||||
// Loop over relocation blocks - each has a 8 byte header
|
||||
while let &[a, b, c, d, e, f, g, h, ref rest @ ..] = reloc_byte_slice {
|
||||
let rva = u32::from_le_bytes([a, b, c, d]) as usize;
|
||||
let relocs_bytes = u32::from_le_bytes([e, f, g, h]) as usize - 8;
|
||||
|
||||
let block_va = unsafe { allocated_ptr.add(rva) };
|
||||
|
||||
// Loop over the relocations in this block
|
||||
let (mut relocs_slice, rest) = unsafe { rest.split_at_unchecked(relocs_bytes) };
|
||||
while let &[a, b, ref rest @ ..] = relocs_slice {
|
||||
let reloc = u16::from_le_bytes([a, b]);
|
||||
|
||||
let reloc_type = (reloc & 0xf000) >> 0xc;
|
||||
let reloc_offset = reloc & 0x0fff;
|
||||
|
||||
let reloc_va = unsafe { block_va.add(reloc_offset as _) };
|
||||
|
||||
// Apply the relocation
|
||||
match reloc_type {
|
||||
IMAGE_REL_BASED_ABSOLUTE => (),
|
||||
IMAGE_REL_BASED_DIR64 => {
|
||||
let ptr = reloc_va as *mut u64;
|
||||
unsafe { *ptr = *ptr + calculated_offset as u64 };
|
||||
}
|
||||
IMAGE_REL_BASED_HIGHLOW => {
|
||||
let ptr = reloc_va as *mut u32;
|
||||
unsafe { *ptr = *ptr + calculated_offset as u32 };
|
||||
}
|
||||
_ => return Err(Error::RelocationType),
|
||||
}
|
||||
|
||||
relocs_slice = rest;
|
||||
}
|
||||
|
||||
reloc_byte_slice = rest;
|
||||
}
|
||||
|
||||
// Set header permissions
|
||||
let base_address = allocated_ptr;
|
||||
let mut region_size = header_size;
|
||||
let mut old_permissions = u32::default();
|
||||
let nt_status = unsafe {
|
||||
syscall5(
|
||||
sys_no_zwprotectvirtualmemory,
|
||||
-1i64 as _,
|
||||
&base_address as *const _ as _,
|
||||
&mut region_size as *mut _ as _,
|
||||
PAGE_READONLY as _,
|
||||
&mut old_permissions as *mut _ as _,
|
||||
)
|
||||
};
|
||||
if nt_status != STATUS_SUCCESS as _ {
|
||||
return Err(Error::Protect);
|
||||
}
|
||||
|
||||
// Set section permissions
|
||||
pe.section_headers.iter().try_for_each(|section| {
|
||||
let virtual_address =
|
||||
unsafe { allocated_ptr.add(section.virtual_address.get(LittleEndian) as _) };
|
||||
let virtual_size = section.virtual_size.get(LittleEndian);
|
||||
|
||||
// Change permissions
|
||||
let characteristics = section.characteristics.get(LittleEndian);
|
||||
let r = characteristics & IMAGE_SCN_MEM_READ != 0;
|
||||
let w = characteristics & IMAGE_SCN_MEM_WRITE != 0;
|
||||
let x = characteristics & IMAGE_SCN_MEM_EXECUTE != 0;
|
||||
let new_permissions = match (r, w, x) {
|
||||
(false, false, false) => PAGE_NOACCESS,
|
||||
(true, false, false) => PAGE_READONLY,
|
||||
(false, true, false) => PAGE_WRITECOPY,
|
||||
(true, true, false) => PAGE_READWRITE,
|
||||
(false, false, true) => PAGE_EXECUTE,
|
||||
(true, false, true) => PAGE_EXECUTE_READ,
|
||||
(false, true, true) => PAGE_EXECUTE_WRITECOPY,
|
||||
(true, true, true) => PAGE_EXECUTE_READWRITE,
|
||||
};
|
||||
let base_address = virtual_address;
|
||||
let mut region_size = virtual_size;
|
||||
let mut old_permissions = u32::default();
|
||||
let nt_status = unsafe {
|
||||
syscall5(
|
||||
sys_no_zwprotectvirtualmemory,
|
||||
-1i64 as _,
|
||||
&base_address as *const _ as _,
|
||||
&mut region_size as *mut _ as _,
|
||||
new_permissions as _,
|
||||
&mut old_permissions as *mut _ as _,
|
||||
)
|
||||
};
|
||||
if nt_status != STATUS_SUCCESS as _ {
|
||||
return Err(Error::Protect);
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
let entry_point = unsafe {
|
||||
allocated_ptr.add(
|
||||
pe.nt_header
|
||||
.optional_header()
|
||||
.address_of_entry_point
|
||||
.get(LittleEndian) as _,
|
||||
)
|
||||
};
|
||||
|
||||
// We must flush the instruction cache to avoid stale code being used which was updated by our relocation processing
|
||||
let nt_status = unsafe { syscall3(sys_no_zwflushinstructioncache, -1i64 as _, 0, 0) };
|
||||
if nt_status != STATUS_SUCCESS as _ {
|
||||
return Err(Error::Flush);
|
||||
}
|
||||
// unsafe { ntflushinstructioncache(-1 as _, null_mut(), 0) };
|
||||
|
||||
Ok((allocated_ptr, entry_point))
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn handle_error(error: Error) {
|
||||
#[cfg(feature = "debug")]
|
||||
{
|
||||
let error_code = error as u16;
|
||||
|
||||
// Write error code to invalid address for rudimentary debugging
|
||||
unsafe { *(0xdeadbeefdeadbeef as *mut _) = error_code };
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[inline(never)]
|
||||
pub extern "system" fn reflective_loader(reserved: usize) {
|
||||
match load() {
|
||||
Ok((allocated_ptr, entry_point)) => {
|
||||
// Call entry point
|
||||
let entry_point_callable = unsafe {
|
||||
transmute::<_, unsafe extern "system" fn(usize, u32, usize)>(entry_point)
|
||||
};
|
||||
|
||||
unsafe { entry_point_callable(allocated_ptr as _, DLL_PROCESS_ATTACH, reserved) };
|
||||
}
|
||||
Err(e) => handle_error(e),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[inline(never)]
|
||||
pub extern "system" fn reflective_loader_wow64(reserved: usize) {
|
||||
unsafe {
|
||||
asm!(
|
||||
".code32",
|
||||
"push ebp",
|
||||
"mov ebp, esp",
|
||||
"and esp, 0xfffffff0",
|
||||
"push 0x33",
|
||||
"call 1f",
|
||||
"1:",
|
||||
"add dword ptr [esp], 5",
|
||||
"retf",
|
||||
".code64",
|
||||
);
|
||||
reflective_loader(reserved);
|
||||
asm!(
|
||||
".code64",
|
||||
"call 1f",
|
||||
"1:",
|
||||
"mov dword ptr [rsp + 4], 0x23",
|
||||
"add dword ptr [rsp], 0xd",
|
||||
"retf",
|
||||
".code32",
|
||||
"mov esp, ebp",
|
||||
"pop ebp",
|
||||
".code64",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
use crate::error::{Error, Result};
|
||||
use core::{mem::size_of, slice};
|
||||
use cstr_core::CStr;
|
||||
use object::{
|
||||
pe::{
|
||||
self, ImageDataDirectory, ImageDosHeader, ImageExportDirectory, ImageImportDescriptor,
|
||||
ImageSectionHeader, IMAGE_DIRECTORY_ENTRY_EXPORT, IMAGE_DIRECTORY_ENTRY_IMPORT,
|
||||
IMAGE_DOS_SIGNATURE, IMAGE_NT_SIGNATURE,
|
||||
},
|
||||
read::pe::{ImageNtHeaders, ImageOptionalHeader},
|
||||
LittleEndian,
|
||||
};
|
||||
|
||||
pub struct PeHeaders {
|
||||
pub dos_header: &'static mut ImageDosHeader,
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub nt_header: &'static mut pe::ImageNtHeaders64,
|
||||
#[cfg(target_arch = "x86")]
|
||||
pub nt_header: &'static mut pe::ImageNtHeaders32,
|
||||
pub data_directories: &'static mut [ImageDataDirectory],
|
||||
pub section_headers: &'static mut [ImageSectionHeader],
|
||||
}
|
||||
|
||||
impl PeHeaders {
|
||||
#[inline(never)]
|
||||
pub fn parse(address: *mut u8) -> Result<Self> {
|
||||
let dos_header_ptr = address;
|
||||
let dos_header = unsafe { &mut *dos_header_ptr.cast::<ImageDosHeader>() };
|
||||
if dos_header.e_magic.get(LittleEndian) != IMAGE_DOS_SIGNATURE {
|
||||
return Err(Error::PeHeaders);
|
||||
}
|
||||
let nt_header_offset = dos_header.nt_headers_offset() as usize;
|
||||
// Sanity check
|
||||
if nt_header_offset > 1024 {
|
||||
return Err(Error::PeHeaders);
|
||||
}
|
||||
let nt_header_ptr = unsafe { address.add(nt_header_offset) };
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
let nt_header = unsafe { &mut *nt_header_ptr.cast::<pe::ImageNtHeaders64>() };
|
||||
#[cfg(target_arch = "x86")]
|
||||
let nt_header = unsafe { &mut *nt_header_ptr.cast::<ImageNtHeaders32>() };
|
||||
if nt_header.signature.get(LittleEndian) != IMAGE_NT_SIGNATURE {
|
||||
return Err(Error::PeHeaders);
|
||||
}
|
||||
if !nt_header.is_valid_optional_magic() {
|
||||
return Err(Error::PeHeaders);
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
let data_directories_ptr = unsafe { nt_header_ptr.add(size_of::<pe::ImageNtHeaders64>()) };
|
||||
#[cfg(target_arch = "x86")]
|
||||
let data_directories_ptr = unsafe { nt_header_ptr.add(size_of::<pe::ImageNtHeaders32>()) };
|
||||
let num_data_directories = nt_header.optional_header().number_of_rva_and_sizes() as _;
|
||||
let data_directories = unsafe {
|
||||
slice::from_raw_parts_mut(
|
||||
data_directories_ptr.cast::<ImageDataDirectory>(),
|
||||
num_data_directories,
|
||||
)
|
||||
};
|
||||
let section_headers_ptr = unsafe {
|
||||
data_directories_ptr.add(num_data_directories * size_of::<ImageDataDirectory>())
|
||||
};
|
||||
let num_section_headers = nt_header.file_header().number_of_sections.get(LittleEndian) as _;
|
||||
let section_headers = unsafe {
|
||||
slice::from_raw_parts_mut(
|
||||
section_headers_ptr.cast::<ImageSectionHeader>(),
|
||||
num_section_headers,
|
||||
)
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
dos_header,
|
||||
nt_header,
|
||||
data_directories,
|
||||
section_headers,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn export_table_mem(&self, image_base: *mut u8) -> Result<ExportTable> {
|
||||
let export_table_data_dir = self
|
||||
.data_directories
|
||||
.get(IMAGE_DIRECTORY_ENTRY_EXPORT)
|
||||
.ok_or(Error::ExportTable)?;
|
||||
let export_table_rva = export_table_data_dir.virtual_address.get(LittleEndian);
|
||||
let export_table_ptr = unsafe { image_base.add(export_table_rva as _) };
|
||||
let export_table_size = export_table_data_dir.size.get(LittleEndian);
|
||||
Ok(ExportTable::parse(
|
||||
export_table_ptr,
|
||||
export_table_rva as _,
|
||||
export_table_size,
|
||||
))
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn import_table_mem(&self, image_base: *mut u8) -> Result<ImportTable> {
|
||||
let import_table_data_dir = self
|
||||
.data_directories
|
||||
.get(IMAGE_DIRECTORY_ENTRY_IMPORT)
|
||||
.ok_or(Error::ImportTable)?;
|
||||
let import_table_rva = import_table_data_dir.virtual_address.get(LittleEndian);
|
||||
let import_table_size = import_table_data_dir.size.get(LittleEndian);
|
||||
let import_table_ptr = unsafe { image_base.add(import_table_rva as _) };
|
||||
Ok(ImportTable::parse(import_table_ptr, import_table_size as _))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ExportTable {
|
||||
pub export_directory: &'static mut ImageExportDirectory,
|
||||
pub address_table: &'static mut [u32],
|
||||
pub name_table: &'static mut [u32],
|
||||
pub ordinal_table: &'static mut [u16],
|
||||
pub start_address: *mut u8,
|
||||
pub size: u32,
|
||||
}
|
||||
|
||||
impl ExportTable {
|
||||
#[inline(never)]
|
||||
pub fn parse(address: *mut u8, rva: usize, size: u32) -> Self {
|
||||
let export_directory_ptr = address;
|
||||
let export_directory = unsafe { &mut *export_directory_ptr.cast::<ImageExportDirectory>() };
|
||||
|
||||
let address_table_ptr = unsafe {
|
||||
address
|
||||
.add(export_directory.address_of_functions.get(LittleEndian) as _)
|
||||
.wrapping_sub(rva)
|
||||
.cast::<u32>()
|
||||
};
|
||||
let address_table_len = export_directory.number_of_functions.get(LittleEndian) as _;
|
||||
let address_table =
|
||||
unsafe { slice::from_raw_parts_mut(address_table_ptr, address_table_len) };
|
||||
|
||||
let name_table_ptr = unsafe {
|
||||
address
|
||||
.add(export_directory.address_of_names.get(LittleEndian) as _)
|
||||
.wrapping_sub(rva)
|
||||
.cast::<u32>()
|
||||
};
|
||||
let name_table_len = export_directory.number_of_names.get(LittleEndian) as _;
|
||||
let name_table = unsafe { slice::from_raw_parts_mut(name_table_ptr, name_table_len) };
|
||||
|
||||
let ordinal_table_ptr = unsafe {
|
||||
address
|
||||
.add(export_directory.address_of_name_ordinals.get(LittleEndian) as _)
|
||||
.wrapping_sub(rva)
|
||||
.cast::<u16>()
|
||||
};
|
||||
let ordinal_table_len = export_directory.number_of_names.get(LittleEndian) as _;
|
||||
let ordinal_table =
|
||||
unsafe { slice::from_raw_parts_mut(ordinal_table_ptr, ordinal_table_len) };
|
||||
|
||||
Self {
|
||||
export_directory,
|
||||
address_table,
|
||||
name_table,
|
||||
ordinal_table,
|
||||
start_address: address,
|
||||
size,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn iter_name_ord(&self) -> impl Iterator<Item = (u32, u16)> + '_ {
|
||||
self.name_table
|
||||
.iter()
|
||||
.copied()
|
||||
.zip(self.ordinal_table.iter().copied())
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub fn iter_string_addr(&self, image_base: *mut u8) -> impl Iterator<Item = (&CStr, *mut u8)> {
|
||||
self.iter_name_ord().map(move |(name_rva, ord)| {
|
||||
let string_ptr = unsafe { image_base.add(name_rva as _) };
|
||||
let string = unsafe { CStr::from_ptr(string_ptr as _) };
|
||||
let address_rva = unsafe { *self.address_table.get_unchecked(ord as usize) };
|
||||
let address = unsafe { image_base.add(address_rva as _) };
|
||||
(string, address)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ImportTable {
|
||||
pub import_descriptors: &'static mut [ImageImportDescriptor],
|
||||
}
|
||||
|
||||
impl ImportTable {
|
||||
#[inline(never)]
|
||||
pub fn parse(address: *mut u8, size: usize) -> Self {
|
||||
let number_of_entries = size / size_of::<ImageImportDescriptor>() - 1;
|
||||
let import_descriptor_ptr = address.cast::<ImageImportDescriptor>();
|
||||
let import_descriptors =
|
||||
unsafe { slice::from_raw_parts_mut(import_descriptor_ptr, number_of_entries) };
|
||||
|
||||
Self { import_descriptors }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
use core::arch::asm;
|
||||
|
||||
#[inline(never)]
|
||||
pub unsafe fn syscall3(number: u32, arg1: u64, arg2: u64, arg3: u64) -> u64 {
|
||||
let output: u64;
|
||||
asm!(
|
||||
"syscall",
|
||||
in("rax") number,
|
||||
in("r10") arg1,
|
||||
in("rdx") arg2,
|
||||
in("r8") arg3,
|
||||
lateout("rax") output,
|
||||
lateout("rcx") _,
|
||||
lateout("r11") _
|
||||
);
|
||||
output
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub unsafe fn syscall5(number: u32, arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64) -> u64 {
|
||||
let output: u64;
|
||||
asm!(
|
||||
"sub rsp, 0x38",
|
||||
"mov [rsp+0x28], {arg5}",
|
||||
"syscall",
|
||||
"add rsp, 0x38",
|
||||
arg5 = in(reg) arg5,
|
||||
in("rax") number,
|
||||
in("r10") arg1,
|
||||
in("rdx") arg2,
|
||||
in("r8") arg3,
|
||||
in("r9") arg4,
|
||||
lateout("rax") output,
|
||||
lateout("rcx") _,
|
||||
lateout("r11") _
|
||||
);
|
||||
output
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
pub unsafe fn syscall6(
|
||||
number: u32,
|
||||
arg1: u64,
|
||||
arg2: u64,
|
||||
arg3: u64,
|
||||
arg4: u64,
|
||||
arg5: u64,
|
||||
arg6: u64,
|
||||
) -> u64 {
|
||||
let output: u64;
|
||||
asm!(
|
||||
"sub rsp, 0x38",
|
||||
"mov [rsp+0x30], {arg6}",
|
||||
"mov [rsp+0x28], {arg5}",
|
||||
"syscall",
|
||||
"add rsp, 0x38",
|
||||
arg5 = in(reg) arg5,
|
||||
arg6 = in(reg) arg6,
|
||||
in("rax") number,
|
||||
in("r10") arg1,
|
||||
in("rdx") arg2,
|
||||
in("r8") arg3,
|
||||
in("r9") arg4,
|
||||
lateout("rax") output,
|
||||
lateout("rcx") _,
|
||||
lateout("r11") _
|
||||
);
|
||||
output
|
||||
}
|
||||
Reference in New Issue
Block a user