aboutsummaryrefslogtreecommitdiff
path: root/src/lib.rs
diff options
context:
space:
mode:
authorJohannes Stoelp <johannes.stoelp@gmail.com>2021-08-08 16:50:14 +0200
committerJohannes Stoelp <johannes.stoelp@gmail.com>2021-08-08 16:50:14 +0200
commiteda0273c1b36b4959e5c61e3d209029e1d8de088 (patch)
treea879bfad2454b16240852ec518e50b207cf06350 /src/lib.rs
downloadvdso-proxy-poc-eda0273c1b36b4959e5c61e3d209029e1d8de088.tar.gz
vdso-proxy-poc-eda0273c1b36b4959e5c61e3d209029e1d8de088.zip
add vdso proxy poc
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs184
1 files changed, 184 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..18c1b74
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,184 @@
+//! Collection of some abstractions and the error definition used by this PoC.
+
+/// Errors that can occur in this PoC.
+#[derive(Debug)]
+pub enum Error {
+ /// Failed to open and read the `/proc/self/maps` file.
+ FailedToReadMaps,
+ /// Failed to a parse line from the `/proc/self/maps` file.
+ /// Captures line that failed to parse.
+ ParseMapEntryError(String),
+ /// No `[vdso]` segment found in `/proc/self/maps`.
+ VdsoSegmentNotFound,
+ /// Failed to parse bytes as ELF file.
+ FailedToParseAsElf,
+ /// No `PT_LOAD` program header found in the ELF file.
+ LoadPhdrNotFound,
+ /// Requested symbol not found in the ELF file.
+ /// Captures the name of the requested symbol.
+ SymbolNotFound(String),
+}
+
+/// Representation of an 64-bit virtual address.
+#[derive(Debug, Copy, Clone)]
+pub struct VirtAddr(pub u64);
+
+/// Represents an entry from the `/proc/self/maps` file with the information needed by this PoC.
+/// ```text
+/// man 5 proc
+/// address perms offset dev inode pathname
+/// 00400000-00452000 r-xp 00000000 08:02 173521 /usr/bin/dbus-daemon
+/// ```
+#[derive(Debug)]
+pub struct MapEntry {
+ /// Start address of the memory segment.
+ pub addr: u64,
+ /// Length of the memory segment.
+ pub len: u64,
+ /// Optional name of the memory segment.
+ pub name: Option<String>,
+}
+
+impl MapEntry {
+ /// Try to parse a [`MapEntry`] from the `line` passed as argument.
+ pub fn from_line<'a>(line: &'a str) -> Result<MapEntry, Error> {
+ let expect = |tok: Option<&'a str>| tok.ok_or(Error::ParseMapEntryError(line.into()));
+
+ // Tokenize the line.
+ let mut toks = line.split_whitespace();
+ let addr = expect(toks.next())?;
+ let _perms = expect(toks.next())?;
+ let _offset = expect(toks.next())?;
+ let _dev = expect(toks.next())?;
+ let _inode = expect(toks.next())?;
+ let name = toks.next().map(|name| String::from(name));
+
+ // Parse the address token.
+ let (addr, len) = {
+ let tou64 = |s: &'a str| {
+ u64::from_str_radix(s, 16)
+ .map_err(|e| Error::ParseMapEntryError(format!("{}\n{}", line, e)))
+ };
+
+ let mut toks = addr.split('-');
+ let start = tou64(expect(toks.next())?)?;
+ let end = tou64(expect(toks.next())?)?;
+
+ (start, end - start)
+ };
+
+ Ok(MapEntry { addr, len, name })
+ }
+}
+
+/// Owned [`libc::mmap`] allocation.
+pub struct Mmap {
+ ptr: *mut libc::c_void,
+ len: usize,
+ map: MapEntry,
+}
+
+impl Mmap {
+ /// Create a new allocation with `read | write | execute` permissions big enough to hold a copy
+ /// of `bytes` and initialize it with the `bytes` passed as argument.
+ pub fn new_rwx_from(bytes: &[u8]) -> Option<Mmap> {
+ use libc::{
+ memcpy, mmap, sysconf, MAP_ANONYMOUS, MAP_FAILED, MAP_PRIVATE, PROT_EXEC, PROT_READ,
+ PROT_WRITE, _SC_PAGESIZE,
+ };
+
+ // Get the page size.
+ let page_size = unsafe { sysconf(_SC_PAGESIZE) } as usize;
+
+ // Compute required size for the new allocation by rounding up to the next page size.
+ let len = ((bytes.len() + page_size - 1) / page_size) * page_size;
+
+ // Allocate new `rwx` memory segment.
+ let ptr = unsafe {
+ mmap(
+ std::ptr::null_mut(),
+ len,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1, /* fd */
+ 0, /* offset */
+ )
+ };
+
+ if ptr == MAP_FAILED {
+ return None;
+ }
+
+ unsafe {
+ // Initialize new allocation with `bytes` passed as argument.
+ memcpy(ptr, bytes.as_ptr().cast(), bytes.len());
+ }
+
+ Some(Mmap {
+ ptr,
+ len,
+ map: MapEntry {
+ addr: ptr as u64,
+ len: len as u64,
+ name: Some("mmap_rwx".into()),
+ },
+ })
+ }
+}
+
+impl Drop for Mmap {
+ fn drop(&mut self) {
+ unsafe { libc::munmap(self.ptr, self.len) };
+ }
+}
+
+impl AsRef<MapEntry> for Mmap {
+ fn as_ref(&self) -> &'_ MapEntry {
+ &self.map
+ }
+}
+
+impl AsMut<[u8]> for Mmap {
+ fn as_mut(&mut self) -> &mut [u8] {
+ unsafe { std::slice::from_raw_parts_mut(self.ptr.cast(), self.len) }
+ }
+}
+
+/// An `x86_64` jump pad (trampoline) that that can be installed at a [`VirtAddr`].
+///
+/// The jump pad is implemented as:
+/// ```asm
+/// mov rax, imm64 ; target
+/// jmp rax
+/// ```
+#[allow(dead_code)]
+#[cfg(target_arch = "x86_64")]
+#[repr(packed)]
+pub struct JmpPad {
+ movabs: u16,
+ target: u64,
+ jmp_rax: u16,
+}
+
+#[cfg(target_arch = "x86_64")]
+impl JmpPad {
+ /// Initialize a new jump pad to the destination virtual address `target`.
+ /// This does not install the jump pad.
+ pub fn to(target: VirtAddr) -> JmpPad {
+ JmpPad {
+ movabs: 0xb848, // REX.W + mov rax, imm64
+ target: target.0,
+ jmp_rax: 0xe0ff, // jmp rax
+ }
+ }
+
+ /// Install the jump pad at the virtual address `addr`.
+ ///
+ /// # Safety
+ /// The caller must guarantee the following constraints:
+ /// - `addr` must be a valid virtual address referring to writeable memory.
+ /// - There must be enough space to store [`size_of::<JmpPad>()`](core::mem::size_of) bytes.
+ pub unsafe fn install_at(self, addr: VirtAddr) {
+ std::ptr::write(addr.0 as *mut JmpPad, self);
+ }
+}