Skip to content

Commit

Permalink
Read vDSO from auxiliary vector
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidVentura committed Jan 4, 2024
1 parent 16798e3 commit a9ca342
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 104 deletions.
51 changes: 51 additions & 0 deletions src/auxv.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use std::error::Error;

#[derive(Debug)]
pub struct AuxVecValues {
pub(crate) vdso_base: usize,
pub(crate) page_size: usize,
}

extern "C" {
static environ: *const *const u8;
}

unsafe fn get_auxv_ptr() -> *const usize {
// the auxiliary vector is right behind the environment variables, which
// is an array of strings, delimited by a nullpointer.
let mut env_entry_ptr = environ;

while !(*env_entry_ptr).is_null() {
env_entry_ptr = env_entry_ptr.offset(1);
}

env_entry_ptr = env_entry_ptr.offset(1);

return std::mem::transmute::<*const *const u8, *const usize>(env_entry_ptr);
}

pub(crate) fn read_aux_vec() -> Result<AuxVecValues, Box<dyn Error>> {
// The auxiliary vector is an array of key:value tuples, represented as [usize, usize]
// The end is delimited by having the key == AT_NULL
let mut out = unsafe { get_auxv_ptr() };
let mut ptr = 0;
let mut pagesize = 0;
unsafe {
while *out != libc::AT_NULL as usize {
let key = *out;
let val = *out.offset(1);
if key == libc::AT_SYSINFO_EHDR as usize {
ptr = val;
}
if key == libc::AT_PAGESZ as usize {
pagesize = val;
}
out = out.offset(2);
}
}
if ptr == 0 || pagesize == 0 {
panic!("wtf");
}
Ok(AuxVecValues {vdso_base: ptr, page_size: pagesize})
}

4 changes: 2 additions & 2 deletions src/bin.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{error::Error, time::SystemTime};

use tpom::{vdso, Kind, TVDSOFun, Time, TimeSpec, TimeVal};
use tpom::{vdso, Kind, Time, TimeSpec, TimeVal, TVDSOFun};

extern crate tpom;

