1mod move_inst;
2#[cfg(target_arch = "x86_64")]
3mod tests;
4
5use std::io::{Cursor, Seek, SeekFrom, Write};
6use std::slice;
7
8#[cfg(windows)]
9use core::ffi::c_void;
10#[cfg(unix)]
11use libc::{__errno_location, c_void, mprotect, sysconf};
12#[cfg(windows)]
13use windows_sys::Win32::Foundation::GetLastError;
14#[cfg(windows)]
15use windows_sys::Win32::System::Memory::VirtualProtect;
16
17use bitflags::bitflags;
18use iced_x86::{Decoder, DecoderOptions, Instruction};
19
20use crate::HookError;
21use move_inst::move_code_to_addr;
22
23const MAX_INST_LEN: usize = 15;
24
25const TRAMPOLINE_MAX_LEN: usize = 1024;
26
27pub type JmpBackRoutine = unsafe extern "win64" fn(regs: *mut Registers, user_data: usize);
35
36pub type RetnRoutine =
50    unsafe extern "win64" fn(regs: *mut Registers, ori_func_ptr: usize, user_data: usize) -> usize;
51
52pub type JmpToAddrRoutine =
61    unsafe extern "win64" fn(regs: *mut Registers, ori_func_ptr: usize, user_data: usize);
62
63pub type JmpToRetRoutine =
76    unsafe extern "win64" fn(regs: *mut Registers, ori_func_ptr: usize, user_data: usize) -> usize;
77
78pub enum HookType {
80    JmpBack(JmpBackRoutine),
82
83    Retn(RetnRoutine),
85
86    JmpToAddr(usize, JmpToAddrRoutine),
88
89    JmpToRet(JmpToRetRoutine),
91}
92
93pub enum JmpType {
95    Direct,
98
99    MovJmp,
102
103    TrampolineJmp(usize),
106}
107
108#[repr(C)]
110#[derive(Debug, Clone)]
111pub struct Registers {
112    pub xmm0: u128,
114    pub xmm1: u128,
116    pub xmm2: u128,
118    pub xmm3: u128,
120    pub r15: u64,
122    pub r14: u64,
124    pub r13: u64,
126    pub r12: u64,
128    pub r11: u64,
130    pub r10: u64,
132    pub r9: u64,
134    pub r8: u64,
136    pub rbp: u64,
138    pub rdi: u64,
140    pub rsi: u64,
142    pub rdx: u64,
144    pub rcx: u64,
146    pub rbx: u64,
148    pub rsp: u64,
150    pub rflags: u64,
152    pub _no_use: u64,
154    pub rax: u64,
156}
157
158impl Registers {
159    #[must_use]
169    pub unsafe fn get_stack(&self, cnt: usize) -> u64 {
170        unsafe { *((self.rsp as usize + cnt * 8) as *mut u64) }
171    }
172}
173
174pub trait ThreadCallback {
177    fn pre(&self) -> bool;
179    fn post(&self);
181}
182
183pub enum CallbackOption {
185    Some(Box<dyn ThreadCallback>),
187    None,
189}
190
191bitflags! {
192    pub struct HookFlags:u32 {
194        const NOT_MODIFY_MEMORY_PROTECT = 0x1;
197    }
198}
199
200pub struct Hooker {
203    addr: usize,
204    hook_type: HookType,
205    thread_cb: CallbackOption,
206    user_data: usize,
207    jmp_inst_size: usize,
208    flags: HookFlags,
209}
210
211pub struct HookPoint {
213    addr: usize,
214    #[allow(dead_code)] trampoline: Box<[u8; TRAMPOLINE_MAX_LEN]>,
216    trampoline_prot: u32,
217    origin: Vec<u8>,
218    thread_cb: CallbackOption,
219    jmp_inst_size: usize,
220    flags: HookFlags,
221}
222
223pub struct ClosureHookPoint<'a> {
227    _inner: HookPoint,
228    _callback: Box<dyn HookCallback + 'a>,
229}
230
231trait HookCallback {}
234impl<T> HookCallback for T {}
235
236#[cfg(not(target_arch = "x86_64"))]
237fn env_lock() {
238    panic!("This crate should only be used in arch x86_32!")
239}
240#[cfg(target_arch = "x86_64")]
241fn env_lock() {}
242
243impl Hooker {
244    #[must_use]
253    pub fn new(
254        addr: usize,
255        hook_type: HookType,
256        thread_cb: CallbackOption,
257        user_data: usize,
258        flags: HookFlags,
259    ) -> Self {
260        env_lock();
261        Self {
262            addr,
263            hook_type,
264            thread_cb,
265            user_data,
266            jmp_inst_size: 14,
267            flags,
268        }
269    }
270
271    pub unsafe fn hook(self) -> Result<HookPoint, HookError> {
283        let (moving_insts, origin) = get_moving_insts(self.addr, self.jmp_inst_size)?;
284        let trampoline =
285            generate_trampoline(&self, moving_insts, origin.len() as u8, self.user_data)?;
286        let trampoline_prot = modify_mem_protect(trampoline.as_ptr() as usize, trampoline.len())?;
287        if !self.flags.contains(HookFlags::NOT_MODIFY_MEMORY_PROTECT) {
288            let old_prot = modify_mem_protect(self.addr, self.jmp_inst_size)?;
289            let ret = modify_jmp_with_thread_cb(&self, trampoline.as_ptr() as usize);
290            recover_mem_protect(self.addr, self.jmp_inst_size, old_prot);
291            ret?;
292        } else {
293            modify_jmp_with_thread_cb(&self, trampoline.as_ptr() as usize)?;
294        }
295        Ok(HookPoint {
296            addr: self.addr,
297            trampoline,
298            trampoline_prot,
299            origin,
300            thread_cb: self.thread_cb,
301            jmp_inst_size: self.jmp_inst_size,
302            flags: self.flags,
303        })
304    }
305}
306
307
308pub unsafe fn hook_closure_jmp_back<'a, T: Fn(*mut Registers) + Send + Sync + 'a>(
320    address: usize,
321    callback: T,
322    callback_option: CallbackOption,
323    hook_flags: HookFlags,
324) -> Result<ClosureHookPoint<'a>, HookError> {
325    let callback = Box::new(callback);
326    let hooker = Hooker::new(
327        address,
328        HookType::JmpBack(run_jmp_back_closure::<T>),
329        callback_option,
330        &*callback as *const T as usize,
331        hook_flags,
332    );
333    Ok(ClosureHookPoint {
334        _inner: unsafe { hooker.hook()? },
335        _callback: callback,
336    })
337}
338
339pub unsafe fn hook_closure_retn<
352    'a,
353    T: (Fn(*mut Registers, usize) -> usize) + Send + Sync + 'a,
354>(
355    address: usize,
356    callback: T,
357    callback_option: CallbackOption,
358    hook_flags: HookFlags,
359) -> Result<ClosureHookPoint<'a>, HookError> {
360    let callback = Box::new(callback);
361    let hooker = Hooker::new(
362        address,
363        HookType::Retn(run_retn_closure::<T>),
364        callback_option,
365        &*callback as *const T as usize,
366        hook_flags,
367    );
368    Ok(ClosureHookPoint {
369        _inner: unsafe { hooker.hook()? },
370        _callback: callback,
371    })
372}
373
374pub unsafe fn hook_closure_jmp_to_addr<'a, T: Fn(*mut Registers, usize) + Send + Sync + 'a>(
387    address: usize,
388    follow_up: usize,
389    callback: T,
390    callback_option: CallbackOption,
391    hook_flags: HookFlags,
392) -> Result<ClosureHookPoint<'a>, HookError> {
393    let callback = Box::new(callback);
394    let hooker = Hooker::new(
395        address,
396        HookType::JmpToAddr(follow_up, run_jmp_to_addr_closure::<T>),
397        callback_option,
398        &*callback as *const T as usize,
399        hook_flags,
400    );
401    Ok(ClosureHookPoint {
402        _inner: unsafe { hooker.hook()? },
403        _callback: callback,
404    })
405}
406
407pub unsafe fn hook_closure_jmp_to_ret<
421    'a,
422    T: (Fn(*mut Registers, usize) -> usize) + Send + Sync + 'a,
423>(
424    address: usize,
425    callback: T,
426    callback_option: CallbackOption,
427    hook_flags: HookFlags,
428) -> Result<ClosureHookPoint<'a>, HookError> {
429    let callback = Box::new(callback);
430    let hooker = Hooker::new(
431        address,
432        HookType::JmpToRet(run_retn_closure::<T>),
433        callback_option,
434        &*callback as *const T as usize,
435        hook_flags,
436    );
437    Ok(ClosureHookPoint {
438        _inner: unsafe { hooker.hook()? },
439        _callback: callback,
440    })
441}
442
443unsafe extern "win64" fn run_jmp_back_closure<T: Fn(*mut Registers)>(
445    reg: *mut Registers,
446    callback: usize,
447) {
448    unsafe { (*(callback as *const T))(reg) };
449}
450
451unsafe extern "win64" fn run_retn_closure<T: Fn(*mut Registers, usize) -> usize>(
454    reg: *mut Registers,
455    original: usize,
456    callback: usize,
457) -> usize {
458    unsafe { (*(callback as *const T))(reg, original) }
459}
460
461unsafe extern "win64" fn run_jmp_to_addr_closure<T: Fn(*mut Registers, usize)>(
463    reg: *mut Registers,
464    original: usize,
465    callback: usize,
466) {
467    unsafe { (*(callback as *const T))(reg, original) };
468}
469
470impl HookPoint {
471    pub unsafe fn unhook(self) -> Result<(), HookError> {
473        self.unhook_by_ref()
474    }
475
476    fn unhook_by_ref(&self) -> Result<(), HookError> {
477        let ret: Result<(), HookError>;
478        if !self.flags.contains(HookFlags::NOT_MODIFY_MEMORY_PROTECT) {
479            let old_prot = modify_mem_protect(self.addr, self.jmp_inst_size)?;
480            ret = recover_jmp_with_thread_cb(self);
481            recover_mem_protect(self.addr, self.jmp_inst_size, old_prot);
482        } else {
483            ret = recover_jmp_with_thread_cb(self)
484        }
485        recover_mem_protect(
486            self.trampoline.as_ptr() as usize,
487            self.trampoline.len(),
488            self.trampoline_prot,
489        );
490        ret
491    }
492}
493
494impl Drop for HookPoint {
496    fn drop(&mut self) {
497        self.unhook_by_ref().unwrap_or_default();
498    }
499}
500
501fn get_moving_insts(
502    addr: usize,
503    min_bytes: usize,
504) -> Result<(Vec<Instruction>, Vec<u8>), HookError> {
505    let code_slice = unsafe { slice::from_raw_parts(addr as *const u8, MAX_INST_LEN * 2) };
506    let mut decoder = Decoder::new(64, code_slice, DecoderOptions::NONE);
507    decoder.set_ip(addr as u64);
508
509    let mut total_bytes = 0;
510    let mut ori_insts: Vec<Instruction> = vec![];
511    for inst in &mut decoder {
512        if inst.is_invalid() {
513            return Err(HookError::Disassemble);
514        }
515        ori_insts.push(inst);
516        total_bytes += inst.len();
517        if total_bytes >= min_bytes {
518            break;
519        }
520    }
521
522    Ok((ori_insts, code_slice[0..decoder.position()].into()))
523}
524
525fn write_trampoline_prolog(buf: &mut impl Write) -> Result<usize, std::io::Error> {
526    buf.write(&[
572        0x54, 0x9C, 0x48, 0xF7, 0xC4, 0x08, 0x00, 0x00, 0x00, 0x74, 0x2C, 0x50, 0x48, 0x83, 0xEC,
573        0x10, 0x48, 0x8B, 0x44, 0x24, 0x20, 0x48, 0x89, 0x04, 0x24, 0x48, 0x8B, 0x44, 0x24, 0x18,
574        0x48, 0x89, 0x44, 0x24, 0x08, 0x48, 0x8B, 0x44, 0x24, 0x10, 0x48, 0x89, 0x44, 0x24, 0x18,
575        0xC7, 0x44, 0x24, 0x10, 0x01, 0x00, 0x00, 0x00, 0xEB, 0x27, 0x50, 0x50, 0x48, 0x8B, 0x44,
576        0x24, 0x18, 0x48, 0x89, 0x04, 0x24, 0x48, 0x8B, 0x44, 0x24, 0x08, 0x48, 0x89, 0x44, 0x24,
577        0x18, 0x48, 0x8B, 0x44, 0x24, 0x10, 0x48, 0x89, 0x44, 0x24, 0x08, 0xC7, 0x44, 0x24, 0x10,
578        0x00, 0x00, 0x00, 0x00, 0x53, 0x51, 0x52, 0x56, 0x57, 0x55, 0x41, 0x50, 0x41, 0x51, 0x41,
579        0x52, 0x41, 0x53, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, 0x48, 0x83, 0xEC, 0x40,
580        0x0F, 0x29, 0x04, 0x24, 0x0F, 0x29, 0x4C, 0x24, 0x10, 0x0F, 0x29, 0x54, 0x24, 0x20, 0x0F,
581        0x29, 0x5C, 0x24, 0x30,
582    ])
583}
584
585fn write_trampoline_epilog1(buf: &mut impl Write) -> Result<usize, std::io::Error> {
586    buf.write(&[
607        0x0F, 0x28, 0x04, 0x24, 0x0F, 0x28, 0x4C, 0x24, 0x10, 0x0F, 0x28, 0x54, 0x24, 0x20, 0x0F,
608        0x28, 0x5C, 0x24, 0x30, 0x48, 0x83, 0xC4, 0x40, 0x41, 0x5F, 0x41, 0x5E, 0x41, 0x5D, 0x41,
609        0x5C, 0x41, 0x5B, 0x41, 0x5A, 0x41, 0x59, 0x41, 0x58, 0x5D, 0x5F, 0x5E, 0x5A, 0x59, 0x5B,
610        0x48, 0x83, 0xC4, 0x08,
611    ])
612}
613
614fn write_trampoline_epilog2_common(buf: &mut impl Write) -> Result<usize, std::io::Error> {
615    buf.write(&[
630        0xF7, 0x44, 0x24, 0x08, 0x01, 0x00, 0x00, 0x00, 0x74, 0x10, 0x48, 0x8B, 0x44, 0x24, 0x10,
631        0x48, 0x89, 0x44, 0x24, 0x18, 0x9D, 0x58, 0x58, 0x58, 0xEB, 0x03, 0x9D, 0x58, 0x58,
632    ])
633}
634
635fn write_trampoline_epilog2_jmp_ret(buf: &mut impl Write) -> Result<usize, std::io::Error> {
636    buf.write(&[
652        0xF7, 0x44, 0x24, 0x08, 0x01, 0x00, 0x00, 0x00, 0x74, 0x0E, 0x9D, 0x48, 0x89, 0x04, 0x24,
653        0x58, 0x58, 0x48, 0x8D, 0x64, 0x24, 0x08, 0xEB, 0x08, 0x9D, 0x48, 0x89, 0x44, 0x24, 0xF8,
654        0x58, 0x58, 0xFF, 0x64, 0x24, 0xE8
655    ])
656}
657
658fn jmp_addr<T: Write>(addr: u64, buf: &mut T) -> Result<(), HookError> {
659    buf.write(&[0xff, 0x25, 0, 0, 0, 0])?;
660    buf.write(&addr.to_le_bytes())?;
661    Ok(())
662}
663
664fn write_ori_func_addr<T: Write + Seek>(buf: &mut T, ori_func_addr_off: u64, ori_func_off: u64) {
665    let pos = buf.stream_position().unwrap();
666    buf.seek(SeekFrom::Start(ori_func_addr_off)).unwrap();
667    buf.write(&ori_func_off.to_le_bytes()).unwrap();
668    buf.seek(SeekFrom::Start(pos)).unwrap();
669}
670
671fn generate_jmp_back_trampoline<T: Write + Seek>(
672    buf: &mut T,
673    trampoline_base_addr: u64,
674    moving_code: &Vec<Instruction>,
675    ori_addr: usize,
676    cb: JmpBackRoutine,
677    ori_len: u8,
678    user_data: usize,
679) -> Result<(), HookError> {
680    buf.write(&[0x48, 0xba])?;
682    buf.write(&(user_data as u64).to_le_bytes())?;
683    buf.write(&[0x48, 0x89, 0xe1, 0x48, 0x83, 0xec, 0x10, 0x48, 0xb8])?;
687    buf.write(&(cb as usize as u64).to_le_bytes())?;
688    buf.write(&[0xff, 0xd0, 0x48, 0x83, 0xc4, 0x10])?;
691    write_trampoline_epilog1(buf)?;
692    write_trampoline_epilog2_common(buf)?;
693
694    let cur_pos = buf.stream_position().unwrap();
695    buf.write(&move_code_to_addr(
696        moving_code,
697        trampoline_base_addr + cur_pos,
698    )?)?;
699
700    jmp_addr(ori_addr as u64 + u64::from(ori_len), buf)?;
701    Ok(())
702}
703
704fn generate_retn_trampoline<T: Write + Seek>(
705    buf: &mut T,
706    trampoline_base_addr: u64,
707    moving_code: &Vec<Instruction>,
708    ori_addr: usize,
709    cb: RetnRoutine,
710    ori_len: u8,
711    user_data: usize,
712) -> Result<(), HookError> {
713    buf.write(&[0x49, 0xb8])?;
715    buf.write(&(user_data as u64).to_le_bytes())?;
716    let ori_func_addr_off = buf.stream_position().unwrap() + 2;
717    buf.write(&[
722        0x48, 0xba, 0, 0, 0, 0, 0, 0, 0, 0, 0x48, 0x89, 0xe1, 0x48, 0x83, 0xec, 0x20, 0x48, 0xb8,
723    ])?;
724    buf.write(&(cb as usize as u64).to_le_bytes())?;
725    buf.write(&[
729        0xff, 0xd0, 0x48, 0x83, 0xc4, 0x20, 0x48, 0x89, 0x84, 0x24, 0xc8, 0x00, 0x00, 0x00,
730    ])?;
731    write_trampoline_epilog1(buf)?;
732    write_trampoline_epilog2_common(buf)?;
733    buf.write(&[0xc3])?;
735
736    let ori_func_off = buf.stream_position().unwrap();
737    buf.write(&move_code_to_addr(
738        moving_code,
739        trampoline_base_addr + ori_func_off,
740    )?)?;
741    jmp_addr(ori_addr as u64 + u64::from(ori_len), buf)?;
742
743    write_ori_func_addr(buf, ori_func_addr_off, trampoline_base_addr + ori_func_off);
744
745    Ok(())
746}
747
748fn generate_jmp_addr_trampoline<T: Write + Seek>(
749    buf: &mut T,
750    trampoline_base_addr: u64,
751    moving_code: &Vec<Instruction>,
752    ori_addr: usize,
753    dest_addr: usize,
754    cb: JmpToAddrRoutine,
755    ori_len: u8,
756    user_data: usize,
757) -> Result<(), HookError> {
758    buf.write(&[0x49, 0xb8])?;
760    buf.write(&(user_data as u64).to_le_bytes())?;
761    let ori_func_addr_off = buf.stream_position().unwrap() + 2;
762    buf.write(&[
767        0x48, 0xba, 0, 0, 0, 0, 0, 0, 0, 0, 0x48, 0x89, 0xe1, 0x48, 0x83, 0xec, 0x20, 0x48, 0xb8,
768    ])?;
769    buf.write(&(cb as usize as u64).to_le_bytes())?;
770    buf.write(&[0xff, 0xd0, 0x48, 0x83, 0xc4, 0x20])?;
773    write_trampoline_epilog1(buf)?;
774    write_trampoline_epilog2_common(buf)?;
775    jmp_addr(dest_addr as u64, buf)?;
776
777    let ori_func_off = buf.stream_position().unwrap();
778    buf.write(&move_code_to_addr(
779        moving_code,
780        trampoline_base_addr + ori_func_off,
781    )?)?;
782    jmp_addr(ori_addr as u64 + u64::from(ori_len), buf)?;
783
784    write_ori_func_addr(buf, ori_func_addr_off, trampoline_base_addr + ori_func_off);
785
786    Ok(())
787}
788
789fn generate_jmp_ret_trampoline<T: Write + Seek>(
790    buf: &mut T,
791    trampoline_base_addr: u64,
792    moving_code: &Vec<Instruction>,
793    ori_addr: usize,
794    cb: JmpToRetRoutine,
795    ori_len: u8,
796    user_data: usize,
797) -> Result<(), HookError> {
798    buf.write(&[0x49, 0xb8])?;
800    buf.write(&(user_data as u64).to_le_bytes())?;
801    let ori_func_addr_off = buf.stream_position().unwrap() + 2;
802    buf.write(&[
807        0x48, 0xba, 0, 0, 0, 0, 0, 0, 0, 0, 0x48, 0x89, 0xe1, 0x48, 0x83, 0xec, 0x20, 0x48, 0xb8,
808    ])?;
809    buf.write(&(cb as usize as u64).to_le_bytes())?;
810    buf.write(&[0xff, 0xd0, 0x48, 0x83, 0xc4, 0x20])?;
813    write_trampoline_epilog1(buf)?;
814    write_trampoline_epilog2_jmp_ret(buf)?;
815
816    let ori_func_off = buf.stream_position().unwrap();
817    buf.write(&move_code_to_addr(
818        moving_code,
819        trampoline_base_addr + ori_func_off,
820    )?)?;
821    jmp_addr(ori_addr as u64 + u64::from(ori_len), buf)?;
822
823    write_ori_func_addr(buf, ori_func_addr_off, trampoline_base_addr + ori_func_off);
824
825    Ok(())
826}
827
828fn generate_trampoline(
829    hooker: &Hooker,
830    moving_code: Vec<Instruction>,
831    ori_len: u8,
832    user_data: usize,
833) -> Result<Box<[u8; TRAMPOLINE_MAX_LEN]>, HookError> {
834    let mut trampoline_buffer = Box::new([0u8; TRAMPOLINE_MAX_LEN]);
835    let trampoline_addr = trampoline_buffer.as_ptr() as u64;
836    let mut buf = Cursor::new(&mut trampoline_buffer[..]);
837
838    write_trampoline_prolog(&mut buf)?;
839
840    match hooker.hook_type {
841        HookType::JmpBack(cb) => generate_jmp_back_trampoline(
842            &mut buf,
843            trampoline_addr,
844            &moving_code,
845            hooker.addr,
846            cb,
847            ori_len,
848            user_data,
849        ),
850        HookType::Retn(cb) => generate_retn_trampoline(
851            &mut buf,
852            trampoline_addr,
853            &moving_code,
854            hooker.addr,
855            cb,
856            ori_len,
857            user_data,
858        ),
859        HookType::JmpToAddr(dest_addr, cb) => generate_jmp_addr_trampoline(
860            &mut buf,
861            trampoline_addr,
862            &moving_code,
863            hooker.addr,
864            dest_addr,
865            cb,
866            ori_len,
867            user_data,
868        ),
869        HookType::JmpToRet(cb) => generate_jmp_ret_trampoline(
870            &mut buf,
871            trampoline_addr,
872            &moving_code,
873            hooker.addr,
874            cb,
875            ori_len,
876            user_data,
877        ),
878    }?;
879
880    Ok(trampoline_buffer)
881}
882
883#[cfg(windows)]
884fn modify_mem_protect(addr: usize, len: usize) -> Result<u32, HookError> {
885    let mut old_prot: u32 = 0;
886    let old_prot_ptr = std::ptr::addr_of_mut!(old_prot);
887    let ret = unsafe { VirtualProtect(addr as *const c_void, len, 0x40, old_prot_ptr) };
889    if ret == 0 {
890        Err(HookError::MemoryProtect(unsafe { GetLastError() }))
891    } else {
892        Ok(old_prot)
893    }
894}
895
896#[cfg(unix)]
897fn modify_mem_protect(addr: usize, len: usize) -> Result<u32, HookError> {
898    let page_size = unsafe { sysconf(30) }; if len > page_size.try_into().unwrap() {
900        Err(HookError::InvalidParameter)
901    } else {
902        let ret = unsafe {
904            mprotect(
905                (addr & !(page_size as usize - 1)) as *mut c_void,
906                page_size as usize,
907                7,
908            )
909        };
910        if ret != 0 {
911            let err = unsafe { *(__errno_location()) };
912            Err(HookError::MemoryProtect(err as u32))
913        } else {
914            Ok(7)
916        }
917    }
918}
919#[cfg(windows)]
920fn recover_mem_protect(addr: usize, len: usize, old: u32) {
921    let mut old_prot: u32 = 0;
922    let old_prot_ptr = std::ptr::addr_of_mut!(old_prot);
923    unsafe { VirtualProtect(addr as *const c_void, len, old, old_prot_ptr) };
924}
925
926#[cfg(unix)]
927fn recover_mem_protect(addr: usize, _: usize, old: u32) {
928    let page_size = unsafe { sysconf(30) }; unsafe {
930        mprotect(
931            (addr & !(page_size as usize - 1)) as *mut c_void,
932            page_size as usize,
933            old as i32,
934        )
935    };
936}
937fn modify_jmp(dest_addr: usize, trampoline_addr: usize) {
938    let buf = unsafe { slice::from_raw_parts_mut(dest_addr as *mut u8, 14) };
939    let distance = trampoline_addr as i64 - (dest_addr as i64 + 5);
940    if distance.abs() <= 0x7fff_ffff {
941        buf[0] = 0xe9;
943        buf[1..5].copy_from_slice(&(distance as i32).to_le_bytes());
944    } else {
945        buf[0..6].copy_from_slice(&[0xff, 0x25, 0, 0, 0, 0]);
947        buf[6..14].copy_from_slice(&(trampoline_addr as u64).to_le_bytes());
948    }
949}
950
951fn modify_jmp_with_thread_cb(hook: &Hooker, trampoline_addr: usize) -> Result<(), HookError> {
952    if let CallbackOption::Some(cbs) = &hook.thread_cb {
953        if !cbs.pre() {
954            return Err(HookError::PreHook);
955        }
956        modify_jmp(hook.addr, trampoline_addr);
957        cbs.post();
958        Ok(())
959    } else {
960        modify_jmp(hook.addr, trampoline_addr);
961        Ok(())
962    }
963}
964
965fn recover_jmp(dest_addr: usize, origin: &[u8]) {
966    let buf = unsafe { slice::from_raw_parts_mut(dest_addr as *mut u8, origin.len()) };
967    buf.copy_from_slice(origin);
969}
970
971fn recover_jmp_with_thread_cb(hook: &HookPoint) -> Result<(), HookError> {
972    if let CallbackOption::Some(cbs) = &hook.thread_cb {
973        if !cbs.pre() {
974            return Err(HookError::PreHook);
975        }
976        recover_jmp(hook.addr, &hook.origin);
977        cbs.post();
978    } else {
979        recover_jmp(hook.addr, &hook.origin);
980    }
981    Ok(())
982}