memflow_win32/win32/
keyboard.rs

1/*!
2Module for reading a target's keyboard state.
3
4The `gafAsyncKeyState` array contains the current Keyboard state on Windows targets.
5This array will internally be read by the [`GetAsyncKeyState()`](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getasynckeystate) function of Windows.
6
7Although the gafAsyncKeyState array is exported by the win32kbase.sys kernel module it is only properly mapped into user mode processes.
8Therefor the Keyboard will by default find the winlogon.exe or wininit.exe process and use it as a proxy to read the data.
9
10# Examples:
11
12```
13use std::{thread, time};
14
15use memflow::mem::{PhysicalMemory, VirtualTranslate2};
16use memflow::os::{Keyboard, KeyboardState};
17use memflow_win32::win32::{Win32Kernel, Win32Keyboard};
18
19fn test<T: 'static + PhysicalMemory + Clone, V: 'static + VirtualTranslate2 + Clone>(kernel: &mut Win32Kernel<T, V>) {
20    let mut kbd = Win32Keyboard::with_kernel_ref(kernel).unwrap();
21
22    loop {
23        let kbs = kbd.state().unwrap();
24        println!("space down: {:?}", kbs.is_down(0x20)); // VK_SPACE
25        thread::sleep(time::Duration::from_millis(1000));
26    }
27}
28```
29*/
30use super::{Win32Kernel, Win32ProcessInfo, Win32VirtualTranslate};
31
32use memflow::cglue::*;
33use memflow::error::PartialResultExt;
34use memflow::error::{Error, ErrorKind, ErrorOrigin, Result};
35use memflow::mem::{MemoryView, PhysicalMemory, VirtualDma, VirtualTranslate2};
36use memflow::os::keyboard::*;
37use memflow::prelude::{ExportInfo, ModuleInfo, Os, Pid, Process};
38use memflow::types::{umem, Address};
39
40#[cfg(feature = "plugins")]
41use memflow::cglue;
42
43use log::debug;
44use std::convert::TryInto;
45
46#[cfg(feature = "plugins")]
47cglue_impl_group!(Win32Keyboard<T>, IntoKeyboard);
48
49/// Interface for accessing the target's keyboard state.
50#[derive(Clone, Debug)]
51pub struct Win32Keyboard<T> {
52    pub virt_mem: T,
53    key_state_addr: Address,
54}
55
56impl<T: 'static + PhysicalMemory + Clone, V: 'static + VirtualTranslate2 + Clone>
57    Win32Keyboard<VirtualDma<T, V, Win32VirtualTranslate>>
58{
59    pub fn with_kernel(mut kernel: Win32Kernel<T, V>) -> Result<Self> {
60        let (user_process_info, key_state_addr) = Self::find_keystate(&mut kernel)?;
61
62        let (phys_mem, vat) = kernel.virt_mem.into_inner();
63        let virt_mem = VirtualDma::with_vat(
64            phys_mem,
65            user_process_info.base_info.proc_arch,
66            user_process_info.translator(),
67            vat,
68        );
69
70        Ok(Self {
71            virt_mem,
72            key_state_addr,
73        })
74    }
75
76    /// Consumes this keyboard, returning the underlying memory and vat objects
77    pub fn into_inner(self) -> (T, V) {
78        self.virt_mem.into_inner()
79    }
80}
81
82impl<'a, T: 'static + PhysicalMemory + Clone, V: 'static + VirtualTranslate2 + Clone>
83    Win32Keyboard<VirtualDma<Fwd<&'a mut T>, Fwd<&'a mut V>, Win32VirtualTranslate>>
84{
85    /// Constructs a new keyboard object by borrowing a kernel object.
86    ///
87    /// Internally this will create a `VirtualDma` object that also
88    /// borrows the PhysicalMemory and Vat objects from the kernel.
89    ///
90    /// The resulting process object is NOT cloneable due to the mutable borrowing.
91    ///
92    /// When u need a cloneable Process u have to use the `::with_kernel` function
93    /// which will move the kernel object.
94    pub fn with_kernel_ref(kernel: &'a mut Win32Kernel<T, V>) -> Result<Self> {
95        let (user_process_info, key_state_addr) = Self::find_keystate(kernel)?;
96
97        let (phys_mem, vat) = kernel.virt_mem.mem_vat_pair();
98        let virt_mem = VirtualDma::with_vat(
99            phys_mem.forward_mut(),
100            user_process_info.base_info.proc_arch,
101            user_process_info.translator(),
102            vat.forward_mut(),
103        );
104
105        Ok(Self {
106            virt_mem,
107            key_state_addr,
108        })
109    }
110}
111
112impl<T> Win32Keyboard<T> {
113    fn find_keystate<
114        P: 'static + PhysicalMemory + Clone,
115        V: 'static + VirtualTranslate2 + Clone,
116    >(
117        kernel: &mut Win32Kernel<P, V>,
118    ) -> Result<(Win32ProcessInfo, Address)> {
119        let win32kbase_module_info = kernel.module_by_name("win32kbase.sys")?;
120        debug!("found win32kbase.sys: {:?}", win32kbase_module_info);
121
122        let procs = kernel.process_info_list()?;
123
124        let gaf = procs
125            .iter()
126            .filter(|p| {
127                p.name.as_ref() == "winlogon.exe"
128                    || p.name.as_ref() == "explorer.exe"
129                    || p.name.as_ref() == "taskhostw.exe"
130                    || p.name.as_ref() == "smartscreen.exe"
131                    || p.name.as_ref() == "dwm.exe"
132            })
133            .find_map(|p| Self::find_in_user_process(kernel, &win32kbase_module_info, p.pid).ok())
134            .ok_or_else(|| {
135                Error(ErrorOrigin::OsLayer, ErrorKind::ExportNotFound)
136                    .log_info("unable to find any proxy process that contains gafAsyncKeyState")
137            })?;
138
139        Ok((gaf.0, gaf.1))
140    }
141
142    fn find_in_user_process<
143        P: 'static + PhysicalMemory + Clone,
144        V: 'static + VirtualTranslate2 + Clone,
145    >(
146        kernel: &mut Win32Kernel<P, V>,
147        win32kbase_module_info: &ModuleInfo,
148        pid: Pid,
149    ) -> Result<(Win32ProcessInfo, Address)> {
150        let user_process_info = kernel.process_info_by_pid(pid)?;
151        let user_process_info_win32 =
152            kernel.process_info_from_base_info(user_process_info.clone())?;
153        let mut user_process = kernel.process_by_info(user_process_info)?;
154        debug!(
155            "trying to find gaf signature in user proxy process `{}`",
156            user_process.info().name.as_ref()
157        );
158
159        // TODO: lazy
160        let export_addr = Self::find_gaf_pe(&mut user_process.virt_mem, win32kbase_module_info)
161            .or_else(|_| Self::find_gaf_sig(&mut user_process.virt_mem, win32kbase_module_info))?;
162        debug!(
163            "found gaf signature in user proxy process `{}` at {:x}",
164            user_process.info().name.as_ref(),
165            export_addr
166        );
167
168        Ok((
169            user_process_info_win32,
170            win32kbase_module_info.base + export_addr,
171        ))
172    }
173
174    fn find_gaf_pe(
175        virt_mem: &mut impl MemoryView,
176        win32kbase_module_info: &ModuleInfo,
177    ) -> Result<umem> {
178        let mut offset = None;
179        let callback = &mut |export: ExportInfo| {
180            if export.name.as_ref() == "gafAsyncKeyState" {
181                offset = Some(export.offset);
182                false
183            } else {
184                true
185            }
186        };
187        memflow::os::util::module_export_list_callback(
188            virt_mem,
189            win32kbase_module_info,
190            callback.into(),
191        )?;
192        offset.ok_or_else(|| {
193            Error(ErrorOrigin::OsLayer, ErrorKind::ExportNotFound)
194                .log_info("unable to find gafAsyncKeyState")
195        })
196    }
197
198    // TODO: replace with a custom signature scanning crate
199    #[cfg(feature = "regex")]
200    fn find_gaf_sig(
201        virt_mem: &mut impl MemoryView,
202        win32kbase_module_info: &ModuleInfo,
203    ) -> Result<umem> {
204        use ::regex::bytes::*;
205
206        let module_buf = virt_mem
207            .read_raw(
208                win32kbase_module_info.base,
209                win32kbase_module_info.size.try_into().unwrap(),
210            )
211            .data_part()?;
212
213        // 48 8B 05 ? ? ? ? 48 89 81 ? ? 00 00 48 8B 8F + 0x3
214        let re = Regex::new("(?-u)\\x48\\x8B\\x05(?s:.)(?s:.)(?s:.)(?s:.)\\x48\\x89\\x81(?s:.)(?s:.)\\x00\\x00\\x48\\x8B\\x8F")
215                    .map_err(|_| Error(ErrorOrigin::OsLayer, ErrorKind::Encoding).log_info("malformed gafAsyncKeyState signature"))?;
216        let buf_offs = re
217            .find(module_buf.as_slice())
218            .ok_or_else(|| {
219                Error(ErrorOrigin::OsLayer, ErrorKind::NotFound)
220                    .log_info("unable to find gafAsyncKeyState signature")
221            })?
222            .start()
223            + 0x3;
224
225        // compute rip relative addr
226        let export_offs = buf_offs as u32
227            + u32::from_le_bytes(module_buf[buf_offs..buf_offs + 4].try_into().unwrap())
228            + 0x4;
229        debug!("gafAsyncKeyState export found at: {:x}", export_offs);
230        Ok(export_offs as umem)
231    }
232
233    #[cfg(not(feature = "regex"))]
234    fn find_gaf_sig(
235        virt_mem: &mut impl MemoryView,
236        win32kbase_module_info: &ModuleInfo,
237    ) -> Result<umem> {
238        Err(
239            Error(ErrorOrigin::OsLayer, ErrorKind::UnsupportedOptionalFeature)
240                .log_error("signature scanning requires std"),
241        )
242    }
243}
244
245macro_rules! get_ks_byte {
246    ($vk:expr) => {
247        $vk * 2 / 8
248    };
249}
250
251macro_rules! get_ks_down_bit {
252    ($vk:expr) => {
253        1 << (($vk % 4) * 2)
254    };
255}
256
257macro_rules! is_key_down {
258    ($ks:expr, $vk:expr) => {
259        ($ks[get_ks_byte!($vk) as usize] & get_ks_down_bit!($vk)) != 0
260    };
261}
262
263macro_rules! set_key_down {
264    ($ks:expr, $vk:expr, $down:expr) => {
265        if $down {
266            ($ks[get_ks_byte!($vk) as usize] |= get_ks_down_bit!($vk))
267        } else {
268            ($ks[get_ks_byte!($vk) as usize] &= !get_ks_down_bit!($vk))
269        }
270    };
271}
272
273impl<T: MemoryView> Keyboard for Win32Keyboard<T> {
274    type KeyboardStateType = Win32KeyboardState;
275
276    /// Reads the gafAsyncKeyState global from the win32kbase.sys kernel module and
277    /// returns true wether the given key was pressed.
278    /// This function accepts a valid microsoft virtual keycode.
279    /// In case of supplying a invalid key this function will just return false cleanly.
280    ///
281    /// A list of all Keycodes can be found on the [msdn](https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes).
282    fn is_down(&mut self, vk: i32) -> bool {
283        if !(0..=256).contains(&vk) {
284            false
285        } else if let Ok(buffer) = self
286            .virt_mem
287            .read::<[u8; 256 * 2 / 8]>(self.key_state_addr)
288            .data_part()
289        {
290            is_key_down!(buffer, vk)
291        } else {
292            false
293        }
294    }
295
296    /// Writes the gafAsyncKeyState global to the win32kbase.sys kernel module.
297    ///
298    /// # Remarks:
299    ///
300    /// This will not enforce key presses in all applications on Windows.
301    /// It will only modify calls to GetKeyState / GetAsyncKeyState.
302    fn set_down(&mut self, vk: i32, down: bool) {
303        if (0..=256).contains(&vk) {
304            if let Ok(mut buffer) = self.virt_mem.read::<[u8; 256 * 2 / 8]>(self.key_state_addr) {
305                set_key_down!(buffer, vk, down);
306                self.virt_mem.write(self.key_state_addr, &buffer).ok();
307            }
308        }
309    }
310
311    /// Reads the gafAsyncKeyState global from the win32kbase.sys kernel module.
312    fn state(&mut self) -> memflow::error::Result<Self::KeyboardStateType> {
313        let buffer: [u8; 256 * 2 / 8] = self.virt_mem.read(self.key_state_addr)?;
314        Ok(Win32KeyboardState { buffer })
315    }
316}
317
318/// Represents the current Keyboardstate.
319///
320/// Internally this will hold a 256 * 2 / 8 byte long copy of the gafAsyncKeyState array from the target.
321#[derive(Clone)]
322pub struct Win32KeyboardState {
323    buffer: [u8; 256 * 2 / 8],
324}
325
326impl KeyboardState for Win32KeyboardState {
327    /// Returns true wether the given key was pressed.
328    /// This function accepts a valid microsoft virtual keycode.
329    /// In case of supplying a invalid key this function will just return false cleanly.
330    ///
331    /// A list of all Keycodes can be found on the [msdn](https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes).
332    fn is_down(&self, vk: i32) -> bool {
333        if !(0..=256).contains(&vk) {
334            false
335        } else {
336            is_key_down!(self.buffer, vk)
337        }
338    }
339}