1mod phantom_member;
8
9pub use phantom_member::PhantomMember;
10
11use crate::rel::ResolvableAddress;
12use crate::rel::id::{DataBaseError, ID, RelocationID};
13use crate::rel::module::{ModuleState, ModuleStateError};
14use crate::rel::offset::{Offset, VariantOffset};
15use crate::rel::version::Version;
16use core::ffi::c_void;
17use core::marker::PhantomData;
18use core::mem;
19use core::num::NonZeroUsize;
20use core::ptr::NonNull;
21
22pub const NOP: u8 = 0x90;
23pub const NOP2: [u8; 2] = [0x66, 0x90];
24pub const NOP3: [u8; 3] = [0x0F, 0x1F, 0x00];
25pub const NOP4: [u8; 4] = [0x0F, 0x1F, 0x40, 0x00];
26pub const NOP5: [u8; 5] = [0x0F, 0x1F, 0x44, 0x00, 0x00];
27pub const NOP6: [u8; 6] = [0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00];
28pub const NOP7: [u8; 7] = [0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00];
29pub const NOP8: [u8; 8] = [0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00];
30pub const NOP9: [u8; 9] = [0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00];
31pub const JMP8: u8 = 0xEB;
32pub const JMP32: u8 = 0xE9;
33pub const RET: u8 = 0xC3;
34pub const INT3: u8 = 0xCC;
35
36#[inline]
37unsafe fn enable_write_permission(
38 addr: *const c_void,
39 len: usize,
40) -> windows::core::Result<windows::Win32::System::Memory::PAGE_PROTECTION_FLAGS> {
41 unsafe {
42 use windows::Win32::System::Memory::{
43 PAGE_EXECUTE_READWRITE, PAGE_PROTECTION_FLAGS, VirtualProtect,
44 };
45 let mut old_protection = PAGE_PROTECTION_FLAGS(0);
46
47 VirtualProtect(addr, len, PAGE_EXECUTE_READWRITE, &mut old_protection)?;
49 Ok(old_protection)
50 }
51}
52
53#[inline]
54unsafe fn restore_memory_protection(
55 addr: *const c_void,
56 len: usize,
57 old_protection: windows::Win32::System::Memory::PAGE_PROTECTION_FLAGS,
58) -> windows::core::Result<()> {
59 unsafe {
60 use windows::Win32::System::Memory::{PAGE_PROTECTION_FLAGS, VirtualProtect};
61 let mut temp = PAGE_PROTECTION_FLAGS(0);
62
63 VirtualProtect(addr, len, old_protection, &mut temp)
65 }
66}
67
68#[inline]
69unsafe fn safe_write<T>(dst: *mut T, src: *const T, len: usize) -> windows::core::Result<()> {
70 unsafe {
71 let old_protection = enable_write_permission(dst as _, len)?;
72 core::ptr::copy_nonoverlapping(src, dst, len);
73 restore_memory_protection(dst.cast(), len, old_protection)
74 }
75}
76
77#[inline]
78pub(crate) unsafe fn safe_write_value<T>(dst: *mut T, src: &T) -> windows::core::Result<()> {
79 unsafe { safe_write(dst, src, core::mem::size_of::<T>()) }
80}
81
82#[inline]
83unsafe fn safe_fill(dst: *mut c_void, value: u8, len: usize) -> windows::core::Result<()> {
84 unsafe {
85 let old_protection = enable_write_permission(dst, len)?;
86 core::ptr::write_bytes(dst, value, len);
87 restore_memory_protection(dst, len, old_protection)
88 }
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92pub struct Relocation {
93 _impl: NonNull<c_void>,
94 _cast_target: PhantomData<c_void>,
96}
97
98impl Relocation {
99 #[inline]
100 pub const fn new(address: NonNull<c_void>) -> Self {
101 Self { _impl: address, _cast_target: PhantomData }
102 }
103
104 #[inline]
109 pub fn from_id_offset<A1, A2>(id: A1, offset: A2) -> Result<Self, DataBaseError>
110 where
111 A1: ResolvableAddress,
112 A2: ResolvableAddress,
113 {
114 Ok(Self {
115 _impl: unsafe { id.address()?.byte_add(offset.offset()?.get()) },
116 _cast_target: PhantomData,
117 })
118 }
119
120 #[inline]
124 pub const fn cast<U>(&self) -> NonNull<U> {
125 self._impl.cast()
126 }
127
128 #[inline]
129 pub fn write<U>(&self, data: &U) {
130 let _ = unsafe { safe_write_value(self._impl.cast::<U>().as_mut(), data) };
131 }
132
133 #[inline]
134 pub fn write_bytes(&self, data: &[u8]) {
135 let _ = unsafe { safe_write(self._impl.cast::<u8>().as_mut(), data.as_ptr(), data.len()) };
136 }
137
138 #[inline]
139 pub fn swap_as_vfn<T>(&mut self, idx: usize, new_fn: *const ()) -> NonNull<c_void> {
140 const PTR_SIZE: usize = mem::size_of::<usize>();
141
142 let mut old_fn = unsafe { self._impl.byte_add(PTR_SIZE * idx) };
143 let _ = unsafe { safe_write(old_fn.as_mut(), new_fn.cast(), PTR_SIZE) };
144 old_fn
145 }
146
147 #[inline]
148 pub fn write_fill(&mut self, value: u8, count: usize) {
149 unsafe {
150 let _ = safe_fill(self._impl.as_mut(), value, count);
151 }
152 }
153}
154
155impl ResolvableAddress for Relocation {
156 #[inline]
158 fn address(&self) -> Result<NonNull<c_void>, DataBaseError> {
159 Ok(self._impl)
160 }
161
162 #[inline]
163 fn offset(&self) -> Result<NonZeroUsize, DataBaseError> {
164 let offset = unsafe { self._impl.byte_offset_from(Self::base()?) as usize };
165 NonZeroUsize::new(offset).ok_or(DataBaseError::SpecifiedZeroOffset)
166 }
167}
168
169#[inline]
171pub fn relocate<T>(se_and_vr: T, ae: T) -> Result<T, ModuleStateError> {
172 let is_ae = ModuleState::map_or_init(|module| module.runtime.is_ae())?;
173 Ok(if is_ae { ae } else { se_and_vr })
174}
175
176impl TryFrom<Offset> for Relocation {
177 type Error = DataBaseError;
178
179 #[inline]
180 fn try_from(offset: Offset) -> Result<Self, Self::Error> {
181 Ok(Self { _impl: offset.address()?, _cast_target: PhantomData })
182 }
183}
184
185impl TryFrom<VariantOffset> for Relocation {
186 type Error = DataBaseError;
187
188 #[inline]
189 fn try_from(offset: VariantOffset) -> Result<Self, Self::Error> {
190 Ok(Self { _impl: offset.address()?, _cast_target: PhantomData })
191 }
192}
193
194impl TryFrom<ID> for Relocation {
195 type Error = DataBaseError;
196
197 #[inline]
198 fn try_from(id: ID) -> Result<Self, Self::Error> {
199 Ok(Self { _impl: id.address()?, _cast_target: PhantomData })
200 }
201}
202
203impl TryFrom<RelocationID> for Relocation {
204 type Error = DataBaseError;
205
206 #[inline]
207 fn try_from(id: RelocationID) -> Result<Self, Self::Error> {
208 Ok(Self { _impl: id.address()?, _cast_target: PhantomData })
209 }
210}
211
212#[inline]
215pub unsafe fn relocate_virtual<F>(
216 se_ae_vtable_offset: isize,
217 vr_vtable_offset: isize,
218 se_ae_vtable_index: isize,
219 vr_vtable_index: isize,
220 this: NonNull<u8>,
221) -> Result<F, ModuleStateError>
222where
223 F: Copy,
224{
225 let is_vr = ModuleState::map_active(|module| module.runtime.is_vr())?;
226
227 unsafe {
228 let vtable_ptr = *(this.as_ptr() as *const *const F).offset(if is_vr {
229 vr_vtable_offset
230 } else {
231 se_ae_vtable_offset
232 });
233 let func_ptr = *vtable_ptr.offset(if is_vr { vr_vtable_index } else { se_ae_vtable_index });
234
235 Ok(func_ptr)
236 }
237}
238
239#[inline]
249pub fn relocate_member<THIS, T>(
250 this: &THIS,
251 se_ae_offset: isize,
252 vr_offset: isize,
253) -> Result<&T, RelocationError> {
254 let member_ptr = {
255 let is_vr = ModuleState::map_active(|module| module.runtime.is_vr())?;
256 let this = this as *const THIS;
257 let offset = if is_vr { vr_offset } else { se_ae_offset };
258 this.wrapping_offset(offset).cast::<T>()
259 };
260
261 Ok(unsafe { raw_pointer_as_ref(member_ptr) }?)
262}
263
264pub fn relocate_member_mut<THIS, T>(
274 this: &mut THIS,
275 se_ae_offset: isize,
276 vr_offset: isize,
277) -> Result<&mut T, RelocationError> {
278 let member_ptr = {
279 let is_vr = ModuleState::map_active(|module| module.runtime.is_vr())?;
280 let this = this as *mut THIS;
281 let offset = if is_vr { vr_offset } else { se_ae_offset };
282 this.wrapping_offset(offset).cast::<T>()
283 };
284
285 Ok(unsafe { raw_pointer_as_mut(member_ptr) }?)
286}
287
288pub const unsafe fn relocate_member_if<T>(
295 condition: bool,
296 this: *mut u8,
297 a: isize,
298 b: isize,
299) -> *mut T {
300 unsafe { this.offset(if condition { a } else { b }).cast::<T>() }
301}
302
303#[inline]
316pub unsafe fn relocate_member_if_newer<THIS, T>(
317 version: Version,
318 this: &THIS,
319 older: isize,
320 newer: isize,
321) -> Result<&T, RelocationError> {
322 let is_old = ModuleState::map_active(|module| module.version < version)?;
323 let this = this as *const THIS;
324 let offset = if is_old { older } else { newer };
325 let member_ptr = unsafe { this.offset(offset).cast::<T>() };
326 Ok(unsafe { raw_pointer_as_ref(member_ptr) }?)
327}
328
329pub unsafe fn relocate_member_if_newer_mut<THIS, T>(
340 version: Version,
341 this: &mut THIS,
342 older: isize,
343 newer: isize,
344) -> Result<&mut T, RelocationError> {
345 let is_old = ModuleState::map_active(|module| module.version < version)?;
346 let this = this as *mut THIS;
347 let offset = if is_old { older } else { newer };
348 let member_ptr = unsafe { this.offset(offset).cast::<T>() };
349 Ok(unsafe { raw_pointer_as_mut(member_ptr) }?)
350}
351
352#[inline]
362pub unsafe fn raw_pointer_as_ref<'a, T>(ptr: *const T) -> Result<&'a T, RawPointerError> {
363 if ptr.is_null() {
364 return Err(RawPointerError::NullPointer);
365 }
366
367 if !ptr.is_aligned() {
368 return Err(RawPointerError::MisalignedPointer {
369 addr: ptr.addr(),
370 expected_align: core::mem::align_of::<T>(),
371 misaligned_type: core::any::type_name::<T>(),
372 });
373 };
374
375 Ok(unsafe { &*ptr })
376}
377
378#[inline]
388pub unsafe fn raw_pointer_as_mut<'a, T>(ptr: *mut T) -> Result<&'a mut T, RawPointerError> {
389 if ptr.is_null() {
390 return Err(RawPointerError::NullPointer);
391 }
392
393 if !ptr.is_aligned() {
394 return Err(RawPointerError::MisalignedPointer {
395 addr: ptr.addr(),
396 expected_align: core::mem::align_of::<T>(),
397 misaligned_type: core::any::type_name::<T>(),
398 });
399 }
400
401 Ok(unsafe { &mut *ptr })
402}
403
404#[derive(Debug, snafu::Snafu)]
406pub enum RelocationError {
407 #[snafu(transparent)]
409 PointerConversion { source: RawPointerError },
410
411 #[snafu(transparent)]
413 ModuleState { source: ModuleStateError },
414}
415
416#[derive(Debug, snafu::Snafu)]
418pub enum RawPointerError {
419 NullPointer,
421
422 #[snafu(display(
424 "This pointer of `{misaligned_type}` was expected to have an alignment of {expected_align}, but the actual address was {addr:X}."
425 ))]
426 MisalignedPointer { addr: usize, expected_align: usize, misaligned_type: &'static str },
427}