Skip to content

Commit

Permalink
merge feat/confloader to dev/0.0.1 (#9)
Browse files Browse the repository at this point in the history
Co-authored-by: std3 <67806187+standard3@users.noreply.github.com>
  • Loading branch information
Esgr0bar and standard3 committed Jun 19, 2024
1 parent 7214c48 commit 38efd7d
Show file tree
Hide file tree
Showing 12 changed files with 452 additions and 8 deletions.
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ keywords = ["forensics", "memory forensics"]
exclude = [".github", "CONTRIBUTING.md", "CODE_OF_CONDUCT.md", "assets"]
license = "GPL-3.0"

authors = ["Théo Abel <theo.abel53@gmail.com>"] # todo: add other contributors
authors = ["Théo Abel <theo.abel53@gmail.com>", "Nathan Dandrimont <nathan.dandrimont@ecole2600.com>"] # todo: add other contributors

[dependencies]
serde = { version = "1.0", features = ["derive"] }
toml = "0.8.12"
anyhow = "1.0"
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,26 @@ todo
- Add support for Binary Code Analysis with `miasm`

```rust
use anyhow::Result;
use libmmu::architectures::{ RiscV, RiscVMMUMode };
use libmmu::utils::{ MemorySpace, SpaceType, MachineConfig };

fn main() {
fn main() -> Result<()> {
let dumpfile = ...;
let outfolder = ...;

let memspaces = MemorySpace::new()
.add(SpaceType::RAM, 0x0000000080000000, 0x000000017fffffff)
.add(SpaceType::ROM, 0x0000000000000000, 0x0000000000011fff);

let conf = MachineConfig::<RiscV>::new()
.dumpfile("dump.raw")
.mmu(RiscVMMUMode::SV39)
.memspaces(memspaces)
.outfile("output");
let machine = Machine::new(
MachineType::RiscV(MMUMode:SV32),
memspaces,
dumpfile,
outfolder
)?;

conf.resolve_spaces()
machine.resolve_spaces()?;
}
```

Expand Down
2 changes: 2 additions & 0 deletions src/architecture.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod generic;
pub mod riscv;
170 changes: 170 additions & 0 deletions src/architecture/generic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::{hash, path::PathBuf};

use super::riscv::MMUMode as RiscVMMUMode;

/// Enumerates types of memory regions.
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum MemoryRegionType {
#[default]
RAM,
ROM,
}

/// Represents a memory region with a start and end address.
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct MemoryRegion {
pub region_type: MemoryRegionType,
pub start_address: u64,
pub end_address: u64,
}

impl MemoryRegion {
pub fn new(
region_type: MemoryRegionType,
start_address: u64,
end_address: u64,
) -> Result<Self> {
// Check that addresses are valid memory addresses
if start_address >= end_address {
return Err(anyhow::anyhow!(
"Invalid memory region, start address is greater than or equal to end address"
));
}

Ok(Self {
region_type,
start_address,
end_address,
})
}

/// Returns the size of the memory region.
pub fn size(&self) -> u64 {
self.end_address - self.start_address
}

/// Returns true if the memory region contains the address.
/// A memory region contains an address if the address is greater than or equal to the start address and less than the end address.
pub fn contains(&self, address: u64) -> bool {
self.start_address <= address && address < self.end_address
}

/// Returns true if the two memory regions are overlapping.
/// Two memory regions are overlapping if the start address of one region is less than the end address of the other region.
pub fn is_overlapping(&self, other: &MemoryRegion) -> bool {
self.contains(other.start_address) || other.contains(self.start_address)
}

/// Returns true if the two memory regions are adjacent.
/// Two memory regions are adjacent if the end address of one region is equal to the start address of the other region.
pub fn is_adjacent(&self, other: &MemoryRegion) -> bool {
self.start_address == other.end_address || other.start_address == self.end_address
}
}

/// Represents a memory space with regions.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct MemorySpace {
pub regions: Vec<MemoryRegion>,
}

impl MemorySpace {
pub fn new() -> Self {
Self {
regions: Vec::new(),
}
}

pub fn add(&mut self, region: MemoryRegion) -> Result<&mut Self> {
// Check if the memory region is overlapping with another region
if self.regions.iter().any(|r| r.is_overlapping(&region)) {
return Err(anyhow::anyhow!(
"Memory region is overlapping with another region"
));
}

self.regions.push(region);
Ok(self)
}
}

/// Represents a CPU register with a value.
/// Depending on the architecture, the *validity* changes.
pub trait CPURegister {
type Value: hash::Hash + Eq + Copy + Default;

fn is_valid(&self) -> Result<Self::Value>;
}

/// Represents a page table entry with an address and flags.
/// It holds the mapping between a virtual address of a page and the address of a physical frame.
/// There is also auxiliary information about the page such as a present bit, a dirty or modified bit,
/// address space or process ID information, amongst others.
pub trait PageTableEntry {
type Address: hash::Hash + Eq + Copy + Default;
type Flags: hash::Hash + Eq + Copy + Default;

fn is_dirty(&self) -> bool;
fn is_accessed(&self) -> bool;
fn is_global(&self) -> bool;
fn is_readable(&self) -> bool;
fn is_writable(&self) -> bool;
fn is_executable(&self) -> bool;
}

