vmi_core/ctx/
state.rs

1use isr_macros::Field;
2use zerocopy::{FromBytes, Immutable, IntoBytes};
3
4use super::session::VmiSession;
5use crate::{
6    AccessContext, AddressContext, Architecture, Pa, Registers as _, Va, VmiCore, VmiDriver,
7    VmiError,
8    os::{NoOS, VmiOs},
9};
10
11/// A VMI state.
12///
13/// The state combines access to a [`VmiSession`] with [`Architecture::Registers`]
14/// to provide unified access to VMI operations in the context of a specific
15/// virtual machine state.
16pub struct VmiState<'a, Driver, Os = NoOS>
17where
18    Driver: VmiDriver,
19    Os: VmiOs<Driver>,
20{
21    /// The VMI session.
22    session: VmiSession<'a, Driver, Os>,
23
24    /// The CPU registers associated with the current VM state.
25    registers: &'a <Driver::Architecture as Architecture>::Registers,
26}
27
28impl<Driver, Os> Clone for VmiState<'_, Driver, Os>
29where
30    Driver: VmiDriver,
31    Os: VmiOs<Driver>,
32{
33    fn clone(&self) -> Self {
34        *self
35    }
36}
37
38impl<Driver, Os> Copy for VmiState<'_, Driver, Os>
39where
40    Driver: VmiDriver,
41    Os: VmiOs<Driver>,
42{
43}
44
45impl<'a, Driver, Os> std::ops::Deref for VmiState<'a, Driver, Os>
46where
47    Driver: VmiDriver,
48    Os: VmiOs<Driver>,
49{
50    type Target = VmiSession<'a, Driver, Os>;
51
52    fn deref(&self) -> &Self::Target {
53        &self.session
54    }
55}
56
57impl<'a, Driver, Os> VmiState<'a, Driver, Os>
58where
59    Driver: VmiDriver,
60    Os: VmiOs<Driver>,
61{
62    /// Creates a new VMI state.
63    pub fn new(
64        session: &'a VmiSession<'a, Driver, Os>,
65        registers: &'a <Driver::Architecture as Architecture>::Registers,
66    ) -> Self {
67        Self {
68            session: *session,
69            registers,
70        }
71    }
72
73    /// Creates a new VMI state with the specified registers.
74    pub fn with_registers(
75        &'a self,
76        registers: &'a <Driver::Architecture as Architecture>::Registers,
77    ) -> Self {
78        Self {
79            session: self.session,
80            registers,
81        }
82    }
83
84    /// Creates a new VMI state without an OS-specific implementation.
85    pub fn without_os(&self) -> VmiState<'a, Driver, NoOS> {
86        VmiState {
87            session: self.session.without_os(),
88            registers: self.registers,
89        }
90    }
91
92    // Note that `core()` and `underlying_os()` are delegated to the `VmiSession`.
93
94    /// Returns the VMI session.
95    pub fn session(&self) -> &VmiSession<'a, Driver, Os> {
96        &self.session
97    }
98
99    /// Returns the CPU registers associated with the current event.
100    pub fn registers(&self) -> &'a <Driver::Architecture as Architecture>::Registers {
101        self.registers
102    }
103
104    /// Returns a wrapper providing access to OS-specific operations.
105    pub fn os(&self) -> VmiOsState<'a, Driver, Os> {
106        VmiOsState(*self)
107    }
108
109    /// Creates an address context for a given virtual address.
110    pub fn access_context(&self, address: Va) -> AccessContext {
111        self.registers().access_context(address)
112    }
113
114    /// Creates an address context for a given virtual address.
115    pub fn address_context(&self, address: Va) -> AddressContext {
116        self.registers().address_context(address)
117    }
118
119    /// Returns the physical address of the root of the current page table
120    /// hierarchy for a given virtual address.
121    pub fn translation_root(&self, va: Va) -> Pa {
122        self.registers().translation_root(va)
123    }
124
125    /// Returns the return address from the current stack frame.
126    pub fn return_address(&self) -> Result<Va, VmiError> {
127        self.registers().return_address(self.core())
128    }
129
130    /// Translates a virtual address to a physical address.
131    pub fn translate_address(&self, va: Va) -> Result<Pa, VmiError> {
132        self.core().translate_address(self.address_context(va))
133    }
134
135    // region: Read
136
137    /// Reads memory from the virtual machine.
138    pub fn read(&self, address: Va, buffer: &mut [u8]) -> Result<(), VmiError> {
139        self.read_in(self.access_context(address), buffer)
140    }
141
142    /// Writes memory to the virtual machine.
143    pub fn write(&self, address: Va, buffer: &[u8]) -> Result<(), VmiError> {
144        self.write_in(self.access_context(address), buffer)
145    }
146
147    /// Reads a single byte from the virtual machine.
148    pub fn read_u8(&self, address: Va) -> Result<u8, VmiError> {
149        self.read_u8_in(self.access_context(address))
150    }
151
152    /// Reads a 16-bit unsigned integer from the virtual machine.
153    pub fn read_u16(&self, address: Va) -> Result<u16, VmiError> {
154        self.read_u16_in(self.access_context(address))
155    }
156
157    /// Reads a 32-bit unsigned integer from the virtual machine.
158    pub fn read_u32(&self, address: Va) -> Result<u32, VmiError> {
159        self.read_u32_in(self.access_context(address))
160    }
161
162    /// Reads a 64-bit unsigned integer from the virtual machine.
163    pub fn read_u64(&self, address: Va) -> Result<u64, VmiError> {
164        self.read_u64_in(self.access_context(address))
165    }
166
167    /// Reads an unsigned integer of the specified size from the virtual machine.
168    ///
169    /// This method reads an unsigned integer of the specified size (in bytes)
170    /// from the virtual machine. Note that the size must be 1, 2, 4, or 8.
171    ///
172    /// The result is returned as a [`u64`] to accommodate the widest possible
173    /// integer size.
174    pub fn read_uint(&self, address: Va, size: usize) -> Result<u64, VmiError> {
175        self.read_uint_in(self.access_context(address), size)
176    }
177
178    /// Reads a field of a structure from the virtual machine.
179    ///
180    /// This method reads a field from the virtual machine. The field is
181    /// defined by the provided [`Field`] structure, which specifies the
182    /// offset and size of the field within the memory region.
183    ///
184    /// The result is returned as a [`u64`] to accommodate the widest possible
185    /// integer size.
186    pub fn read_field(&self, base_address: Va, field: &Field) -> Result<u64, VmiError> {
187        self.read_field_in(self.access_context(base_address), field)
188    }
189
190    /// Reads an address-sized unsigned integer from the virtual machine.
191    pub fn read_address(&self, address: Va) -> Result<u64, VmiError> {
192        self.read_address_in(self.access_context(address))
193    }
194
195    /// Reads an address-sized unsigned integer from the virtual machine.
196    pub fn read_address_native(&self, address: Va) -> Result<u64, VmiError> {
197        self.read_address_native_in(self.access_context(address))
198    }
199
200    /// Reads a 32-bit address from the virtual machine.
201    pub fn read_address32(&self, address: Va) -> Result<u64, VmiError> {
202        self.read_address32_in(self.access_context(address))
203    }
204
205    /// Reads a 64-bit address from the virtual machine.
206    pub fn read_address64(&self, address: Va) -> Result<u64, VmiError> {
207        self.read_address64_in(self.access_context(address))
208    }
209
210    /// Reads a virtual address from the virtual machine.
211    pub fn read_va(&self, address: Va) -> Result<Va, VmiError> {
212        self.read_va_in(self.access_context(address))
213    }
214
215    /// Reads a virtual address from the virtual machine.
216    pub fn read_va_native(&self, address: Va) -> Result<Va, VmiError> {
217        self.read_va_native_in(self.access_context(address))
218    }
219
220    /// Reads a 32-bit virtual address from the virtual machine.
221    pub fn read_va32(&self, address: Va) -> Result<Va, VmiError> {
222        self.read_va32_in(self.access_context(address))
223    }
224
225    /// Reads a 64-bit virtual address from the virtual machine.
226    pub fn read_va64(&self, address: Va) -> Result<Va, VmiError> {
227        self.read_va64_in(self.access_context(address))
228    }
229
230    /// Reads a null-terminated string of bytes from the virtual machine with a
231    /// specified limit.
232    pub fn read_string_bytes_limited(
233        &self,
234        address: Va,
235        limit: usize,
236    ) -> Result<Vec<u8>, VmiError> {
237        self.read_string_bytes_limited_in(self.access_context(address), limit)
238    }
239
240    /// Reads a null-terminated string of bytes from the virtual machine.
241    pub fn read_string_bytes(&self, address: Va) -> Result<Vec<u8>, VmiError> {
242        self.read_string_bytes_in(self.access_context(address))
243    }
244
245    /// Reads a null-terminated wide string (UTF-16) from the virtual machine
246    /// with a specified limit.
247    pub fn read_wstring_bytes_limited(
248        &self,
249        address: Va,
250        limit: usize,
251    ) -> Result<Vec<u16>, VmiError> {
252        self.read_wstring_bytes_limited_in(self.access_context(address), limit)
253    }
254
255    /// Reads a null-terminated wide string (UTF-16) from the virtual machine.
256    pub fn read_wstring_bytes(&self, address: Va) -> Result<Vec<u16>, VmiError> {
257        self.read_wstring_bytes_in(self.access_context(address))
258    }
259
260    /// Reads a null-terminated string from the virtual machine with a specified
261    /// limit.
262    pub fn read_string_limited(&self, address: Va, limit: usize) -> Result<String, VmiError> {
263        self.read_string_limited_in(self.access_context(address), limit)
264    }
265
266    /// Reads a null-terminated string from the virtual machine.
267    pub fn read_string(&self, address: Va) -> Result<String, VmiError> {
268        self.read_string_in(self.access_context(address))
269    }
270
271    /// Reads a null-terminated wide string (UTF-16) from the virtual machine
272    /// with a specified limit.
273    pub fn read_wstring_limited(&self, address: Va, limit: usize) -> Result<String, VmiError> {
274        self.read_wstring_limited_in(self.access_context(address), limit)
275    }
276
277    /// Reads a null-terminated wide string (UTF-16) from the virtual machine.
278    pub fn read_wstring(&self, address: Va) -> Result<String, VmiError> {
279        self.read_wstring_in(self.access_context(address))
280    }
281
282    /// Reads a struct from the virtual machine.
283    pub fn read_struct<T>(&self, address: Va) -> Result<T, VmiError>
284    where
285        T: IntoBytes + FromBytes,
286    {
287        self.read_struct_in(self.access_context(address))
288    }
289
290    // endregion: Read
291
292    // region: Read in
293
294    /// Reads memory from the virtual machine.
295    pub fn read_in(
296        &self,
297        ctx: impl Into<AccessContext>,
298        buffer: &mut [u8],
299    ) -> Result<(), VmiError> {
300        self.core().read(ctx, buffer)
301    }
302
303    /// Writes memory to the virtual machine.
304    pub fn write_in(&self, ctx: impl Into<AccessContext>, buffer: &[u8]) -> Result<(), VmiError> {
305        self.core().write(ctx, buffer)
306    }
307
308    /// Reads a single byte from the virtual machine.
309    pub fn read_u8_in(&self, ctx: impl Into<AccessContext>) -> Result<u8, VmiError> {
310        self.core().read_u8(ctx)
311    }
312
313    /// Reads a 16-bit unsigned integer from the virtual machine.
314    pub fn read_u16_in(&self, ctx: impl Into<AccessContext>) -> Result<u16, VmiError> {
315        self.core().read_u16(ctx)
316    }
317
318    /// Reads a 32-bit unsigned integer from the virtual machine.
319    pub fn read_u32_in(&self, ctx: impl Into<AccessContext>) -> Result<u32, VmiError> {
320        self.core().read_u32(ctx)
321    }
322
323    /// Reads a 64-bit unsigned integer from the virtual machine.
324    pub fn read_u64_in(&self, ctx: impl Into<AccessContext>) -> Result<u64, VmiError> {
325        self.core().read_u64(ctx)
326    }
327
328    /// Reads an unsigned integer of the specified size from the virtual machine.
329    ///
330    /// This method reads an unsigned integer of the specified size (in bytes)
331    /// from the virtual machine. Note that the size must be 1, 2, 4, or 8.
332    ///
333    /// The result is returned as a [`u64`] to accommodate the widest possible
334    /// integer size.
335    pub fn read_uint_in(
336        &self,
337        ctx: impl Into<AccessContext>,
338        size: usize,
339    ) -> Result<u64, VmiError> {
340        self.core().read_uint(ctx, size)
341    }
342
343    /// Reads a field of a structure from the virtual machine.
344    ///
345    /// This method reads a field from the virtual machine. The field is
346    /// defined by the provided [`Field`] structure, which specifies the
347    /// offset and size of the field within the memory region.
348    ///
349    /// The result is returned as a [`u64`] to accommodate the widest possible
350    /// integer size.
351    pub fn read_field_in(
352        &self,
353        ctx: impl Into<AccessContext>,
354        field: &Field,
355    ) -> Result<u64, VmiError> {
356        self.core().read_field(ctx, field)
357    }
358
359    /// Reads an address-sized unsigned integer from the virtual machine.
360    pub fn read_address_in(&self, ctx: impl Into<AccessContext>) -> Result<u64, VmiError> {
361        self.core()
362            .read_address(ctx, self.registers().effective_address_width())
363    }
364
365    /// Reads an address-sized unsigned integer from the virtual machine.
366    pub fn read_address_native_in(&self, ctx: impl Into<AccessContext>) -> Result<u64, VmiError> {
367        self.core()
368            .read_address(ctx, self.registers().address_width())
369    }
370
371    /// Reads a 32-bit address from the virtual machine.
372    pub fn read_address32_in(&self, ctx: impl Into<AccessContext>) -> Result<u64, VmiError> {
373        self.core().read_address32(ctx)
374    }
375
376    /// Reads a 64-bit address from the virtual machine.
377    pub fn read_address64_in(&self, ctx: impl Into<AccessContext>) -> Result<u64, VmiError> {
378        self.core().read_address64(ctx)
379    }
380
381    /// Reads a virtual address from the virtual machine.
382    pub fn read_va_in(&self, ctx: impl Into<AccessContext>) -> Result<Va, VmiError> {
383        self.core()
384            .read_va(ctx, self.registers().effective_address_width())
385    }
386
387    /// Reads a virtual address from the virtual machine.
388    pub fn read_va_native_in(&self, ctx: impl Into<AccessContext>) -> Result<Va, VmiError> {
389        self.core().read_va(ctx, self.registers().address_width())
390    }
391
392    /// Reads a 32-bit virtual address from the virtual machine.
393    pub fn read_va32_in(&self, ctx: impl Into<AccessContext>) -> Result<Va, VmiError> {
394        self.core().read_va32(ctx)
395    }
396
397    /// Reads a 64-bit virtual address from the virtual machine.
398    pub fn read_va64_in(&self, ctx: impl Into<AccessContext>) -> Result<Va, VmiError> {
399        self.core().read_va64(ctx)
400    }
401
402    /// Reads a null-terminated string of bytes from the virtual machine with a
403    /// specified limit.
404    pub fn read_string_bytes_limited_in(
405        &self,
406        ctx: impl Into<AccessContext>,
407        limit: usize,
408    ) -> Result<Vec<u8>, VmiError> {
409        self.core().read_string_bytes_limited(ctx, limit)
410    }
411
412    /// Reads a null-terminated string of bytes from the virtual machine.
413    pub fn read_string_bytes_in(&self, ctx: impl Into<AccessContext>) -> Result<Vec<u8>, VmiError> {
414        self.core().read_string_bytes(ctx)
415    }
416
417    /// Reads a null-terminated wide string (UTF-16) from the virtual machine
418    /// with a specified limit.
419    pub fn read_wstring_bytes_limited_in(
420        &self,
421        ctx: impl Into<AccessContext>,
422        limit: usize,
423    ) -> Result<Vec<u16>, VmiError> {
424        self.core().read_wstring_bytes_limited(ctx, limit)
425    }
426
427    /// Reads a null-terminated wide string (UTF-16) from the virtual machine.
428    pub fn read_wstring_bytes_in(
429        &self,
430        ctx: impl Into<AccessContext>,
431    ) -> Result<Vec<u16>, VmiError> {
432        self.core().read_wstring_bytes(ctx)
433    }
434
435    /// Reads a null-terminated string from the virtual machine with a specified
436    /// limit.
437    pub fn read_string_limited_in(
438        &self,
439        ctx: impl Into<AccessContext>,
440        limit: usize,
441    ) -> Result<String, VmiError> {
442        self.core().read_string_limited(ctx, limit)
443    }
444
445    /// Reads a null-terminated string from the virtual machine.
446    pub fn read_string_in(&self, ctx: impl Into<AccessContext>) -> Result<String, VmiError> {
447        self.core().read_string(ctx)
448    }
449
450    /// Reads a null-terminated wide string (UTF-16) from the virtual machine
451    /// with a specified limit.
452    pub fn read_wstring_limited_in(
453        &self,
454        ctx: impl Into<AccessContext>,
455        limit: usize,
456    ) -> Result<String, VmiError> {
457        self.core().read_wstring_limited(ctx, limit)
458    }
459
460    /// Reads a null-terminated wide string (UTF-16) from the virtual machine.
461    pub fn read_wstring_in(&self, ctx: impl Into<AccessContext>) -> Result<String, VmiError> {
462        self.core().read_wstring(ctx)
463    }
464
465    /// Reads a struct from the virtual machine.
466    pub fn read_struct_in<T>(&self, ctx: impl Into<AccessContext>) -> Result<T, VmiError>
467    where
468        T: IntoBytes + FromBytes,
469    {
470        self.core().read_struct(ctx)
471    }
472
473    // endregion: Read in
474
475    /// Writes a single byte to the virtual machine.
476    pub fn write_u8(&self, address: Va, value: u8) -> Result<(), VmiError> {
477        self.core().write_u8(self.access_context(address), value)
478    }
479
480    /// Writes a 16-bit unsigned integer to the virtual machine.
481    pub fn write_u16(&self, address: Va, value: u16) -> Result<(), VmiError> {
482        self.core().write_u16(self.access_context(address), value)
483    }
484
485    /// Writes a 32-bit unsigned integer to the virtual machine.
486    pub fn write_u32(&self, address: Va, value: u32) -> Result<(), VmiError> {
487        self.core().write_u32(self.access_context(address), value)
488    }
489
490    /// Writes a 64-bit unsigned integer to the virtual machine.
491    pub fn write_u64(&self, address: Va, value: u64) -> Result<(), VmiError> {
492        self.core().write_u64(self.access_context(address), value)
493    }
494
495    /// Writes a struct to the virtual machine.
496    pub fn write_struct<T>(&self, address: Va, value: T) -> Result<(), VmiError>
497    where
498        T: FromBytes + IntoBytes + Immutable,
499    {
500        self.core()
501            .write_struct(self.access_context(address), value)
502    }
503}
504
505/// Wrapper providing access to OS-specific operations.
506pub struct VmiOsState<'a, Driver, Os>(VmiState<'a, Driver, Os>)
507where
508    Driver: VmiDriver,
509    Os: VmiOs<Driver>;
510
511impl<'a, Driver, Os> VmiOsState<'a, Driver, Os>
512where
513    Driver: VmiDriver,
514    Os: VmiOs<Driver>,
515{
516    /// Returns the VMI core.
517    pub fn core(&self) -> &'a VmiCore<Driver> {
518        self.0.core()
519    }
520
521    /// Returns the underlying OS-specific implementation.
522    pub fn underlying_os(&self) -> &'a Os {
523        self.0.underlying_os()
524    }
525
526    /// Returns the VMI session.
527    pub fn session(&self) -> &VmiSession<'a, Driver, Os> {
528        self.0.session()
529    }
530
531    /// Returns the VMI state.
532    pub fn state(&self) -> VmiState<'a, Driver, Os> {
533        self.0
534    }
535
536    /// Returns the CPU registers associated with the current event.
537    pub fn registers(&self) -> &<Driver::Architecture as Architecture>::Registers {
538        self.0.registers()
539    }
540
541    /// Retrieves a specific function argument according to the calling
542    /// convention of the operating system.
543    pub fn function_argument_for_registers(
544        &self,
545        registers: &<Driver::Architecture as Architecture>::Registers,
546        index: u64,
547    ) -> Result<u64, VmiError> {
548        Os::function_argument(self.0.with_registers(registers), index)
549    }
550
551    /// Retrieves the return value of a function.
552    pub fn function_return_value_for_registers(
553        &self,
554        registers: &<Driver::Architecture as Architecture>::Registers,
555    ) -> Result<u64, VmiError> {
556        Os::function_return_value(self.0.with_registers(registers))
557    }
558}