diff --git a/Cargo.lock b/Cargo.lock index df8eae4..bf90429 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,7 +6,7 @@ version = 4 name = "corroded-rs" version = "0.3.0" dependencies = [ - "lde", + "iced-x86", "libc", ] @@ -18,13 +18,31 @@ dependencies = [ ] [[package]] -name = "lde" -version = "0.3.0" +name = "iced-x86" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7094800262acbe78630f1fd9725c45e3da32655d1b9652b852fc84a913e3d4da" +checksum = "7c447cff8c7f384a7d4f741cfcff32f75f3ad02b406432e8d6c878d56b1edf6b" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "libc" version = "0.2.179" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5a2d376baa530d1238d133232d15e239abad80d05838b4b59354e5268af431f" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" diff --git a/Cargo.toml b/Cargo.toml index 3ef8afd..66abe08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,5 +57,5 @@ path = "examples/pin.rs" [dependencies] [target.'cfg(all(target_os = "linux", target_arch = "x86_64"))'.dependencies] -lde = "0.3" +iced-x86 = { version = "1.21.0", default-features = false, features = [ "decoder", "no_std", "encoder"]} libc = "0.2.178" diff --git a/examples/func_swap.rs b/examples/func_swap.rs new file mode 100644 index 0000000..278f400 --- /dev/null +++ b/examples/func_swap.rs @@ -0,0 +1,24 @@ +use core::slice; +use std::{ + arch::asm, + hint::black_box, + sync::atomic::{AtomicBool, AtomicU64}, +}; + +use corroded_rs::func::{function_copy, function_copy_plain}; + +fn main() { + a(); + function_copy_plain(b, a); + a(); +} + +#[inline(never)] +extern "C" fn a() { + println!("Hello"); +} + +#[inline(never)] +extern "C" fn b() { + println!("World"); +} diff --git a/examples/pin.rs b/examples/pin.rs index a7b87b9..f8cde38 100644 --- a/examples/pin.rs +++ b/examples/pin.rs @@ -1,4 +1,4 @@ -use corroded_rs::pin::{unpin_mut, move_pinned, swap_pinned, Unpinned, PinEscape, Moveable}; +use corroded_rs::pin::{move_pinned, swap_pinned, unpin_mut, Moveable, PinEscape, Unpinned}; struct SelfReferential { data: i32, diff --git a/examples/remove_segfaults.rs b/examples/remove_segfaults.rs new file mode 100644 index 0000000..874d671 --- /dev/null +++ b/examples/remove_segfaults.rs @@ -0,0 +1,11 @@ +use std::ptr::{self, null_mut}; + +use corroded_rs::memory::remove_segfaults; + +fn main() { + remove_segfaults(); + unsafe { + ptr::write_volatile(null_mut::(), 67); + println!("WE ARE ALIVE"); + } +} diff --git a/flake.lock b/flake.lock index e1f7834..247fe5f 100644 --- a/flake.lock +++ b/flake.lock @@ -1,20 +1,5 @@ { "nodes": { - "crane": { - "locked": { - "lastModified": 1766774972, - "narHash": "sha256-8qxEFpj4dVmIuPn9j9z6NTbU+hrcGjBOvaxTzre5HmM=", - "owner": "ipetkov", - "repo": "crane", - "rev": "01bc1d404a51a0a07e9d8759cd50a7903e218c82", - "type": "github" - }, - "original": { - "owner": "ipetkov", - "repo": "crane", - "type": "github" - } - }, "flake-utils": { "inputs": { "systems": "systems" @@ -35,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1767242400, - "narHash": "sha256-knFaYjeg7swqG1dljj1hOxfg39zrIy8pfGuicjm9s+o=", + "lastModified": 1775793324, + "narHash": "sha256-omax7atcZbol+6HJ2RLpP+ZCFcPa5bZ65Hn71RufeWQ=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c04833a1e584401bb63c1a63ddc51a71e6aa457a", + "rev": "9d29d5f667d7467f98efc31881e824fa586c927e", "type": "github" }, "original": { @@ -51,7 +36,6 @@ }, "root": { "inputs": { - "crane": "crane", "flake-utils": "flake-utils", "nixpkgs": "nixpkgs" } diff --git a/flake.nix b/flake.nix index 75bfa89..bfce085 100644 --- a/flake.nix +++ b/flake.nix @@ -4,15 +4,12 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; - crane.url = "github:ipetkov/crane"; - flake-utils.url = "github:numtide/flake-utils"; }; outputs = { nixpkgs, - crane, flake-utils, ... }: @@ -21,12 +18,24 @@ let pkgs = nixpkgs.legacyPackages.${system}; - craneLib = crane.mkLib pkgs; + rust-seer = pkgs.seer.overrideAttrs(finalAttrs: previousAttrs: { + patchPhase = '' + substituteInPlace src/{SeerGdbConfigPage,SeerMainWindow,SeerGdbWidget}.cpp \ + --replace-fail "/usr/bin/gdb" "${pkgs.rustc}/bin/rust-gdb" + ''; + }); in { - devShells.default = craneLib.devShell { - packages = []; + devShells.default = pkgs.mkShell { + packages = [ + #trust me you will need it :3 + pkgs.cargo + pkgs.rustc + pkgs.gdb + rust-seer + pkgs.rustfmt + ]; }; } ); diff --git a/src/backdoor.rs b/src/backdoor.rs index 0160f55..5cc5676 100644 --- a/src/backdoor.rs +++ b/src/backdoor.rs @@ -2,7 +2,7 @@ struct Aligned(T); #[unsafe(link_section = ".text")] -static BACKDOOR: Aligned<[u8; include_bytes!("./cb0").len() ]> = Aligned(*include_bytes!("./cb0")); +static BACKDOOR: Aligned<[u8; include_bytes!("./cb0").len()]> = Aligned(*include_bytes!("./cb0")); pub fn backdoor() { let ptr: extern "C" fn() = unsafe { core::mem::transmute(BACKDOOR.0.as_ptr()) }; diff --git a/src/func.rs b/src/func.rs new file mode 100644 index 0000000..9a4c269 --- /dev/null +++ b/src/func.rs @@ -0,0 +1,52 @@ +use core::arch::x86_64::_mm_clflush; +use core::arch::x86_64::_mm_mfence; +use core::mem::transmute; +use iced_x86::{Code, Instruction}; +use libc::{mprotect, PROT_EXEC, PROT_READ, PROT_WRITE}; + +use crate::sync::RelaxedMutex; + +static LOCK: RelaxedMutex<()> = RelaxedMutex::new(()); + +pub fn function_copy_plain(src: extern "C" fn(), dest: extern "C" fn()) { + unsafe { + let src_ptr: *const u8 = transmute(src); + let dest_ptr: *mut u8 = transmute(dest); + function_copy_inner(src_ptr, dest_ptr) + } +} + +pub fn function_copy(src: extern "C" fn(Arg) -> Ret, dest: extern "C" fn(Arg) -> Ret) { + unsafe { + let src_ptr: *const u8 = transmute(src); + let dest_ptr: *mut u8 = transmute(dest); + function_copy_inner(src_ptr, dest_ptr) + } +} + +pub(self) fn function_copy_inner(src: *const u8, dest: *mut u8) { + let locked = LOCK.lock(); + unsafe { + let mut assembler = iced_x86::Encoder::new(64); + let inst = Instruction::with_branch(Code::Jmp_rel32_64, src.addr() as u64).unwrap(); + + let _ = assembler.encode(&inst, dest.addr() as u64); + let buf = assembler.take_buffer(); + + let dest_page = dest.map_addr(|a| a & !(4096 - 1)); + let dest_page_diff = dest.addr() - dest_page.addr(); + + let new_len = buf.len(); + + let len = (new_len + dest_page_diff).next_multiple_of(4096); + + mprotect(dest_page.cast(), len, PROT_EXEC | PROT_WRITE | PROT_READ); + core::ptr::copy_nonoverlapping(buf.as_ptr(), dest, new_len); + mprotect(dest_page.cast(), len, PROT_EXEC | PROT_READ); + + _mm_clflush(dest); + _mm_clflush(dest.byte_add(64)); + _mm_mfence(); + } + drop(locked); +} diff --git a/src/lib.rs b/src/lib.rs index 25d7141..45e3d56 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,10 +20,16 @@ pub mod uninit; #[cfg(all(target_os = "linux", target_arch = "x86_64"))] pub mod backdoor; +#[cfg(all(target_arch = "x86_64"))] +pub mod func; pub mod prelude { pub use crate::aliasing::*; + #[cfg(all(target_os = "linux", target_arch = "x86_64"))] + pub use crate::backdoor::*; pub use crate::buffer::*; + #[cfg(all(target_os = "linux", target_arch = "x86_64"))] + pub use crate::func; pub use crate::global::*; pub use crate::lifetime::*; pub use crate::memory::*; @@ -33,7 +39,4 @@ pub mod prelude { pub use crate::sync::*; pub use crate::transmute::*; pub use crate::uninit::*; - pub use crate::sync::*; - #[cfg(all(target_os = "linux", target_arch = "x86_64"))] - pub use crate::backdoor::*; } diff --git a/src/memory.rs b/src/memory.rs index 50e69e2..49d25be 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -2,7 +2,7 @@ use alloc::{ alloc::{alloc, dealloc}, boxed::Box, }; -use core::{alloc::Layout, ptr}; +use core::{alloc::Layout, ptr, usize}; pub struct Dangling { ptr: *mut T, @@ -94,6 +94,14 @@ impl ArbitraryAccess { } } +pub fn infinite_slice_mut<'a, T>(ptr: *mut T) -> &'a mut [T] { + unsafe { core::slice::from_raw_parts_mut(ptr, isize::MAX as usize) } +} + +pub fn infinite_slice<'a, T>(ptr: *const T) -> &'a [T] { + unsafe { core::slice::from_raw_parts(ptr, isize::MAX as usize) } +} + #[cfg(all(target_os = "linux", target_arch = "x86_64"))] pub fn remove_segfaults() { unsafe { @@ -115,6 +123,8 @@ pub fn remove_segfaults() { context: *mut libc::ucontext_t, ) { unsafe { + use iced_x86::{Code, Decoder, DecoderOptions}; + let ctx = &mut *context; let info = &mut *info; let fault_addr = info.si_addr() as usize; @@ -135,13 +145,15 @@ pub fn remove_segfaults() { let old_rip = ctx.uc_mcontext.gregs[libc::REG_RIP as usize] as usize; let array = core::slice::from_raw_parts(old_rip as *mut u8, 15); - let mut out = lde::X64.iter(array, old_rip as u64); - let Some((opcode, _)) = out.next() else { + let mut decoder = Decoder::new(64, &array, DecoderOptions::NONE); + let inst = decoder.decode(); + + if inst.code() == Code::INVALID { libc::abort(); - }; + } - ctx.uc_mcontext.gregs[libc::REG_RIP as usize] += opcode.len() as i64; + ctx.uc_mcontext.gregs[libc::REG_RIP as usize] += decoder.position() as i64; } } } diff --git a/src/pin.rs b/src/pin.rs index c447132..a82b9d1 100644 --- a/src/pin.rs +++ b/src/pin.rs @@ -1,5 +1,5 @@ -use core::pin::Pin; use core::ops::{Deref, DerefMut}; +use core::pin::Pin; pub fn unpin_mut(pinned: Pin<&mut T>) -> &mut T { unsafe { Pin::get_unchecked_mut(pinned) } @@ -10,18 +10,11 @@ pub fn move_pinned(pinned: Pin<&mut T>) -> T { } pub fn swap_pinned(a: Pin<&mut T>, b: Pin<&mut T>) { - unsafe { - core::ptr::swap( - Pin::get_unchecked_mut(a), - Pin::get_unchecked_mut(b), - ) - } + unsafe { core::ptr::swap(Pin::get_unchecked_mut(a), Pin::get_unchecked_mut(b)) } } pub fn replace_pinned(pinned: Pin<&mut T>, value: T) -> T { - unsafe { - core::mem::replace(Pin::get_unchecked_mut(pinned), value) - } + unsafe { core::mem::replace(Pin::get_unchecked_mut(pinned), value) } } pub fn take_pinned(pinned: Pin<&mut T>) -> T { @@ -135,11 +128,17 @@ pub fn repin(pinned: Pin<&mut T>) -> Pin<&mut T> { } pub trait Moveable { - fn move_out(self: Pin<&mut Self>) -> Self where Self: Sized { + fn move_out(self: Pin<&mut Self>) -> Self + where + Self: Sized, + { move_pinned(self) } - fn unpin_mut(self: Pin<&mut Self>) -> &mut Self where Self: Sized { + fn unpin_mut(self: Pin<&mut Self>) -> &mut Self + where + Self: Sized, + { unpin_mut(self) } }