vmi_core/os/
common.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
use serde::{Deserialize, Serialize};

use crate::{MemoryAccess, Pa, Va, VmiError};

/// A process object within a system.
///
/// Equivalent to `EPROCESS*` on Windows or `task_struct*` on Linux.
#[derive(
    Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
)]
pub struct ProcessObject(pub Va);

impl ProcessObject {
    /// Checks if the process object is a null reference.
    pub fn is_null(&self) -> bool {
        self.0 .0 == 0
    }

    /// Converts the process object to a 64-bit unsigned integer.
    pub fn to_u64(&self) -> u64 {
        self.0 .0
    }
}

impl From<Va> for ProcessObject {
    fn from(va: Va) -> Self {
        Self(va)
    }
}

impl From<ProcessObject> for Va {
    fn from(value: ProcessObject) -> Self {
        value.0
    }
}

impl std::fmt::Display for ProcessObject {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// A thread object within a system.
///
/// Equivalent to `ETHREAD*` on Windows.
#[derive(
    Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
)]
pub struct ThreadObject(pub Va);

impl ThreadObject {
    /// Checks if the thread object is a null reference.
    pub fn is_null(&self) -> bool {
        self.0 .0 == 0
    }

    /// Converts the thread object to a 64-bit unsigned integer.
    pub fn to_u64(&self) -> u64 {
        self.0 .0
    }
}

impl From<Va> for ThreadObject {
    fn from(va: Va) -> Self {
        Self(va)
    }
}

impl From<ThreadObject> for Va {
    fn from(value: ThreadObject) -> Self {
        value.0
    }
}

impl std::fmt::Display for ThreadObject {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// A process ID within a system.
#[derive(
    Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
)]
pub struct ProcessId(pub u32);

impl From<u32> for ProcessId {
    fn from(value: u32) -> Self {
        Self(value)
    }
}

impl From<ProcessId> for u32 {
    fn from(value: ProcessId) -> Self {
        value.0
    }
}

impl std::fmt::Display for ProcessId {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// A thread ID within a system.
#[derive(
    Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
)]
pub struct ThreadId(pub u32);

impl From<u32> for ThreadId {
    fn from(value: u32) -> Self {
        Self(value)
    }
}

impl From<ThreadId> for u32 {
    fn from(value: ThreadId) -> Self {
        value.0
    }
}

impl std::fmt::Display for ThreadId {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// The architecture of the operating system.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum OsArchitecture {
    /// The architecture is unknown.
    Unknown,

    /// The x86 architecture.
    X86,

    /// The x86-64 architecture.
    Amd64,
}

/// Represents information about a process in the target system.
#[derive(Debug, Serialize, Deserialize)]
pub struct OsProcess {
    /// The PID of the process.
    pub id: ProcessId,

    /// The process object.
    pub object: ProcessObject,

    /// The short name of the process.
    ///
    /// # Platform-specific
    ///
    /// - **Windows**: `_EPROCESS::ImageFileName` (limited to 16 characters).
    /// - **Linux**: `_task_struct::comm` (limited to 16 characters).
    pub name: String,

    /// The translation root of the process.
    pub translation_root: Pa,
}

/// A region of memory within a process.
#[derive(Debug, Serialize, Deserialize)]
pub struct OsRegion {
    /// The start address of the region.
    pub start: Va,

    /// The end address of the region.
    pub end: Va,

    /// The protection flags of the region.
    pub protection: MemoryAccess,

    /// The kind of memory region.
    pub kind: OsRegionKind,
}

/// Specifies the kind of memory region.
#[derive(Debug, Serialize, Deserialize)]
pub enum OsRegionKind {
    /// A private region of memory.
    ///
    /// Such regions are usually created by functions like `VirtualAlloc` on
    /// Windows.
    Private,

    /// A mapped region of memory. Might be backed by a file.
    ///
    /// Such regions are usually created by functions like `MapViewOfFile` on
    /// Windows.
    Mapped(OsMapped),
}

/// Contains information about a mapped memory region.
#[derive(Debug, Serialize, Deserialize)]
pub struct OsMapped {
    /// The path to the file backing the region.
    ///
    /// This field is represented as a [`Result<Option<String>, VmiError>`] to
    /// handle cases where the path is not available (e.g., due to a page
    /// fault).
    #[serde(with = "serde_result_option")]
    pub path: Result<Option<String>, VmiError>,
}

/// An exported symbol from an image (e.g., DLL or .so file).
#[derive(Debug, Serialize, Deserialize)]
pub struct OsImageExportedSymbol {
    /// The name of the symbol.
    pub name: String,

    /// The virtual address of the symbol.
    pub address: Va,
}

/// Custom serialization module for [`Result<Option<String>, VmiError>`].
///
/// Provides custom serialization and deserialization logic for handling
/// the path field in [`OsMapped`], which may be unavailable due to paging
/// issues.
mod serde_result_option {
    use crate::VmiError;

    pub fn serialize<S>(
        value: &Result<Option<String>, VmiError>,
        serializer: S,
    ) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        match value {
            Ok(Some(value)) => serializer.serialize_some(value),
            _ => serializer.serialize_none(),
        }
    }

    pub fn deserialize<'de, D>(
        deserializer: D,
    ) -> Result<Result<Option<String>, VmiError>, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        use serde::Deserialize as _;

        let value = Option::deserialize(deserializer)?;
        match value {
            Some(value) => Ok(Ok(Some(value))),
            None => Ok(Err(VmiError::Other("PageFault"))),
        }
    }
}