pub struct PageTableMonitor<Driver, Tag = &'static str>where
Driver: VmiRead + VmiQueryProtection + VmiSetProtection,
<Driver as VmiDriver>::Architecture: ArchAdapter<Driver, Tag>,
Tag: TagType,{ /* private fields */ }ptm and utils only.Expand description
Architecture-independent page table monitor.
Thin wrapper that delegates to an architecture-specific implementation
selected via ArchAdapter.
Implementations§
Source§impl<Driver, Tag> PageTableMonitor<Driver, Tag>where
Driver: VmiRead + VmiQueryProtection + VmiSetProtection,
<Driver as VmiDriver>::Architecture: ArchAdapter<Driver, Tag>,
Tag: TagType,
impl<Driver, Tag> PageTableMonitor<Driver, Tag>where
Driver: VmiRead + VmiQueryProtection + VmiSetProtection,
<Driver as VmiDriver>::Architecture: ArchAdapter<Driver, Tag>,
Tag: TagType,
Sourcepub fn new() -> PageTableMonitor<Driver, Tag>
pub fn new() -> PageTableMonitor<Driver, Tag>
Creates a new page table monitor.
Examples found in repository?
161 pub fn new(
162 session: &VmiSession<WindowsOs<Driver>>,
163 profile: &Profile,
164 terminate_flag: Arc<AtomicBool>,
165 output_directory: PathBuf,
166 ) -> Result<Self, VmiError> {
167 let paused = session.pause_guard()?;
168 let vmi = paused.state();
169
170 let kernel_image_base = vmi.os().kernel_image_base()?;
171 let root = vmi.os().system_process()?.translation_root()?;
172 let symbols = Symbols::new(profile)?;
173
174 vmi.monitor_enable(EventMonitor::Interrupt(ExceptionVector::Breakpoint))?;
175 vmi.monitor_enable(EventMonitor::Singlestep)?;
176 vmi.monitor_enable(EventMonitor::Hypercall {
177 allow_userspace: true,
178 })?;
179
180 let view = vmi.create_view(MemoryAccess::RWX)?;
181 vmi.switch_to_view(view)?;
182
183 let mut bpm = BreakpointManager::new();
184 let mut ptm = PageTableMonitor::new();
185
186 macro_rules! install {
187 ($($name:ident),+ $(,)?) => {
188 $(
189 let va = kernel_image_base + symbols.$name;
190 let context = (va, root);
191 let hook = MonitorHook {
192 name: stringify!($name),
193 callback: self::hooks::$name::<Driver>,
194 };
195 let breakpoint = Breakpoint::new(context, view).global().with_tag(hook);
196 bpm.insert(&vmi, breakpoint)?;
197 ptm.monitor(&vmi, context, view, hook)?;
198 tracing::debug!(
199 ?hook,
200 %va,
201 "installed monitor hook"
202 );
203 )+
204 };
205 }
206
207 install!(
208 PspInsertProcess,
209 PspInsertThread,
210 KeTerminateThread,
211 MmCleanProcessAddressSpace,
212 NtWriteFile,
213 NtClose,
214 );
215
216 Ok(Self {
217 terminate_flag,
218 view,
219 bpm,
220 ptm,
221 bridge: Bridge::new((
222 DeployBridge::new(
223 DeployPolicy::default().execute_response(ExecuteResponse::ContinueAndNotify),
224 ),
225 FileTransferBridge::new(output_directory),
226 )),
227 state: MonitorState {
228 tracker: Tracker::default(),
229 creator_thread: None,
230 target_process: None,
231 output: None,
232 },
233 })
234 }More examples
68 pub fn new(
69 session: &VmiSession<WindowsOs<Driver>>,
70 profile: &Profile,
71 terminate_flag: Arc<AtomicBool>,
72 ) -> Result<Self, VmiError> {
73 // Capture the current state of the vCPU and get the base address of
74 // the kernel.
75 //
76 // This base address is essential to correctly offset monitored
77 // functions.
78 //
79 // NOTE: `kernel_image_base` tries to find the kernel in the memory
80 // with the help of the CPU registers. On AMD64 architecture,
81 // the kernel image base is usually found using the `MSR_LSTAR`
82 // register, which contains the address of the system call
83 // handler. This register is set by the operating system during
84 // boot and is left unchanged (unless some rootkits are involved).
85 //
86 // Therefore, we can take an arbitrary registers at any point
87 // in time (as long as the OS has booted and the page tables are
88 // set up) and use them to find the kernel image base.
89 let registers = session.registers(VcpuId(0))?;
90 let vmi = session.with_registers(®isters);
91
92 let kernel_image_base = vmi.os().kernel_image_base()?;
93 tracing::info!(%kernel_image_base);
94
95 // Get the system process.
96 //
97 // The system process is the first process created by the kernel.
98 // In Windows, it is referenced by the kernel symbol `PsInitialSystemProcess`.
99 // To monitor page table entries, we need to locate the translation root
100 // of this process.
101 let system_process = vmi.os().system_process()?;
102 tracing::info!(system_process = %system_process.object()?);
103
104 // Get the translation root of the system process.
105 // This is effectively "the CR3 of the kernel".
106 //
107 // The translation root is the root of the page table hierarchy (also
108 // known as the Directory Table Base or PML4).
109 let root = system_process.translation_root()?;
110 tracing::info!(%root);
111
112 // Load the symbols from the profile.
113 let symbols = Symbols::new(profile)?;
114
115 // Enable monitoring of the INT3 and singlestep events.
116 //
117 // INT3 is used to monitor the execution of specific functions.
118 // Singlestep is used to monitor the modifications of page table
119 // entries.
120 vmi.monitor_enable(EventMonitor::Interrupt(ExceptionVector::Breakpoint))?;
121 vmi.monitor_enable(EventMonitor::Singlestep)?;
122
123 // Create a new view for the monitor.
124 // This view is used for monitoring function calls and memory accesses.
125 let view = vmi.create_view(MemoryAccess::RWX)?;
126 vmi.switch_to_view(view)?;
127
128 // Create a new breakpoint controller.
129 //
130 // The breakpoint controller is used to insert breakpoints for specific
131 // functions.
132 //
133 // From the guest's perspective, these breakpoints are "hidden", since
134 // the breakpoint controller will unset the read/write access to the
135 // physical memory page where the breakpoint is inserted, while keeping
136 // the execute access.
137 //
138 // This way, the guest will be able to execute the code, but attempts to
139 // read or write the memory will trigger the `memory_access` callback.
140 //
141 // When a vCPU tries to execute the breakpoint instruction:
142 // - an `interrupt` callback will be triggered
143 // - the breakpoint will be handled (e.g., log the function call)
144 // - a fast-singlestep[1] will be performed over the INT3 instruction
145 //
146 // When a vCPU tries to read from this page (e.g., a PatchGuard check):
147 // - `memory_access` callback will be triggered (with the `MemoryAccess::R`
148 // access type)
149 // - fast-singlestep[1] will be performed over the instruction that tried to
150 // read the memory
151 //
152 // This way, the instruction will read the original memory content.
153 //
154 // [1] Fast-singlestep is a VMI feature that allows to switch the vCPU
155 // to a different view, execute a single instruction, and then
156 // switch back to the original view. In this case, the view is
157 // switched to the `default_view` (which is unmodified).
158 let mut bpm = BreakpointManager::new();
159
160 // Create a new page table monitor.
161 //
162 // The page table monitor is used to monitor the page table entries of
163 // the hooked functions.
164 //
165 // More specifically, it is used to monitor the pages that the breakpoint
166 // was inserted into. This is necessary to handle the case when the
167 // page containing the breakpoint is paged out (and then paged in
168 // again).
169 //
170 // `PageTableMonitor` works by unsetting the write access to the page
171 // tables of the hooked functions. When the page is paged out, the
172 // `PRESENT` bit in the page table entry is unset and, conversely, when
173 // the page is paged in, the `PRESENT` bit is set again.
174 //
175 // When that happens:
176 // - the `memory_access` callback will be triggered (with the `MemoryAccess::R`
177 // access type)
178 // - the callback will mark the page as dirty in the page table monitor
179 // - a singlestep will be performed over the instruction that tried to modify
180 // the memory containing the page table entry
181 // - the `singlestep` handler will process the dirty page table entries and
182 // inform the breakpoint controller to handle the changes
183 let mut ptm = PageTableMonitor::new();
184
185 // Pause the VM to avoid race conditions between inserting breakpoints
186 // and monitoring page table entries. The VM resumes when the pause
187 // guard is dropped.
188 let _pause_guard = vmi.pause_guard()?;
189
190 // Insert breakpoint for the `NtCreateFile` function.
191 let va_NtCreateFile = kernel_image_base + symbols.NtCreateFile;
192 let cx_NtCreateFile = (va_NtCreateFile, root);
193 let bp_NtCreateFile = Breakpoint::new(cx_NtCreateFile, view)
194 .global()
195 .with_tag("NtCreateFile");
196 bpm.insert(&vmi, bp_NtCreateFile)?;
197 ptm.monitor(&vmi, cx_NtCreateFile, view, "NtCreateFile")?;
198 tracing::info!(%va_NtCreateFile);
199
200 // Insert breakpoint for the `NtWriteFile` function.
201 let va_NtWriteFile = kernel_image_base + symbols.NtWriteFile;
202 let cx_NtWriteFile = (va_NtWriteFile, root);
203 let bp_NtWriteFile = Breakpoint::new(cx_NtWriteFile, view)
204 .global()
205 .with_tag("NtWriteFile");
206 bpm.insert(&vmi, bp_NtWriteFile)?;
207 ptm.monitor(&vmi, cx_NtWriteFile, view, "NtWriteFile")?;
208 tracing::info!(%va_NtWriteFile);
209
210 // Insert breakpoint for the `PspInsertProcess` function.
211 let va_PspInsertProcess = kernel_image_base + symbols.PspInsertProcess;
212 let cx_PspInsertProcess = (va_PspInsertProcess, root);
213 let bp_PspInsertProcess = Breakpoint::new(cx_PspInsertProcess, view)
214 .global()
215 .with_tag("PspInsertProcess");
216 bpm.insert(&vmi, bp_PspInsertProcess)?;
217 ptm.monitor(&vmi, cx_PspInsertProcess, view, "PspInsertProcess")?;
218
219 // Insert breakpoint for the `MmCleanProcessAddressSpace` function.
220 let va_MmCleanProcessAddressSpace = kernel_image_base + symbols.MmCleanProcessAddressSpace;
221 let cx_MmCleanProcessAddressSpace = (va_MmCleanProcessAddressSpace, root);
222 let bp_MmCleanProcessAddressSpace = Breakpoint::new(cx_MmCleanProcessAddressSpace, view)
223 .global()
224 .with_tag("MmCleanProcessAddressSpace");
225 bpm.insert(&vmi, bp_MmCleanProcessAddressSpace)?;
226 ptm.monitor(
227 &vmi,
228 cx_MmCleanProcessAddressSpace,
229 view,
230 "MmCleanProcessAddressSpace",
231 )?;
232
233 Ok(Self {
234 terminate_flag,
235 view,
236 bpm,
237 ptm,
238 })
239 }Sourcepub fn monitored_tables(&self) -> usize
pub fn monitored_tables(&self) -> usize
Returns the number of write-protected page table pages.
Sourcepub fn monitored_entries(&self) -> usize
pub fn monitored_entries(&self) -> usize
Returns the number of monitored entries.
Sourcepub fn paged_in_entries(&self) -> usize
pub fn paged_in_entries(&self) -> usize
Returns the number of monitored addresses currently backed by physical memory.
Sourcepub fn monitor(
&mut self,
vmi: &VmiCore<Driver>,
ctx: impl Into<AddressContext>,
view: View,
tag: Tag,
) -> Result<(), VmiError>
pub fn monitor( &mut self, vmi: &VmiCore<Driver>, ctx: impl Into<AddressContext>, view: View, tag: Tag, ) -> Result<(), VmiError>
Acquires a reference to monitoring a virtual address for page table changes.
On the first reference, walks the page table hierarchy and write-protects the relevant pages. Repeated calls for the same address and view increment its reference count.
Examples found in repository?
68 pub fn new(
69 session: &VmiSession<WindowsOs<Driver>>,
70 profile: &Profile,
71 terminate_flag: Arc<AtomicBool>,
72 ) -> Result<Self, VmiError> {
73 // Capture the current state of the vCPU and get the base address of
74 // the kernel.
75 //
76 // This base address is essential to correctly offset monitored
77 // functions.
78 //
79 // NOTE: `kernel_image_base` tries to find the kernel in the memory
80 // with the help of the CPU registers. On AMD64 architecture,
81 // the kernel image base is usually found using the `MSR_LSTAR`
82 // register, which contains the address of the system call
83 // handler. This register is set by the operating system during
84 // boot and is left unchanged (unless some rootkits are involved).
85 //
86 // Therefore, we can take an arbitrary registers at any point
87 // in time (as long as the OS has booted and the page tables are
88 // set up) and use them to find the kernel image base.
89 let registers = session.registers(VcpuId(0))?;
90 let vmi = session.with_registers(®isters);
91
92 let kernel_image_base = vmi.os().kernel_image_base()?;
93 tracing::info!(%kernel_image_base);
94
95 // Get the system process.
96 //
97 // The system process is the first process created by the kernel.
98 // In Windows, it is referenced by the kernel symbol `PsInitialSystemProcess`.
99 // To monitor page table entries, we need to locate the translation root
100 // of this process.
101 let system_process = vmi.os().system_process()?;
102 tracing::info!(system_process = %system_process.object()?);
103
104 // Get the translation root of the system process.
105 // This is effectively "the CR3 of the kernel".
106 //
107 // The translation root is the root of the page table hierarchy (also
108 // known as the Directory Table Base or PML4).
109 let root = system_process.translation_root()?;
110 tracing::info!(%root);
111
112 // Load the symbols from the profile.
113 let symbols = Symbols::new(profile)?;
114
115 // Enable monitoring of the INT3 and singlestep events.
116 //
117 // INT3 is used to monitor the execution of specific functions.
118 // Singlestep is used to monitor the modifications of page table
119 // entries.
120 vmi.monitor_enable(EventMonitor::Interrupt(ExceptionVector::Breakpoint))?;
121 vmi.monitor_enable(EventMonitor::Singlestep)?;
122
123 // Create a new view for the monitor.
124 // This view is used for monitoring function calls and memory accesses.
125 let view = vmi.create_view(MemoryAccess::RWX)?;
126 vmi.switch_to_view(view)?;
127
128 // Create a new breakpoint controller.
129 //
130 // The breakpoint controller is used to insert breakpoints for specific
131 // functions.
132 //
133 // From the guest's perspective, these breakpoints are "hidden", since
134 // the breakpoint controller will unset the read/write access to the
135 // physical memory page where the breakpoint is inserted, while keeping
136 // the execute access.
137 //
138 // This way, the guest will be able to execute the code, but attempts to
139 // read or write the memory will trigger the `memory_access` callback.
140 //
141 // When a vCPU tries to execute the breakpoint instruction:
142 // - an `interrupt` callback will be triggered
143 // - the breakpoint will be handled (e.g., log the function call)
144 // - a fast-singlestep[1] will be performed over the INT3 instruction
145 //
146 // When a vCPU tries to read from this page (e.g., a PatchGuard check):
147 // - `memory_access` callback will be triggered (with the `MemoryAccess::R`
148 // access type)
149 // - fast-singlestep[1] will be performed over the instruction that tried to
150 // read the memory
151 //
152 // This way, the instruction will read the original memory content.
153 //
154 // [1] Fast-singlestep is a VMI feature that allows to switch the vCPU
155 // to a different view, execute a single instruction, and then
156 // switch back to the original view. In this case, the view is
157 // switched to the `default_view` (which is unmodified).
158 let mut bpm = BreakpointManager::new();
159
160 // Create a new page table monitor.
161 //
162 // The page table monitor is used to monitor the page table entries of
163 // the hooked functions.
164 //
165 // More specifically, it is used to monitor the pages that the breakpoint
166 // was inserted into. This is necessary to handle the case when the
167 // page containing the breakpoint is paged out (and then paged in
168 // again).
169 //
170 // `PageTableMonitor` works by unsetting the write access to the page
171 // tables of the hooked functions. When the page is paged out, the
172 // `PRESENT` bit in the page table entry is unset and, conversely, when
173 // the page is paged in, the `PRESENT` bit is set again.
174 //
175 // When that happens:
176 // - the `memory_access` callback will be triggered (with the `MemoryAccess::R`
177 // access type)
178 // - the callback will mark the page as dirty in the page table monitor
179 // - a singlestep will be performed over the instruction that tried to modify
180 // the memory containing the page table entry
181 // - the `singlestep` handler will process the dirty page table entries and
182 // inform the breakpoint controller to handle the changes
183 let mut ptm = PageTableMonitor::new();
184
185 // Pause the VM to avoid race conditions between inserting breakpoints
186 // and monitoring page table entries. The VM resumes when the pause
187 // guard is dropped.
188 let _pause_guard = vmi.pause_guard()?;
189
190 // Insert breakpoint for the `NtCreateFile` function.
191 let va_NtCreateFile = kernel_image_base + symbols.NtCreateFile;
192 let cx_NtCreateFile = (va_NtCreateFile, root);
193 let bp_NtCreateFile = Breakpoint::new(cx_NtCreateFile, view)
194 .global()
195 .with_tag("NtCreateFile");
196 bpm.insert(&vmi, bp_NtCreateFile)?;
197 ptm.monitor(&vmi, cx_NtCreateFile, view, "NtCreateFile")?;
198 tracing::info!(%va_NtCreateFile);
199
200 // Insert breakpoint for the `NtWriteFile` function.
201 let va_NtWriteFile = kernel_image_base + symbols.NtWriteFile;
202 let cx_NtWriteFile = (va_NtWriteFile, root);
203 let bp_NtWriteFile = Breakpoint::new(cx_NtWriteFile, view)
204 .global()
205 .with_tag("NtWriteFile");
206 bpm.insert(&vmi, bp_NtWriteFile)?;
207 ptm.monitor(&vmi, cx_NtWriteFile, view, "NtWriteFile")?;
208 tracing::info!(%va_NtWriteFile);
209
210 // Insert breakpoint for the `PspInsertProcess` function.
211 let va_PspInsertProcess = kernel_image_base + symbols.PspInsertProcess;
212 let cx_PspInsertProcess = (va_PspInsertProcess, root);
213 let bp_PspInsertProcess = Breakpoint::new(cx_PspInsertProcess, view)
214 .global()
215 .with_tag("PspInsertProcess");
216 bpm.insert(&vmi, bp_PspInsertProcess)?;
217 ptm.monitor(&vmi, cx_PspInsertProcess, view, "PspInsertProcess")?;
218
219 // Insert breakpoint for the `MmCleanProcessAddressSpace` function.
220 let va_MmCleanProcessAddressSpace = kernel_image_base + symbols.MmCleanProcessAddressSpace;
221 let cx_MmCleanProcessAddressSpace = (va_MmCleanProcessAddressSpace, root);
222 let bp_MmCleanProcessAddressSpace = Breakpoint::new(cx_MmCleanProcessAddressSpace, view)
223 .global()
224 .with_tag("MmCleanProcessAddressSpace");
225 bpm.insert(&vmi, bp_MmCleanProcessAddressSpace)?;
226 ptm.monitor(
227 &vmi,
228 cx_MmCleanProcessAddressSpace,
229 view,
230 "MmCleanProcessAddressSpace",
231 )?;
232
233 Ok(Self {
234 terminate_flag,
235 view,
236 bpm,
237 ptm,
238 })
239 }Sourcepub fn unmonitor(
&mut self,
vmi: &VmiCore<Driver>,
ctx: impl Into<AddressContext>,
view: View,
) -> Result<(), VmiError>
pub fn unmonitor( &mut self, vmi: &VmiCore<Driver>, ctx: impl Into<AddressContext>, view: View, ) -> Result<(), VmiError>
Releases one reference to a monitored virtual address.
The address is removed after its last reference is released. This also removes write protection from page table pages that no longer have any monitored entries.
Sourcepub fn unmonitor_by_force(
&mut self,
vmi: &VmiCore<Driver>,
ctx: impl Into<AddressContext>,
view: View,
) -> Result<(), VmiError>
pub fn unmonitor_by_force( &mut self, vmi: &VmiCore<Driver>, ctx: impl Into<AddressContext>, view: View, ) -> Result<(), VmiError>
Stops monitoring a virtual address regardless of its reference count.
This releases all references to the address.
Sourcepub fn unmonitor_all(&mut self, vmi: &VmiCore<Driver>)
pub fn unmonitor_all(&mut self, vmi: &VmiCore<Driver>)
Stops monitoring all virtual addresses and restores memory access.
Examples found in repository?
397 fn cleanup(&mut self, vmi: &VmiSession<WindowsOs<Driver>>) {
398 if let Err(err) = vmi.switch_to_view(vmi.default_view()) {
399 tracing::error!(%err, "failed to switch to the default view");
400 }
401
402 if let Err(err) = vmi.monitor_disable(EventMonitor::Singlestep) {
403 tracing::error!(%err, "failed to disable singlestep monitoring");
404 }
405
406 if let Err(err) = vmi.monitor_disable(EventMonitor::Interrupt(ExceptionVector::Breakpoint))
407 {
408 tracing::error!(%err, "failed to disable breakpoint monitoring");
409 }
410
411 if let Err(err) = vmi.monitor_disable(EventMonitor::Hypercall {
412 allow_userspace: true,
413 }) {
414 tracing::error!(%err, "failed to disable hypercall monitoring");
415 }
416
417 match self.bpm.remove_by_view(vmi, self.view) {
418 Ok(true) => {}
419 Ok(false) => tracing::warn!("no deploy monitor breakpoints to remove"),
420 Err(err) => tracing::error!(%err, "failed to remove deploy monitor breakpoints"),
421 }
422 self.ptm.unmonitor_all(vmi);
423
424 if let Err(err) = vmi.destroy_view(self.view) {
425 tracing::error!(%err, "failed to destroy deploy monitor view");
426 }
427 }Sourcepub fn unmonitor_view(&mut self, vmi: &VmiCore<Driver>, view: View)
pub fn unmonitor_view(&mut self, vmi: &VmiCore<Driver>, view: View)
Stops monitoring all virtual addresses associated with a view.
Sourcepub fn mark_dirty_entry(
&mut self,
entry_pa: Pa,
view: View,
vcpu_id: VcpuId,
) -> bool
pub fn mark_dirty_entry( &mut self, entry_pa: Pa, view: View, vcpu_id: VcpuId, ) -> bool
Marks a page table entry as dirty after a write to a monitored page.
Returns true if the entry is being monitored (and was marked),
false otherwise.
Examples found in repository?
237 fn memory_access(
238 &mut self,
239 vmi: &VmiContext<WindowsOs<Driver>>,
240 ) -> Result<VmiEventResponse<Amd64>, VmiError> {
241 let memory_access = vmi.event().reason().as_memory_access();
242
243 if memory_access.access.contains(MemoryAccess::W) {
244 self.ptm
245 .mark_dirty_entry(memory_access.pa, self.view, vmi.event().vcpu_id());
246
247 Ok(VmiEventResponse::singlestep().with_view(vmi.default_view()))
248 }
249 else if memory_access.access.contains(MemoryAccess::R) {
250 Ok(VmiEventResponse::fast_singlestep(vmi.default_view()))
251 }
252 else {
253 panic!("unhandled memory access: {memory_access:?}");
254 }
255 }More examples
242 fn memory_access(
243 &mut self,
244 vmi: &VmiContext<WindowsOs<Driver>>,
245 ) -> Result<VmiEventResponse<Amd64>, VmiError> {
246 let memory_access = vmi.event().reason().as_memory_access();
247
248 tracing::trace!(
249 pa = %memory_access.pa,
250 va = %memory_access.va,
251 access = %memory_access.access,
252 );
253
254 if memory_access.access.contains(MemoryAccess::W) {
255 // It is assumed that a write memory access event is caused by a
256 // page table modification.
257 //
258 // The page table entry is marked as dirty in the page table monitor
259 // and a singlestep is performed to process the dirty entries.
260 self.ptm
261 .mark_dirty_entry(memory_access.pa, self.view, vmi.event().vcpu_id());
262
263 Ok(VmiEventResponse::singlestep().with_view(vmi.default_view()))
264 }
265 else if memory_access.access.contains(MemoryAccess::R) {
266 // When the guest tries to read from the memory, a fast-singlestep
267 // is performed over the instruction that tried to read the memory.
268 // This is done to allow the instruction to read the original memory
269 // content.
270 Ok(VmiEventResponse::fast_singlestep(vmi.default_view()))
271 }
272 else {
273 panic!("Unhandled memory access: {memory_access:?}");
274 }
275 }Sourcepub fn process_dirty_entries(
&mut self,
vmi: &VmiCore<Driver>,
vcpu_id: VcpuId,
) -> Result<Vec<PageTableMonitorEvent>, VmiError>
pub fn process_dirty_entries( &mut self, vmi: &VmiCore<Driver>, vcpu_id: VcpuId, ) -> Result<Vec<PageTableMonitorEvent>, VmiError>
Processes dirty entries for a vCPU, returning page-in/page-out events.
Should be called after singlestepping past the write that triggered the dirty marking, so the new PTE value is committed to memory.
Examples found in repository?
More examples
320 fn singlestep(
321 &mut self,
322 vmi: &VmiContext<WindowsOs<Driver>>,
323 ) -> Result<VmiEventResponse<Amd64>, VmiError> {
324 // Get the page table modifications by processing the dirty page table
325 // entries.
326 let ptm_events = self.ptm.process_dirty_entries(vmi, vmi.event().vcpu_id())?;
327
328 // Let the breakpoint controller handle the page table modifications.
329 self.bpm.handle_ptm_events(vmi, ptm_events)?;
330
331 // Disable singlestep and switch back to our view.
332 Ok(VmiEventResponse::default().with_view(self.view))
333 }Auto Trait Implementations§
impl<Driver, Tag> Freeze for PageTableMonitor<Driver, Tag>
impl<Driver, Tag> RefUnwindSafe for PageTableMonitor<Driver, Tag>
impl<Driver, Tag> Send for PageTableMonitor<Driver, Tag>
impl<Driver, Tag> Sync for PageTableMonitor<Driver, Tag>
impl<Driver, Tag> Unpin for PageTableMonitor<Driver, Tag>
impl<Driver, Tag> UnsafeUnpin for PageTableMonitor<Driver, Tag>
impl<Driver, Tag> UnwindSafe for PageTableMonitor<Driver, Tag>
Blanket Implementations§
Source§impl<T> ArchivePointee for T
impl<T> ArchivePointee for T
Source§type ArchivedMetadata = ()
type ArchivedMetadata = ()
Source§fn pointer_metadata(
_: &<T as ArchivePointee>::ArchivedMetadata,
) -> <T as Pointee>::Metadata
fn pointer_metadata( _: &<T as ArchivePointee>::ArchivedMetadata, ) -> <T as Pointee>::Metadata
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> LayoutRaw for T
impl<T> LayoutRaw for T
Source§fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>
fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>
Source§impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
Source§unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool
unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool
Source§fn resolve_niched(out: Place<NichedOption<T, N1>>)
fn resolve_niched(out: Place<NichedOption<T, N1>>)
out indicating that a T is niched.