use libusbk::ffi::KUSB_DRIVER_API;
use log::debug;
use std::ffi::c_void;
use std::os::raw::{c_int, c_long, c_uint, c_ulong};
use crate::Result;
use super::Vulnerability;
use crate::SwitchHandle;
const WINDOWS_FILE_DEVICE_UNKNOWN: u32 = 0x00000022;
const LIBUSBK_FUNCTION_CODE_GET_STATUS: u32 = 0x807;
const WINDOWS_METHOD_BUFFERED: u32 = 0;
const WINDOWS_FILE_ANY_ACCESS: u32 = 0;
const RAW_REQUEST_STRUCT_SIZE: usize = 24; const TO_ENDPOINT: u32 = 2;
const GET_STATUS: u32 = 0;
const INVALID_HANDLE_VALUE: isize = -1;
impl Vulnerability for SwitchHandle {
fn backend_name() -> &'static str {
"windows"
}
fn trigger(&self, length: usize) -> Result<()> {
use std::mem::size_of;
use std::mem::transmute;
let device = &self.handle;
debug!("DriverId is {:#?}", device.driver_id());
let handle = device.raw_handle().as_ptr();
let internal = unsafe { transmute::<*mut c_void, *mut KUsbHandleInternal>(handle) };
let master_handle = unsafe { (*(*internal).device).master_device_handle };
if handle.is_null()
|| master_handle as isize == INVALID_HANDLE_VALUE
|| master_handle.is_null()
{
panic!()
}
let raw_request = RawRequest {
timeout: 1000,
status: Status {
recipient: TO_ENDPOINT,
index: GET_STATUS,
status: 0,
},
_extra: [0; 8],
};
let mut out_buf = vec![0u8; length];
let in_buf = unsafe { transmute::<RawRequest, [u8; size_of::<RawRequest>()]>(raw_request) };
debug_assert_eq!(size_of::<RawRequest>(), RAW_REQUEST_STRUCT_SIZE);
debug_assert_eq!(in_buf.len(), RAW_REQUEST_STRUCT_SIZE);
const CODE: u32 = win_ctrl_code(
WINDOWS_FILE_DEVICE_UNKNOWN,
LIBUSBK_FUNCTION_CODE_GET_STATUS,
WINDOWS_METHOD_BUFFERED,
WINDOWS_FILE_ANY_ACCESS,
);
let ret = unsafe { ioctl(master_handle, CODE, &in_buf, &mut out_buf) };
if let Err(e) = ret {
const TIMEOUT_ERROR: u32 = 997;
if e == TIMEOUT_ERROR {
return Ok(());
} else {
panic!("{}", ret.unwrap_err());
}
}
Err(crate::SwitchError::ExpectedError)
}
fn validate_environment(&self) -> crate::Result<()> {
match crate::error::WindowsDriver::from(self.handle.driver_id()) {
crate::error::WindowsDriver::LibUsbK => Ok(()),
driver => Err(crate::SwitchError::WindowsWrongDriver(driver)),
}
}
}
const fn win_ctrl_code(device_type: u32, function: u32, method: u32, access: u32) -> u32 {
(device_type) << 16 | ((access) << 14) | (function) << 2 | (method)
}
unsafe fn ioctl(
driver_handle: *mut c_void,
code: u32,
input: &[u8],
output: &mut [u8],
) -> std::result::Result<(), u32> {
use std::mem::zeroed;
use winapi::ctypes::c_void;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::ioapiset::DeviceIoControl;
use winapi::um::minwinbase::OVERLAPPED;
let mut overlapped = zeroed::<OVERLAPPED>();
let mut output_bytes = 0;
let ret = DeviceIoControl(
driver_handle as usize as *mut winapi::ctypes::c_void,
code,
input.as_ptr() as *mut c_void,
input.len() as u32,
output.as_mut_ptr() as *mut c_void,
output.len() as u32,
&mut output_bytes,
&mut overlapped,
);
if ret == 0 {
let err = GetLastError();
return Err(err);
}
Ok(())
}
#[derive(Debug)]
#[repr(C)]
struct RawRequest {
timeout: c_long,
status: Status,
_extra: [u8; 8],
}
#[derive(Debug)]
#[repr(C)]
struct KDevHandleInternal {
base: KObjBase,
master_device_handle: *mut c_void,
master_interface_handle: *mut c_void,
device_path: *mut u8,
config_descriptor: *mut c_void, shared_interfaces: *mut c_void,
driver_api: *mut KUSB_DRIVER_API,
usb_stack: *mut c_void, backend: *mut c_void, }
#[derive(Debug)]
#[repr(C)]
struct KUsbHandleInternal {
base: KObjBase,
device: *mut KDevHandleInternal,
selected_shared_interface_index: c_long,
is_clone: c_int, r#move: Move,
}
#[derive(Debug)]
#[repr(C)]
struct KObjBase {
disposing: c_ulong,
evt: Evt,
count: Count,
user: User,
}
#[derive(Debug)]
#[repr(C)]
struct Evt {
cleanup: *mut c_void,
}
#[derive(Debug)]
#[repr(C)]
struct Count {
r#use: c_ulong,
r#ref: c_ulong,
}
#[derive(Debug)]
#[repr(C)]
struct User {
valid: c_int, cleanup_cb: *mut c_void,
context: *mut c_void,
}
#[derive(Debug)]
#[repr(C)]
struct Move {
end: c_int,
interface_el: *mut c_void,
alt_interface_el: *mut c_void,
pipe_el: *mut c_void,
}
#[derive(Debug)]
#[repr(C)]
struct Status {
recipient: c_uint,
index: c_uint,
status: c_uint,
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn raw_request_size() {
use std::mem::size_of;
assert_eq!(size_of::<RawRequest>(), RAW_REQUEST_STRUCT_SIZE)
}
}