Skip to main content

imping_hook/
context.rs

1//! The CPU register context passed to register-context injections.
2
3/// The x86-64 registers at an injection site: general-purpose registers,
4/// flags, instruction pointer and SSE registers.
5///
6/// [`inspect`](crate::InjectionBuilder::inspect) callbacks read it;
7/// [`modify`](crate::InjectionBuilder::modify) callbacks can also change it
8/// before the original code resumes, and redirect with [`rip`](Self::rip).
9// The stub reads and writes this layout on the stack; don't reorder fields.
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11#[repr(C)]
12pub struct Context {
13    /// The `rflags` register.
14    pub rflags: u64,
15    /// The `rax` register.
16    pub rax: u64,
17    /// The `rbx` register.
18    pub rbx: u64,
19    /// The `rcx` register.
20    pub rcx: u64,
21    /// The `rdx` register.
22    pub rdx: u64,
23    /// The `rsi` register.
24    pub rsi: u64,
25    /// The `rdi` register.
26    pub rdi: u64,
27    /// The `rbp` register.
28    pub rbp: u64,
29    /// The stack pointer at the injection site.
30    ///
31    /// In `modify`, a changed `rsp` only takes effect along with a changed
32    /// [`rip`](Self::rip); otherwise the original code resumes on its own stack.
33    pub rsp: u64,
34    /// The `r8` register.
35    pub r8: u64,
36    /// The `r9` register.
37    pub r9: u64,
38    /// The `r10` register.
39    pub r10: u64,
40    /// The `r11` register.
41    pub r11: u64,
42    /// The `r12` register.
43    pub r12: u64,
44    /// The `r13` register.
45    pub r13: u64,
46    /// The `r14` register.
47    pub r14: u64,
48    /// The `r15` register.
49    pub r15: u64,
50    /// The address of the injection site, the patched instruction.
51    ///
52    /// In `modify`, setting it to another address jumps there with the (maybe
53    /// changed) [`rsp`](Self::rsp), and the displaced instructions don't run.
54    pub rip: u64,
55    /// The SSE registers `xmm0`–`xmm15`.
56    pub xmm: [Xmm; 16],
57}
58
59/// The 128 bits of one SSE (`xmm`) register.
60///
61/// Stored as raw little-endian bytes; the helpers read and build them as
62/// scalars or packed lanes.
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64#[repr(C)]
65pub struct Xmm(pub [u8; 16]);
66
67impl Xmm {
68    /// An all-zero register.
69    pub const ZERO: Xmm = Xmm([0; 16]);
70
71    /// Returns the raw little-endian bytes.
72    pub fn to_bytes(self) -> [u8; 16] {
73        self.0
74    }
75
76    /// Builds a register from raw little-endian bytes.
77    pub fn from_bytes(bytes: [u8; 16]) -> Xmm {
78        Xmm(bytes)
79    }
80
81    /// Returns all 128 bits as an unsigned integer.
82    pub fn u128(self) -> u128 {
83        u128::from_le_bytes(self.0)
84    }
85
86    /// Builds a register from a 128-bit unsigned integer.
87    pub fn from_u128(value: u128) -> Xmm {
88        Xmm(value.to_le_bytes())
89    }
90
91    /// Returns the low lane as an `f32`, a scalar single as after `movss`.
92    pub fn f32(self) -> f32 {
93        f32::from_le_bytes([self.0[0], self.0[1], self.0[2], self.0[3]])
94    }
95
96    /// Returns the low lane as an `f64`, a scalar double as after `movsd`.
97    pub fn f64(self) -> f64 {
98        f64::from_le_bytes([
99            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7],
100        ])
101    }
102
103    /// Returns the four packed `f32` lanes.
104    pub fn f32x4(self) -> [f32; 4] {
105        let mut out = [0.0f32; 4];
106        for (lane, value) in out.iter_mut().enumerate() {
107            let base = lane * 4;
108            *value = f32::from_le_bytes([
109                self.0[base],
110                self.0[base + 1],
111                self.0[base + 2],
112                self.0[base + 3],
113            ]);
114        }
115        out
116    }
117
118    /// Returns the two packed `f64` lanes.
119    pub fn f64x2(self) -> [f64; 2] {
120        let mut out = [0.0f64; 2];
121        for (lane, value) in out.iter_mut().enumerate() {
122            let base = lane * 8;
123            let mut bytes = [0u8; 8];
124            bytes.copy_from_slice(&self.0[base..base + 8]);
125            *value = f64::from_le_bytes(bytes);
126        }
127        out
128    }
129
130    /// Returns the four packed `i32` lanes.
131    pub fn i32x4(self) -> [i32; 4] {
132        let mut out = [0i32; 4];
133        for (lane, value) in out.iter_mut().enumerate() {
134            let base = lane * 4;
135            *value = i32::from_le_bytes([
136                self.0[base],
137                self.0[base + 1],
138                self.0[base + 2],
139                self.0[base + 3],
140            ]);
141        }
142        out
143    }
144
145    /// Returns the two packed `u64` lanes.
146    pub fn u64x2(self) -> [u64; 2] {
147        let mut out = [0u64; 2];
148        for (lane, value) in out.iter_mut().enumerate() {
149            let base = lane * 8;
150            let mut bytes = [0u8; 8];
151            bytes.copy_from_slice(&self.0[base..base + 8]);
152            *value = u64::from_le_bytes(bytes);
153        }
154        out
155    }
156
157    /// Sets the low `f32` lane, leaving the rest unchanged.
158    pub fn set_f32(&mut self, value: f32) {
159        self.0[0..4].copy_from_slice(&value.to_le_bytes());
160    }
161
162    /// Sets the low `f64` lane, leaving the rest unchanged.
163    pub fn set_f64(&mut self, value: f64) {
164        self.0[0..8].copy_from_slice(&value.to_le_bytes());
165    }
166
167    /// Builds a register with `value` in the low `f32` lane and zeros elsewhere.
168    pub fn from_f32(value: f32) -> Xmm {
169        let mut xmm = Xmm::ZERO;
170        xmm.set_f32(value);
171        xmm
172    }
173
174    /// Builds a register with `value` in the low `f64` lane and zeros elsewhere.
175    pub fn from_f64(value: f64) -> Xmm {
176        let mut xmm = Xmm::ZERO;
177        xmm.set_f64(value);
178        xmm
179    }
180
181    /// Builds a register from four packed `f32` lanes.
182    pub fn from_f32x4(lanes: [f32; 4]) -> Xmm {
183        let mut bytes = [0u8; 16];
184        for (lane, value) in lanes.iter().enumerate() {
185            bytes[lane * 4..lane * 4 + 4].copy_from_slice(&value.to_le_bytes());
186        }
187        Xmm(bytes)
188    }
189
190    /// Builds a register from two packed `f64` lanes.
191    pub fn from_f64x2(lanes: [f64; 2]) -> Xmm {
192        let mut bytes = [0u8; 16];
193        for (lane, value) in lanes.iter().enumerate() {
194            bytes[lane * 8..lane * 8 + 8].copy_from_slice(&value.to_le_bytes());
195        }
196        Xmm(bytes)
197    }
198}
199
200impl Default for Xmm {
201    fn default() -> Self {
202        Xmm::ZERO
203    }
204}