emd_common/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3// - STD
4#[cfg(feature = "std")]
5use std::{
6    ops::Range,
7    path::Path,
8    fs::File,
9    io::{Result, BufReader, BufRead, Error, ErrorKind},
10};
11
12// This file exists to enable the library target.
13pub const BUFFER_SIZE: usize = 256; // 512 bytes is the maximum allowed stack size of an ebpf program. We won't reach this.
14pub const QUEUE_SIZE: u32 = 64;
15pub const MAX_QUEUE_SIZE: usize = QUEUE_SIZE as usize * BUFFER_SIZE;
16
17// IOMEM
18pub const PROC_IOMEM: &str = "/proc/iomem";
19pub const SEPARATOR_SYSTEM_RAM: &str = " : System RAM";
20pub const SEPARATOR_HYPHEN: char = '-';
21
22// KALLSYMS
23pub const PROC_KALLSYMS: &str = "/proc/kallsyms";
24pub const PAGE_OFFSET_BASE: &str = "page_offset_base";
25
26// self exe
27pub const PROC_SELF_EXE: &str = "/proc/self/exe";
28
29// eBPF fns
30pub const READ_KERNEL_MEM: &str = "read_kernel_memory";
31
32// - Errors
33pub const ERROR_DUMP_MEMORY_IOMEM_SEPARATE_KEY_VAL_MAP: &str = "There is no left side in key/value pair";
34pub const ERROR_DUMP_MEMORY_IOMEM_CAPSYSADM: &str = "Need CAP_SYS_ADMIN to read /proc/iomem";
35pub const ERROR_PATH_READ_SYS: &str = "An error occured while trying to read necessary data from /sys";
36
37pub fn calc_queue_elements(dump_size: usize) -> usize {
38    if dump_size % BUFFER_SIZE == 0 {
39            dump_size / BUFFER_SIZE
40    } else {
41        dump_size / BUFFER_SIZE + 1
42    }
43}
44
45/// This method results you the current map of the system's memory for each physical device. Normally, could be found at
46/// /proc/iomem. 
47/// # Example output
48///As following you can see an  **partial** example output of the content of file **/proc/iomem**:
49/// ```bash
50/// 00000000-00000fff : Reserved
51/// 00001000-0009efff : System RAM
52/// 0009f000-000fffff : Reserved
53///   000a0000-000bffff : PCI Bus 0000:00
54///   000f0000-000fffff : System ROM
55/// 00100000-5894bfff : System RAM
56/// 5894c000-589c6fff : Reserved
57/// 589c7000-5e068fff : System RAM
58/// 5e069000-5e968fff : Reserved
59/// 5e969000-6cb7efff : System RAM
60/// 6cb7f000-6cf4efff : Unknown E820 type
61/// 6cf4f000-6ed6efff : Reserved
62/// 6ed6f000-6fbcefff : ACPI Non-volatile Storage
63/// 6fbcf000-6fc4efff : ACPI Tables
64/// 6fc4f000-6fc4ffff : System RAM
65/// 6fc50000-7d7fffff : Reserved
66///   70200000-75f7ffff : INT0E0C:00
67///   79800000-7d7fffff : Graphics Stolen Memory
68/// 7d800000-dfffffff : PCI Bus 0000:00
69/// ```
70///The first column displays the memory registers used by each of the different types of memory. The second column
71///lists the kind of memory located within those registers and displays which memory registers are used by the kernel
72///within the system RAM or, if the network interface card has multiple Ethernet ports, the memory registers assigned
73///for each port.
74///This method results you the entries as a Vec<std::ops::Range<u64>> for the given identifier (e.g. "System RAM").
75#[cfg(feature = "std")]
76pub fn extract_mem_range<I: Into<String>>(identifier: I) -> Result<Vec<Range<u64>>> {
77    let identifier = identifier.into();
78    let path = Path::new(PROC_IOMEM);
79    let file = File::open(path)?;
80    let reader = BufReader::new(file);
81    let mut ranges = Vec::new();
82
83    for line in reader.lines() {
84        let line = line?;
85        if line.contains(&identifier) {
86            if let Some((start, end)) = parse_memory_range(&line) {
87                ranges.push(start..(end + 1));
88            }
89        }
90    }
91
92    Ok(ranges)
93}
94
95#[cfg(feature = "std")]
96pub fn get_page_offset_base_address() -> Result<u64>{
97    let path = Path::new(PROC_KALLSYMS);
98    let file = File::open(path)?;
99    let reader = BufReader::new(file);
100    for line in reader.lines() {
101        let line = line?;
102        if line.contains(PAGE_OFFSET_BASE) {
103            let parts: Vec<&str> = line.split_whitespace().collect();
104            if let Some(offset_str) = parts.first() {
105                return u64::from_str_radix(offset_str, 16)
106                    .map_err(|e| Error::new(ErrorKind::InvalidData, e))
107            }
108        }
109    }
110    // if no page offset base is found, KASLR is disabled...?
111    Ok(0)
112}
113
114#[cfg(feature = "std")]
115fn parse_memory_range(line: &str) -> Option<(u64, u64)> {
116    let parts: Vec<&str> = line.split_whitespace().collect();
117    if let Some(range_part) = parts.first() {
118        let bounds: Vec<&str> = range_part.split(SEPARATOR_HYPHEN).collect();
119        if bounds.len() == 2 {
120            if let (Ok(start), Ok(end)) = (u64::from_str_radix(bounds[0], 16), u64::from_str_radix(bounds[1], 16)) {
121                return Some((start, end));
122            }
123        }
124    }
125    None
126}