1use alloc::{string::ToString, sync::Arc};
2use core::{
3 fmt::Debug,
4 ops::{Deref, DerefMut},
5 sync::atomic::AtomicUsize,
6};
7
8use lock_api::RawMutex;
9use yaxpeax_arch::LengthedInstruction;
10
11use crate::{KprobeAuxiliaryOps, KprobeOps, ProbeBasic, ProbeBuilder, arch::ExecMemType};
12
13const EBREAK_INST: u8 = 0xcc; const MAX_INSTRUCTION_SIZE: usize = 15; pub struct Probe<L: RawMutex + 'static, F: KprobeAuxiliaryOps> {
18 basic: ProbeBasic<L>,
19 point: Arc<X86ProbePoint<F>>,
20}
21
22#[derive(Debug)]
24pub struct X86ProbePoint<F: KprobeAuxiliaryOps> {
25 addr: usize,
26 old_instruction_ptr: ExecMemType<F>,
27 old_instruction_len: usize,
28 user_pid: Option<i32>,
29 dynamic_user_ptr: AtomicUsize,
31 _marker: core::marker::PhantomData<F>,
32}
33
34impl<F: KprobeAuxiliaryOps> Drop for X86ProbePoint<F> {
35 fn drop(&mut self) {
36 let address = self.addr;
37 F::set_writeable_for_address(address, self.old_instruction_len, self.user_pid, |ptr| {
38 unsafe {
40 core::ptr::copy_nonoverlapping(
41 self.old_instruction_ptr.as_ptr(),
42 ptr,
43 self.old_instruction_len,
44 );
45 }
46 });
47
48 let dyn_ptr = self.dynamic_user_ptr();
50 if dyn_ptr != 0 {
51 F::free_user_exec_memory(self.user_pid, dyn_ptr as *mut u8);
52 }
53 log::trace!(
54 "X86KprobePoint::drop: Restored instruction at address: {:#x}",
55 address
56 );
57 }
58}
59
60impl<L: RawMutex + 'static, F: KprobeAuxiliaryOps> Debug for Probe<L, F> {
61 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
62 f.debug_struct("Probe")
63 .field("basic", &self.basic)
64 .field("point", &self.point)
65 .finish()
66 }
67}
68
69impl<L: RawMutex + 'static, F: KprobeAuxiliaryOps> Deref for Probe<L, F> {
70 type Target = ProbeBasic<L>;
71
72 fn deref(&self) -> &Self::Target {
73 &self.basic
74 }
75}
76
77impl<L: RawMutex + 'static, F: KprobeAuxiliaryOps> DerefMut for Probe<L, F> {
78 fn deref_mut(&mut self) -> &mut Self::Target {
79 &mut self.basic
80 }
81}
82
83impl<F: KprobeAuxiliaryOps> ProbeBuilder<F> {
84 pub(crate) fn install<L: RawMutex + 'static>(self) -> (Probe<L, F>, Arc<X86ProbePoint<F>>) {
85 let probe_point = match &self.probe_point {
86 Some(point) => point.clone(),
87 None => self.replace_inst(),
88 };
89 let probe = Probe {
90 basic: ProbeBasic::from(self),
91 point: probe_point.clone(),
92 };
93 (probe, probe_point)
94 }
95
96 fn replace_inst(&self) -> Arc<X86ProbePoint<F>> {
98 let address = self.symbol_addr + self.offset;
99 let inst_tmp = super::alloc_exec_memory::<F>(self.user_pid);
100
101 F::copy_memory(
102 address as *const u8,
103 inst_tmp.as_ptr(),
104 MAX_INSTRUCTION_SIZE,
105 self.user_pid,
106 );
107
108 let buf = unsafe { core::slice::from_raw_parts(inst_tmp.as_ptr(), MAX_INSTRUCTION_SIZE) };
109
110 let decoder = yaxpeax_x86::amd64::InstDecoder::default();
111 let inst = decoder.decode_slice(buf).unwrap();
112 let len = inst.len().to_const();
113 log::trace!("inst: {:?}, len: {:?}", inst.to_string(), len);
114
115 let point = Arc::new(X86ProbePoint {
116 addr: address,
117 old_instruction_ptr: inst_tmp,
118 old_instruction_len: len as usize,
119 user_pid: self.user_pid,
120 dynamic_user_ptr: AtomicUsize::new(0),
121 _marker: core::marker::PhantomData,
122 });
123
124 F::set_writeable_for_address(address, len as usize, self.user_pid, |ptr| unsafe {
125 core::ptr::write(ptr, EBREAK_INST);
126 });
127 log::trace!(
128 "Kprobe::install: address: {:#x}, func_name: {:?}",
129 address,
130 self.symbol
131 );
132 point
133 }
134}
135
136impl<L: RawMutex + 'static, F: KprobeAuxiliaryOps> Probe<L, F> {
137 pub fn probe_point(&self) -> &Arc<X86ProbePoint<F>> {
139 &self.point
140 }
141}
142
143impl<F: KprobeAuxiliaryOps> KprobeOps for X86ProbePoint<F> {
144 fn return_address(&self) -> usize {
145 self.addr + self.old_instruction_len
146 }
147
148 fn single_step_address(&self) -> usize {
149 self.old_instruction_ptr.as_ptr() as usize
150 }
151
152 fn debug_address(&self) -> usize {
153 let dynamic_user_ptr = self.dynamic_user_ptr();
154 if dynamic_user_ptr != 0 {
155 dynamic_user_ptr + self.old_instruction_len
156 } else {
157 self.old_instruction_ptr.as_ptr() as usize + self.old_instruction_len
158 }
159 }
160
161 fn break_address(&self) -> usize {
162 self.addr
163 }
164
165 fn dynamic_user_ptr(&self) -> usize {
166 self.dynamic_user_ptr
167 .load(core::sync::atomic::Ordering::SeqCst)
168 }
169
170 fn set_dynamic_user_ptr(&self, ptr: usize) -> usize {
171 self.dynamic_user_ptr
172 .store(ptr, core::sync::atomic::Ordering::SeqCst);
173 ptr + self.old_instruction_len
174 }
175
176 fn old_instruction_len(&self) -> usize {
177 self.old_instruction_len
178 }
179
180 fn pid(&self) -> Option<i32> {
181 self.user_pid
182 }
183}
184
185pub(crate) fn setup_single_step(pt_regs: &mut PtRegs, single_step_address: usize) {
189 pt_regs.update_pc(single_step_address);
190 pt_regs.set_single_step(true);
191}
192
193pub(crate) fn clear_single_step(pt_regs: &mut PtRegs, single_step_address: usize) {
197 pt_regs.update_pc(single_step_address);
198 pt_regs.set_single_step(false);
199}
200
201#[repr(C)]
203#[derive(Debug, Copy, Clone)]
204#[allow(missing_docs)]
205pub struct PtRegs {
206 pub r15: usize,
207 pub r14: usize,
208 pub r13: usize,
209 pub r12: usize,
210 pub rbp: usize,
211 pub rbx: usize,
212 pub r11: usize,
213 pub r10: usize,
214 pub r9: usize,
215 pub r8: usize,
216 pub rax: usize,
217 pub rcx: usize,
218 pub rdx: usize,
219 pub rsi: usize,
220 pub rdi: usize,
221 pub orig_rax: usize,
224 pub rip: usize,
225 pub cs: usize,
226 pub rflags: usize,
227 pub rsp: usize,
228 pub ss: usize,
229}
230
231impl PtRegs {
232 pub(crate) fn break_address(&self) -> usize {
233 self.rip - 1 }
235
236 pub(crate) fn debug_address(&self) -> usize {
237 self.rip }
239
240 pub(crate) fn update_pc(&mut self, pc: usize) {
241 self.rip = pc as _;
242 }
243
244 pub(crate) fn set_single_step(&mut self, enable: bool) {
245 if enable {
246 self.rflags |= 0x100;
247 } else {
248 self.rflags &= !0x100;
249 }
250 }
251
252 pub(crate) fn sp(&self) -> usize {
253 self.rsp
254 }
255
256 pub fn first_ret_value(&self) -> usize {
258 self.rax
259 }
260
261 pub fn second_ret_value(&self) -> usize {
263 self.rdx
264 }
265}
266
267const KERNEL_DS: usize = 24; #[unsafe(naked)]
271pub(crate) unsafe extern "C" fn arch_rethook_trampoline<
272 L: RawMutex + 'static,
273 F: KprobeAuxiliaryOps + 'static,
274>() {
275 core::arch::naked_asm!(
276 "pushq $0",
279 "pushq ${kernel_data_segment}", "pushq %rsp",
282 "pushfq", "subq $24, %rsp", "pushq %rdi",
287 "pushq %rsi",
288 "pushq %rdx",
289 "pushq %rcx",
290 "pushq %rax",
291 "pushq %r8",
292 "pushq %r9",
293 "pushq %r10",
294 "pushq %r11",
295 "pushq %rbx",
296 "pushq %rbp",
297 "pushq %r12",
298 "pushq %r13",
299 "pushq %r14",
300 "pushq %r15",
301 "movq %rsp, %rdi",
304 "call {rethook_trampoline_callback}",
305 "popq %r15",
307 "popq %r14",
308 "popq %r13",
309 "popq %r12",
310 "popq %rbp",
311 "popq %rbx",
312 "popq %r11",
313 "popq %r10",
314 "popq %r9",
315 "popq %r8",
316 "popq %rax",
317 "popq %rcx",
318 "popq %rdx",
319 "popq %rsi",
320 "popq %rdi",
321 "addq $24, %rsp",
323
324 "addq $16, %rsp",
326
327 "popfq",
328 "ret",
329 kernel_data_segment = const KERNEL_DS, rethook_trampoline_callback = sym arch_rethook_trampoline_callback::<L, F>,
332 options(att_syntax)
333 )
334}
335
336pub(crate) fn arch_rethook_trampoline_callback<
337 L: RawMutex + 'static,
338 F: KprobeAuxiliaryOps + 'static,
339>(
340 pt_regs: &mut PtRegs,
341) -> usize {
342 pt_regs.rip = arch_rethook_trampoline::<L, F> as *const () as usize; pt_regs.orig_rax = usize::MAX;
344 pt_regs.rsp += 16; let pt_regs_pointer = unsafe { (pt_regs as *mut PtRegs).add(1) as *mut usize };
347
348 let correct_ret_addr =
349 super::retprobe::rethook_trampoline_handler::<L, F>(pt_regs, pt_regs_pointer as _);
350 pt_regs.ss = pt_regs.rflags; correct_ret_addr
352}
353
354pub(crate) fn arch_rethook_fixup_return(pt_regs: &mut PtRegs, correct_ret_addr: usize) {
356 let pt_regs_pointer = unsafe { (pt_regs as *mut PtRegs).add(1) as *mut usize };
357 unsafe {
358 *pt_regs_pointer = correct_ret_addr;
360 }
361}
362
363pub(crate) fn arch_rethook_prepare<L: RawMutex + 'static, F: KprobeAuxiliaryOps + 'static>(
365 kretprobe_instance: &mut super::retprobe::RetprobeInstance,
366 pt_regs: &mut PtRegs,
367) {
368 let sp = pt_regs.sp();
369 let stack = unsafe { &mut *(sp as *mut usize) };
370 kretprobe_instance.ret_addr = *stack;
373 kretprobe_instance.frame = sp;
374 *stack = arch_rethook_trampoline::<L, F> as *const () as usize;
376}