1use std::collections::HashMap;
2
3use vmi_core::{Architecture, Hex, Registers, Va, VmiDriver, VmiError, VmiState, os::VmiOs};
4
5pub enum RecipeControlFlow {
7 Continue,
9
10 Break,
12
13 Repeat,
15
16 Skip,
18
19 Goto(usize),
21}
22
23pub type RecipeStepFn<Driver, Os, T> = Box<
25 dyn Fn(&mut RecipeContext<'_, Driver, Os, T>) -> Result<RecipeControlFlow, VmiError>
26 + Send
27 + Sync
28 + 'static,
29>;
30
31pub type SymbolCache = HashMap<String, Va>;
34
35pub type ImageSymbolCache = HashMap<String, SymbolCache>;
38
39pub struct Recipe<Driver, Os, T>
41where
42 Driver: VmiDriver,
43 Os: VmiOs<Driver>,
44{
45 pub(super) steps: Vec<RecipeStepFn<Driver, Os, T>>,
47
48 pub(super) data: T,
50}
51
52impl<Driver, Os, T> Recipe<Driver, Os, T>
53where
54 Driver: VmiDriver,
55 Os: VmiOs<Driver>,
56{
57 pub fn new(data: T) -> Self {
59 Self {
60 steps: Vec::new(),
61 data,
62 }
63 }
64
65 pub fn step<F>(mut self, f: F) -> Self
67 where
68 F: Fn(&mut RecipeContext<'_, Driver, Os, T>) -> Result<RecipeControlFlow, VmiError>
69 + Send
70 + Sync
71 + 'static,
72 {
73 self.steps.push(Box::new(f));
74 self
75 }
76}
77
78pub struct RecipeContext<'a, Driver, Os, T>
80where
81 Driver: VmiDriver,
82 Os: VmiOs<Driver>,
83{
84 pub vmi: &'a VmiState<'a, Driver, Os>,
86
87 pub registers: &'a mut <<Driver as VmiDriver>::Architecture as Architecture>::Registers,
89
90 pub data: &'a mut T,
92
93 pub cache: &'a mut ImageSymbolCache,
95}
96
97pub struct RecipeExecutor<Driver, Os, T>
99where
100 Driver: VmiDriver,
101 Os: VmiOs<Driver>,
102{
103 recipe: Recipe<Driver, Os, T>,
105
106 cache: ImageSymbolCache,
108
109 original_registers: Option<<Driver::Architecture as Architecture>::Registers>,
111
112 index: Option<usize>,
114
115 previous_stack_pointer: Option<Va>,
117}
118
119impl<Driver, Os, T> RecipeExecutor<Driver, Os, T>
120where
121 Driver: VmiDriver,
122 Os: VmiOs<Driver>,
123{
124 pub fn new(recipe: Recipe<Driver, Os, T>) -> Self {
126 Self {
127 recipe,
128 cache: ImageSymbolCache::new(),
129 original_registers: None,
130 index: None,
131 previous_stack_pointer: None,
132 }
133 }
134
135 pub fn execute(
140 &mut self,
141 vmi: &VmiState<Driver, Os>,
142 ) -> Result<Option<<<Driver as VmiDriver>::Architecture as Architecture>::Registers>, VmiError>
143 {
144 if self.has_stack_pointer_decreased(vmi) {
147 return Ok(None);
148 }
149
150 let index = match &mut self.index {
151 Some(index) => index,
152 None => {
153 self.original_registers = Some(*vmi.registers());
154 self.index.insert(0)
155 }
156 };
157
158 if let Some(step) = self.recipe.steps.get(*index) {
159 tracing::debug!(index, "recipe step");
160
161 let mut registers = *vmi.registers();
162
163 let next = step(&mut RecipeContext {
164 vmi,
165 registers: &mut registers,
166 data: &mut self.recipe.data,
167 cache: &mut self.cache,
168 })?;
169
170 self.previous_stack_pointer = Some(Va(registers.stack_pointer()));
172
173 match next {
174 RecipeControlFlow::Continue => {
175 *index += 1;
176 return Ok(Some(registers));
177 }
178 RecipeControlFlow::Break => {}
179 RecipeControlFlow::Repeat => {
180 return Ok(Some(registers));
181 }
182 RecipeControlFlow::Skip => {
183 *index += 2;
184 return Ok(Some(registers));
185 }
186 RecipeControlFlow::Goto(i) => {
187 *index = i;
188 return Ok(Some(registers));
189 }
190 }
191 }
192
193 tracing::debug!(
194 result = %Hex(vmi.registers().result()),
195 "recipe finished"
196 );
197
198 self.index = None;
199 let original_registers = self.original_registers.expect("original_registers");
200 Ok(Some(original_registers))
201 }
202
203 fn has_stack_pointer_decreased(&self, vmi: &VmiState<Driver, Os>) -> bool {
208 let previous_stack_pointer = match self.previous_stack_pointer {
209 Some(previous_stack_pointer) => previous_stack_pointer,
210 None => return false,
211 };
212
213 let current_stack_pointer = Va(vmi.registers().stack_pointer());
214
215 let result = current_stack_pointer < previous_stack_pointer;
216 if result {
217 tracing::trace!(
218 %previous_stack_pointer,
219 %current_stack_pointer,
220 "stack pointer decreased"
221 );
222 }
223
224 result
225 }
226
227 pub fn reset(&mut self) {
229 self.index = None;
230 }
231
232 pub fn done(&self) -> bool {
234 self.index.is_none() && self.original_registers.is_some()
235 }
236}