Expand All @@ -25,7 +25,7 @@ fn my_time() -> Time {
pub fn main() -> Result<(), Box<dyn Error>> {
println!("Now: {:?}", SystemTime::now());
println!("Executing");
let v = vdso::vDSO::open()?;
let v = vdso::vDSO::read()?;
let og = v.entry(Kind::GetTime).ok_or("Could not find clock")?;
let backup = og.overwrite(myclock);
println!("Done, Now: {:?}, restoring", SystemTime::now());
Expand Down
10 changes: 2 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
//! }
//! }
//!
//! let v = vdso::vDSO::open().unwrap();
//! let v = vdso::vDSO::read().unwrap();
//! let og = v.entry(Kind::GetTime).ok_or("Could not find clock").unwrap();
//! let backup = og.overwrite(myclock);
//!
Expand All @@ -43,17 +43,11 @@
mod opcodes;
pub(crate) mod trampolines;
pub mod vdso;
pub mod auxv;

use crate::trampolines::*;
use crate::vdso::vDSO;

#[derive(Debug, Clone, Copy, PartialEq)]
struct Range {
start: usize,
end: usize,
writable: bool,
}

pub type Time = libc::time_t; // as libc::time_t

/// Return type for `ClockGetTime` and `ClockGetRes`; maps to
Expand Down
25 changes: 0 additions & 25 deletions src/test_files/proc/self/maps

This file was deleted.

104 changes: 36 additions & 68 deletions src/vdso.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,43 @@
use crate::Range;
use crate::*;
use goblin::elf::*;
use goblin::strtab::Strtab;
use core::slice;
use std::error::Error;
use std::fs::{self, File};
use std::os::unix::prelude::FileExt;
use std::fs;

#[derive(Debug, PartialEq)]
pub(crate) struct DynSym {
pub(crate) name: String,
pub(crate) address: usize,
pub(crate) size: usize,
}

#[allow(non_camel_case_types)]
#[derive(PartialEq, Clone, Debug)]
#[derive(Debug)]
pub struct vDSO {
range: Range,
avv: auxv::AuxVecValues,
data: Vec<u8>,
}

#[cfg(target_pointer_width="32")]
const ELF_HDR_SIZE: usize = 52;

#[cfg(target_pointer_width="64")]
const ELF_HDR_SIZE: usize = 64;

impl vDSO {
pub(crate) fn read(range: &Range) -> Vec<u8> {
let mut buf = vec![0; range.end - range.start];
let f = File::open("/proc/self/mem").unwrap();
f.read_at(&mut buf, range.start as u64).unwrap();
drop(f);
buf
pub fn read() -> Result<vDSO, Box<dyn Error>> {
let auxvec = auxv::read_aux_vec()?;

// As the size of the vDSO is unknown, read first only the header which has constant size
let header_bytes: &[u8] = unsafe { slice::from_raw_parts(&*(auxvec.vdso_base as *const u8), ELF_HDR_SIZE) };
let bare_header = Elf::parse_header(&header_bytes).unwrap();
// Having parsed the header, we can now calculate the len of the vDSO
let vdso_len = usize::from(bare_header.e_shnum * bare_header.e_shentsize) + (bare_header.e_shoff as usize);
// And with the len, we can read the right amount
let vdso_bytes = unsafe { slice::from_raw_parts(&*(auxvec.vdso_base as *const u8), vdso_len) };

Ok(vDSO {data: vdso_bytes.into(), avv: auxvec })
}

pub(crate) fn change_mode(&self, write: bool) {
Expand All @@ -34,49 +46,19 @@ impl vDSO {
} else {
libc::PROT_EXEC | libc::PROT_READ
};
// As we need to mprotect() the vDSO and that can only be done in full pages, we need
// to bump the vDSO length to the next page
let vdso_size_page_aligned = (self.data.len() + self.avv.page_size-1) & !(self.avv.page_size-1);
unsafe {

libc::mprotect(
self.range.start as *mut libc::c_void,
self.range.end - self.range.start,
self.avv.vdso_base as *mut libc::c_void,
vdso_size_page_aligned,
mode,
);
}
}
fn parse_mem_map(path: Option<&str>) -> Result<Range, Box<dyn Error>> {
// could use getauxval(AT_SYSINFO_EHDR)
// but calculating the length is complicated, and i'm not really sure how to
// pass a pointer to memory as a &[u8], without specifying length

let data = fs::read_to_string(path.unwrap_or("/proc/self/maps"))?;

for line in data.lines() {
if !line.contains("[vdso]") {
continue;
}
let (range, _) = line.split_once(' ').unwrap();
let (start, end) = range.split_once('-').unwrap();
let parts: Vec<&str> = line.split_whitespace().collect();
let perms = parts[1];
return Ok(Range {
start: usize::from_str_radix(start, 16).unwrap(),
end: usize::from_str_radix(end, 16).unwrap(),
writable: perms.contains('w'),
});
}
Err("No vDSO mapped in memory range. Cannot continue".into())
}

pub fn open() -> Result<Self, Box<dyn Error>> {
vDSO::open_at(None)
}

pub fn open_at(path: Option<&str>) -> Result<Self, Box<dyn Error>> {
let r = vDSO::parse_mem_map(path)?;
Ok(vDSO {
range: r,
data: vDSO::read(&r),
})
}
pub(crate) fn dynsyms(&self) -> Vec<DynSym> {
let r = Elf::parse(&self.data).expect("bad elf");

Expand Down Expand Up @@ -123,7 +105,7 @@ impl vDSO {
/// Overwrites the process' vDSO memory at offset `symbol_address` with `opcodes`.
/// It is the caller's responsibility to provide the correct amount of data.
pub(crate) fn overwrite(&self, symbol_address: usize, opcodes: &[u8]) {
let dst_addr = self.range.start + symbol_address;
let dst_addr = self.avv.vdso_base + symbol_address;
self.change_mode(true);
for (i, b) in opcodes.iter().enumerate() {
unsafe {
Expand Down Expand Up @@ -187,27 +169,14 @@ fn get_str_til_nul(s: &Strtab, at: usize) -> String {
mod tests {
use super::*;

#[test]
fn test_parse_proc_self_maps() {
let parsed = vDSO::parse_mem_map(Some("src/test_files/proc/self/maps"));
let expected = Range {
start: 0x7fff37953000,
end: 0x7fff37955000,
writable: false,
};
assert_eq!(parsed.is_ok(), true);
assert_eq!(parsed.unwrap(), expected);
}

#[test]
fn test_dynsyms() {
let test_vdso =
fs::read("src/test_files/test_vdso_elf_1").expect("Unable to read test file");
let a = vDSO {
range: Range {
start: 0,
end: 0,
writable: true,
avv: auxv::AuxVecValues {
vdso_base: 0,
page_size: 0x1000,
},
data: test_vdso,
};
Expand Down Expand Up @@ -276,10 +245,9 @@ mod tests {
let test_vdso =
fs::read("src/test_files/test_vdso_elf_2").expect("Unable to read test file");
let a = vDSO {
range: Range {
start: 0,
end: 0,
writable: true,
avv: auxv::AuxVecValues {
vdso_base: 0,
page_size: 0x1000,
},
data: test_vdso,
};
Expand Down
2 changes: 1 addition & 1 deletion tests/pub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ mod tests {
#[test]
#[serial]
fn it_freezes_system_clock() {
let v = vdso::vDSO::open().unwrap();
let v = vdso::vDSO::read().unwrap();
let og = v
.entry(Kind::GetTime)
.ok_or("Could not find clock")
Expand Down

0 comments on commit a9ca342

Please sign in to comment.