vmi_os_windows/comps/object/
directory.rs1use vmi_core::{Registers as _, Va, VmiError, VmiState, VmiVa, driver::VmiRead};
2
3use super::{FromWindowsObject, WindowsObject, WindowsObjectTypeKind};
4use crate::{ArchAdapter, DirectoryObjectIterator, WindowsOs, offset};
5
6pub struct WindowsDirectoryObject<'a, Driver>
15where
16 Driver: VmiRead,
17 Driver::Architecture: ArchAdapter<Driver>,
18{
19 vmi: VmiState<'a, WindowsOs<Driver>>,
21
22 va: Va,
24}
25
26impl<'a, Driver> From<WindowsDirectoryObject<'a, Driver>> for WindowsObject<'a, Driver>
27where
28 Driver: VmiRead,
29 Driver::Architecture: ArchAdapter<Driver>,
30{
31 fn from(value: WindowsDirectoryObject<'a, Driver>) -> Self {
32 Self::new(value.vmi, value.va)
33 }
34}
35
36impl<'a, Driver> FromWindowsObject<'a, Driver> for WindowsDirectoryObject<'a, Driver>
37where
38 Driver: VmiRead,
39 Driver::Architecture: ArchAdapter<Driver>,
40{
41 fn from_object(object: WindowsObject<'a, Driver>) -> Result<Option<Self>, VmiError> {
42 match object.type_kind()? {
43 Some(WindowsObjectTypeKind::Directory) => Ok(Some(Self::new(object.vmi, object.va))),
44 _ => Ok(None),
45 }
46 }
47}
48
49impl<Driver> VmiVa for WindowsDirectoryObject<'_, Driver>
50where
51 Driver: VmiRead,
52 Driver::Architecture: ArchAdapter<Driver>,
53{
54 fn va(&self) -> Va {
55 self.va
56 }
57}
58
59impl<'a, Driver> WindowsDirectoryObject<'a, Driver>
60where
61 Driver: VmiRead,
62 Driver::Architecture: ArchAdapter<Driver>,
63{
64 pub fn new(vmi: VmiState<'a, WindowsOs<Driver>>, va: Va) -> Self {
66 Self { vmi, va }
67 }
68
69 pub fn iter(
71 &self,
72 ) -> Result<
73 impl Iterator<Item = Result<WindowsObject<'a, Driver>, VmiError>> + use<'a, Driver>,
74 VmiError,
75 > {
76 Ok(DirectoryObjectIterator::new(self.vmi, self.va))
77 }
78
79 pub fn lookup(
91 &self,
92 path: impl AsRef<str>,
93 ) -> Result<Option<WindowsObject<'a, Driver>>, VmiError> {
94 let path = path.as_ref();
95 let mut components = path.split('\\').filter(|component| !component.is_empty());
96
97 let first = match components.next() {
98 Some(first) => first,
99 None => return Ok(Some(WindowsObject::new(self.vmi, self.va))),
100 };
101
102 let mut directory = WindowsDirectoryObject::new(self.vmi, self.va);
103 let mut next = first;
104 for component in components {
105 let object = match directory.child(next)? {
106 Some(object) => object,
107 None => return Ok(None),
108 };
109
110 directory = match WindowsDirectoryObject::from_object(object)? {
111 Some(dir) => dir,
112 None => return Ok(None),
113 };
114
115 next = component;
116 }
117
118 directory.child(next)
119 }
120
121 pub fn child(
136 &self,
137 name: impl AsRef<str>,
138 ) -> Result<Option<WindowsObject<'a, Driver>>, VmiError> {
139 const NUMBER_HASH_BUCKETS: u64 = 37;
140
141 let OBJECT_DIRECTORY = offset!(self.vmi, _OBJECT_DIRECTORY);
142
143 let name = name.as_ref();
144 let address_width = self.vmi.registers().address_width() as u64;
145
146 for index in 0..NUMBER_HASH_BUCKETS {
147 let bucket = self.va + OBJECT_DIRECTORY.HashBuckets.offset() + index * address_width;
148
149 match self.lookup_in_bucket(bucket, name) {
150 Ok(Some(object)) => return Ok(Some(object)),
151 Ok(None) => {}
152 Err(err) => {
153 tracing::trace!(%err, index, "skipping directory bucket after read error");
154 }
155 }
156 }
157
158 Ok(None)
159 }
160
161 fn lookup_in_bucket(
163 &self,
164 bucket: Va,
165 name: &str,
166 ) -> Result<Option<WindowsObject<'a, Driver>>, VmiError> {
167 let OBJECT_DIRECTORY_ENTRY = offset!(self.vmi, _OBJECT_DIRECTORY_ENTRY);
168
169 let mut entry = self.vmi.read_va_native(bucket)?;
170 while !entry.is_null() {
171 let object = self
172 .vmi
173 .read_va_native(entry + OBJECT_DIRECTORY_ENTRY.Object.offset())?;
174
175 let object = WindowsObject::new(self.vmi, object);
176
177 match object.name() {
178 Ok(Some(entry_name)) => {
179 if entry_name.eq_ignore_ascii_case(name) {
180 return Ok(Some(object));
181 }
182 }
183 Ok(None) => {}
184 Err(err) => {
185 tracing::trace!(%err, "skipping directory entry with unreadable name");
186 }
187 }
188
189 entry = self
190 .vmi
191 .read_va_native(entry + OBJECT_DIRECTORY_ENTRY.ChainLink.offset())?;
192 }
193
194 Ok(None)
195 }
196}