plugify  1.0.0.0
mem_hook.hpp
1 #pragma once
2 
3 #include <plugify/mem_protector.hpp>
4 
5 namespace plugify {
17  template<typename F> requires(std::is_pointer_v<F> && std::is_function_v<std::remove_pointer_t<F>>)
18  F HookMethod(void* vtp, F func, int offset) {
19  const uintptr_t vtable = *reinterpret_cast<uintptr_t*>(vtp);
20  const uintptr_t entry = vtable + (static_cast<size_t>(offset) * sizeof(void*));
21  const uintptr_t orig = *reinterpret_cast<uintptr_t*>(entry);
22  MemProtector memProtector(entry, sizeof(entry), ProtFlag::RWX);
23  *reinterpret_cast<uintptr_t*>(entry) = reinterpret_cast<uintptr_t>(func);
24  return reinterpret_cast<F>(orig);
25  }
26 
38  template<typename F>
39  int GetVirtualTableIndex(F func, ProtFlag flag = ProtFlag::RWX) {
40  void* ptr = (void*&)func;
41 
42  constexpr size_t size = 12;
43 
44  MemProtector protector(ptr, size, flag);
45 
46 #if defined(__GNUC__) || defined(__clang__)
47  struct GCC_MemFunPtr {
48  union {
49  void* adrr; // always even
50  intptr_t vti_plus1; // vindex+1, always odd
51  };
52  intptr_t delta;
53  };
54 
55  int vtindex;
56  auto mfp_detail = (GCC_MemFunPtr*)&ptr;
57  if (mfp_detail->vti_plus1 & 1) {
58  vtindex = (mfp_detail->vti_plus1 - 1) / sizeof(void*);
59  } else {
60  vtindex = -1;
61  }
62 
63  return vtindex;
64 #elif defined(_MSC_VER)
65  // https://www.unknowncheats.me/forum/c-and-c-/102577-vtable-index-pure-virtual-function.html
66 
67  // Check whether it's a virtual function call on x86
68 
69  // They look like this:a
70  // 0: 8b 01 mov eax,DWORD PTR [ecx]
71  // 2: ff 60 04 jmp DWORD PTR [eax+0x4]
72  // ==OR==
73  // 0: 8b 01 mov eax,DWORD PTR [ecx]
74  // 2: ff a0 18 03 00 00 jmp DWORD PTR [eax+0x318]]
75 
76  // However, for vararg functions, they look like this:
77  // 0: 8b 44 24 04 mov eax,DWORD PTR [esp+0x4]
78  // 4: 8b 00 mov eax,DWORD PTR [eax]
79  // 6: ff 60 08 jmp DWORD PTR [eax+0x8]
80  // ==OR==
81  // 0: 8b 44 24 04 mov eax,DWORD PTR [esp+0x4]
82  // 4: 8b 00 mov eax,DWORD PTR [eax]
83  // 6: ff a0 18 03 00 00 jmp DWORD PTR [eax+0x318]
84  // With varargs, the this pointer is passed as if it was the first argument
85 
86  // On x64
87  // 0: 48 8b 01 mov rax,QWORD PTR [rcx]
88  // 3: ff 60 04 jmp QWORD PTR [rax+0x4]
89  // ==OR==
90  // 0: 48 8b 01 mov rax,QWORD PTR [rcx]
91  // 3: ff a0 18 03 00 00 jmp QWORD PTR [rax+0x318]
92  auto finder = [&](uint8_t* addr) {
93  std::unique_ptr<MemProtector> protector;
94 
95  if (*addr == 0xE9) {
96  // May or may not be!
97  // Check where it'd jump
98  addr += 5 /*size of the instruction*/ + *(uint32_t*)(addr + 1);
99 
100  protector = std::make_unique<MemProtector>(addr, size, flag);
101  }
102 
103  bool ok = false;
104 #if INTPTR_MAX == INT64_MAX
105  if (addr[0] == 0x48 && addr[1] == 0x8B && addr[2] == 0x01) {
106  addr += 3;
107  ok = true;
108  } else
109 #endif
110  if (addr[0] == 0x8B && addr[1] == 0x01) {
111  addr += 2;
112  ok = true;
113  } else if (addr[0] == 0x8B && addr[1] == 0x44 && addr[2] == 0x24 && addr[3] == 0x04 && addr[4] == 0x8B && addr[5] == 0x00) {
114  addr += 6;
115  ok = true;
116  }
117 
118  if (!ok)
119  return -1;
120 
121  constexpr int PtrSize = static_cast<int>(sizeof(void*));
122 
123  if (*addr++ == 0xFF) {
124  if (*addr == 0x60)
125  return *++addr / PtrSize;
126  else if (*addr == 0xA0)
127  return int(*((uint32_t*)++addr)) / PtrSize;
128  else if (*addr == 0x20)
129  return 0;
130  else
131  return -1;
132  }
133 
134  return -1;
135  };
136 
137  return finder((uint8_t*)ptr);
138 #else
139 #error "Compiler not support"
140 #endif
141  }
142 } // namespace plugify