/// Represents a page table with entries.
/// It is a data structure used in a virtual memory system to manage the mapping between virtual addresses and physical addresses.
/// It is used to translate virtual addresses to physical addresses and to manage the memory permissions of the pages.
/// It is also used to store additional information about the pages, such as the status of the page, the address space or process ID, amongst others.
pub trait PageTable {
type Entries: hash::Hash + Eq + Copy + Default + PageTableEntry;

// fn apply_on_entries(function: FnMut(PageTableEntry) -> Vec<?> ) -> ? // FIXME: to be defined, but is it necessary?
}

/// Enumerates types of supported machines.
/// This enum is used to specify the type of machine that is being parsed.
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MachineType {
RiscV(RiscVMMUMode),
}

impl Default for MachineType {
fn default() -> Self {
Self::RiscV(RiscVMMUMode::SV32)
}
}

/// Represents a machine with a type, MMU, CPU, memory regions, and an associated dump file.
/// It is used to store the machine's configuration, memory regions, and the dump file that is being used.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct Machine {
/// Type of the machine and its associated MMU mode.
pub machine_type: MachineType,
/// Memory regions of the machine.
pub memory_regions: MemorySpace,
/// Path to the dump file.
pub dumpfile: PathBuf,
/// Path to the output folder.
pub outfolder: PathBuf,
}

impl Machine {
pub fn new(
machine_type: MachineType,
memory_regions: MemorySpace,
dumpfile: PathBuf,
outfolder: PathBuf,
) -> Self {
// TODO: Validate each field

Self {
machine_type,
memory_regions,
dumpfile,
outfolder,
}
}
}
111 changes: 111 additions & 0 deletions src/architecture/riscv.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use super::generic::{CPURegister as CPURegisterTrait, PageTableEntry as PageTableEntryTrait};

use anyhow::Result;
use serde::{Deserialize, Serialize};

/// Represents a RISC-V CPU register associated with a value.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Default, Hash, Eq, PartialEq)]
pub struct CPURegister {
pub value: u64,
}

impl CPURegisterTrait for CPURegister {
type Value = u64;

fn is_valid(&self) -> Result<u64> {
todo!()
}
}

impl CPURegister {
pub fn new(value: u64) -> Self {
Self { value }
}
}

/// Represents a RISC-V page table entry.
/// It holds the mapping between a virtual address of a page and the address of a physical frame.
/// There is also auxiliary information about the page such as a present bit, a dirty or modified bit,
/// address space or process ID information, amongst others.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Hash, Eq, PartialEq)]
pub struct PageTableEntry {
pub address: u64,
pub flags: u64,
}

impl PageTableEntry {
pub fn new(address: u64, flags: u64) -> Self {
Self { address, flags }
}

pub fn is_supervisor(&self) -> bool {
todo!()
}
}

impl PageTableEntryTrait for PageTableEntry {
type Address = u64;
type Flags = u64;

// FIXME: Implement the following methods
fn is_dirty(&self) -> bool {
todo!()
}

fn is_accessed(&self) -> bool {
todo!()
}

fn is_global(&self) -> bool {
todo!()
}

fn is_readable(&self) -> bool {
todo!()
}

fn is_writable(&self) -> bool {
todo!()
}

fn is_executable(&self) -> bool {
todo!()
}
}

/// Enumerates RISC-V MMU modes.
/// The MMU modes are used to determine the number of bits used for virtual and physical addresses.
/// The modes are named after the number of bits used for the virtual address space.
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Hash, Eq, PartialEq, Default)]
pub enum MMUMode {
#[default]
SV32,
SV39,
SV48,
}

/// Represents a RISC-V CPU.
#[derive(Debug, Clone, Serialize, Deserialize, Default, Hash, Eq, PartialEq)]
pub struct CPU {
pub registers: Vec<CPURegister>,
}

impl CPU {
pub fn new() -> Self {
Self {
registers: Vec::new(),
}
}
}

/// Represents a RISC-V MMU.
#[derive(Debug, Clone, Serialize, Deserialize, Default, Hash, Eq, PartialEq)]
pub struct MMU {
pub mode: MMUMode,
}

impl MMU {
pub fn new(mode: MMUMode) -> Self {
Self { mode }
}
}
19 changes: 19 additions & 0 deletions src/configuration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use anyhow::Result;
use std::fs;
use std::path::Path;

use crate::utils::TryFromPath;

use super::architecture::generic::Machine;

/// Represents a configuration file for a machine.
/// It holds information about the machine's architecture, memory regions, and other relevant information.
/// The configuration file must be written in the TOML format.
impl TryFromPath for Machine {
fn from_path(path: &Path) -> Result<Self> {
let configuration = fs::read_to_string(path)?;
let machine: Machine = toml::from_str(&configuration)?;

Ok(machine)
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod architecture;
pub mod configuration;
pub mod utils;
28 changes: 28 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use anyhow::Result;
use std::path::{Path, PathBuf};

/// Try to convert from a path.
pub trait TryFromPath {
/// Converts from a path reference.
fn from_path(path: &Path) -> Result<Self>
where
Self: Sized;
}

/// Defines a trait for converting from a path.
pub trait FromPath {
/// Converts from a path reference.
fn from_path(path: &Path) -> Self;
}

impl FromPath for String {
fn from_path(path: &Path) -> String {
path.to_string_lossy().into_owned()
}
}

impl FromPath for PathBuf {
fn from_path(path: &Path) -> PathBuf {
path.to_path_buf()
}
}
Loading

0 comments on commit 38efd7d

Please sign in to comment.