Skip to main content

vmi_os_windows/comps/object/
file.rs

1use vmi_core::{Va, VmiError, VmiState, VmiVa, driver::VmiRead};
2
3use super::{FromWindowsObject, WindowsObject, WindowsObjectTypeKind};
4use crate::{ArchAdapter, WindowsOs, WindowsOsExt as _, offset};
5
6/// A Windows file object.
7///
8/// A file object is a kernel structure that represents an open file or device
9/// in the Windows Object Manager. It contains metadata about the file, its access
10/// permissions, and associated device or volume.
11///
12/// # Implementation Details
13///
14/// Corresponds to `_FILE_OBJECT`.
15pub struct WindowsFileObject<'a, Driver>
16where
17    Driver: VmiRead,
18    Driver::Architecture: ArchAdapter<Driver>,
19{
20    /// The VMI state.
21    vmi: VmiState<'a, WindowsOs<Driver>>,
22
23    /// Address of the `_FILE_OBJECT` structure.
24    va: Va,
25}
26
27impl<'a, Driver> From<WindowsFileObject<'a, Driver>> for WindowsObject<'a, Driver>
28where
29    Driver: VmiRead,
30    Driver::Architecture: ArchAdapter<Driver>,
31{
32    fn from(value: WindowsFileObject<'a, Driver>) -> Self {
33        Self::new(value.vmi, value.va)
34    }
35}
36
37impl<'a, Driver> FromWindowsObject<'a, Driver> for WindowsFileObject<'a, Driver>
38where
39    Driver: VmiRead,
40    Driver::Architecture: ArchAdapter<Driver>,
41{
42    fn from_object(object: WindowsObject<'a, Driver>) -> Result<Option<Self>, VmiError> {
43        match object.type_kind()? {
44            Some(WindowsObjectTypeKind::File) => Ok(Some(Self::new(object.vmi, object.va))),
45            _ => Ok(None),
46        }
47    }
48}
49
50impl<Driver> VmiVa for WindowsFileObject<'_, Driver>
51where
52    Driver: VmiRead,
53    Driver::Architecture: ArchAdapter<Driver>,
54{
55    fn va(&self) -> Va {
56        self.va
57    }
58}
59
60impl<'a, Driver> WindowsFileObject<'a, Driver>
61where
62    Driver: VmiRead,
63    Driver::Architecture: ArchAdapter<Driver>,
64{
65    /// Creates a new Windows file object.
66    pub fn new(vmi: VmiState<'a, WindowsOs<Driver>>, va: Va) -> Self {
67        Self { vmi, va }
68    }
69
70    /// Returns the device object associated with the file object.
71    ///
72    /// # Implementation Details
73    ///
74    /// Corresponds to `_FILE_OBJECT.DeviceObject`.
75    pub fn device_object(&self) -> Result<WindowsObject<'a, Driver>, VmiError> {
76        let FILE_OBJECT = offset!(self.vmi, _FILE_OBJECT);
77
78        let device_object = self
79            .vmi
80            .read_va_native(self.va + FILE_OBJECT.DeviceObject.offset())?;
81
82        Ok(WindowsObject::new(self.vmi, device_object))
83    }
84
85    /// Returns the filename associated with the file object.
86    ///
87    /// # Implementation Details
88    ///
89    /// Corresponds to `_FILE_OBJECT.FileName`.
90    ///
91    /// # Notes
92    ///
93    /// This operation might fail as the filename is allocated from paged pool.
94    pub fn filename(&self) -> Result<String, VmiError> {
95        let FILE_OBJECT = offset!(self.vmi, _FILE_OBJECT);
96
97        // Note that filename is allocated from paged pool,
98        // so this read might fail.
99        self.vmi
100            .os()
101            .read_unicode_string(self.va + FILE_OBJECT.FileName.offset())
102    }
103
104    /// Constructs the full path of a file from its `FILE_OBJECT`.
105    ///
106    /// This function first reads the `DeviceObject` field of the `FILE_OBJECT`
107    /// structure. Then it reads the `ObjectNameInfo` of the `DeviceObject`
108    /// and its directory. Finally, it concatenates the device directory
109    /// name, device name, and file name.
110    ///
111    /// # Implementation Details
112    ///
113    /// Corresponds to `_FILE_OBJECT.DeviceObject.NameInfo.Name` concatenated
114    /// with `_FILE_OBJECT.FileName`.
115    pub fn full_path(&self) -> Result<String, VmiError> {
116        let device = self.device_object()?.name_info()?;
117        let directory = match &device {
118            Some(device) => match device.directory()? {
119                Some(directory) => directory.name_info()?,
120                None => None,
121            },
122            None => None,
123        };
124
125        let mut result = String::new();
126        if let Some(directory) = directory {
127            result.push('\\');
128            result.push_str(&directory.name()?);
129        }
130
131        if let Some(device) = device {
132            result.push('\\');
133            result.push_str(&device.name()?);
134        }
135
136        result.push_str(&self.filename()?);
137
138        Ok(result)
139    }
140}