funchook.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. #include <windows.h>
  2. #include <stdlib.h>
  3. #include "funchook.h"
  4. #define JMP_64_SIZE 14
  5. #define JMP_32_SIZE 5
  6. #define X86_NOP 0x90
  7. #define X86_JMP_NEG_5 0xF9EB
  8. static inline void fix_permissions(void *addr, size_t size)
  9. {
  10. DWORD protect_val;
  11. VirtualProtect(addr, size, PAGE_EXECUTE_READWRITE, &protect_val);
  12. }
  13. void hook_init(struct func_hook *hook, void *func_addr, void *hook_addr,
  14. const char *name)
  15. {
  16. memset(hook, 0, sizeof(*hook));
  17. hook->func_addr = (uintptr_t)func_addr;
  18. hook->hook_addr = (uintptr_t)hook_addr;
  19. hook->name = name;
  20. fix_permissions((void *)(hook->func_addr - JMP_32_SIZE),
  21. JMP_64_SIZE + JMP_32_SIZE);
  22. memcpy(hook->unhook_data, func_addr, JMP_64_SIZE);
  23. }
  24. static inline size_t patch_size(struct func_hook *hook)
  25. {
  26. return hook->is_64bit_jump ? JMP_64_SIZE : JMP_32_SIZE;
  27. }
  28. static const uint8_t longjmp64[6] = {0xFF, 0x25, 0x00, 0x00, 0x00, 0x00};
  29. static inline void rehook64(struct func_hook *hook)
  30. {
  31. uint8_t data[JMP_64_SIZE];
  32. uintptr_t *ptr_loc = (uintptr_t *)((uint8_t *)data + sizeof(longjmp64));
  33. fix_permissions((void *)hook->func_addr, JMP_64_SIZE);
  34. memcpy(data, (void *)hook->func_addr, JMP_64_SIZE);
  35. memcpy(data, longjmp64, sizeof(longjmp64));
  36. *ptr_loc = hook->hook_addr;
  37. hook->call_addr = (void *)hook->func_addr;
  38. hook->type = HOOKTYPE_FORWARD_OVERWRITE;
  39. hook->hooked = true;
  40. memcpy((void *)hook->func_addr, data, JMP_64_SIZE);
  41. }
  42. static inline void hook_reverse_new(struct func_hook *hook, uint8_t *p)
  43. {
  44. hook->call_addr = (void *)(hook->func_addr + 2);
  45. hook->type = HOOKTYPE_REVERSE_CHAIN;
  46. hook->hooked = true;
  47. p[0] = 0xE9;
  48. *((uint32_t *)&p[1]) = (uint32_t)(hook->hook_addr - hook->func_addr);
  49. *((uint16_t *)&p[5]) = X86_JMP_NEG_5;
  50. }
  51. static inline void hook_reverse_chain(struct func_hook *hook, uint8_t *p)
  52. {
  53. if (hook->type != HOOKTYPE_FORWARD_OVERWRITE)
  54. return;
  55. hook->call_addr = (void *)(hook->func_addr + *((int32_t *)&p[1]));
  56. hook->type = HOOKTYPE_REVERSE_CHAIN;
  57. hook->hooked = true;
  58. *((uint32_t *)&p[1]) = (uint32_t)(hook->hook_addr - hook->func_addr);
  59. }
  60. static inline void hook_forward_chain(struct func_hook *hook, uint8_t *p,
  61. intptr_t offset)
  62. {
  63. int32_t cur_offset = *(int32_t *)&p[6];
  64. if (hook->type != HOOKTYPE_FORWARD_OVERWRITE)
  65. return;
  66. hook->call_addr = (void *)(hook->func_addr + JMP_32_SIZE + cur_offset);
  67. hook->type = HOOKTYPE_FORWARD_CHAIN;
  68. hook->hooked = true;
  69. *((int32_t *)&p[6]) = (int32_t)offset;
  70. }
  71. static inline void hook_forward_overwrite(struct func_hook *hook,
  72. intptr_t offset)
  73. {
  74. uint8_t *ptr = (uint8_t *)hook->func_addr;
  75. hook->call_addr = (void *)hook->func_addr;
  76. hook->type = HOOKTYPE_FORWARD_OVERWRITE;
  77. hook->hooked = true;
  78. *(ptr++) = 0xE9;
  79. *((int32_t *)ptr) = (int32_t)offset;
  80. }
  81. static inline void rehook32(struct func_hook *hook, bool force, intptr_t offset)
  82. {
  83. fix_permissions((void *)(hook->func_addr - JMP_32_SIZE),
  84. JMP_32_SIZE * 2);
  85. if (force || !hook->started) {
  86. uint8_t *p = (uint8_t *)hook->func_addr - JMP_32_SIZE;
  87. size_t nop_count = 0;
  88. /* check for reverse chain hook availability */
  89. for (size_t i = 0; i < JMP_32_SIZE; i++) {
  90. if (p[i] == X86_NOP)
  91. nop_count++;
  92. }
  93. if (nop_count == JMP_32_SIZE && p[5] == 0x8B && p[6] == 0xFF) {
  94. hook_reverse_new(hook, p);
  95. } else if (p[0] == 0xE9 &&
  96. *(uint16_t *)&p[5] == X86_JMP_NEG_5) {
  97. hook_reverse_chain(hook, p);
  98. } else if (p[5] == 0xE9) {
  99. hook_forward_chain(hook, p, offset);
  100. } else if (hook->type != HOOKTYPE_FORWARD_OVERWRITE) {
  101. hook->type = HOOKTYPE_FORWARD_OVERWRITE;
  102. }
  103. hook->started = true;
  104. }
  105. if (hook->type == HOOKTYPE_FORWARD_OVERWRITE) {
  106. hook_forward_overwrite(hook, offset);
  107. }
  108. }
  109. /*
  110. * Creates memory close to the target function, used to force the actual hook
  111. * to use a 32bit jump instead of a 64bit jump, thus preventing the chance of
  112. * overwriting adjacent functions, which can cause a crash. (by R1CH)
  113. */
  114. static void setup_64bit_bounce(struct func_hook *hook, intptr_t *offset)
  115. {
  116. MEMORY_BASIC_INFORMATION mbi;
  117. uintptr_t address;
  118. uintptr_t newdiff;
  119. SYSTEM_INFO si;
  120. bool success;
  121. int pagesize;
  122. int i;
  123. success = VirtualQueryEx(GetCurrentProcess(),
  124. (const void *)hook->func_addr, &mbi,
  125. sizeof(mbi));
  126. if (!success)
  127. return;
  128. GetSystemInfo(&si);
  129. pagesize = (int)si.dwAllocationGranularity;
  130. address = (uintptr_t)mbi.AllocationBase - pagesize;
  131. for (i = 0; i < 256; i++, address -= pagesize) {
  132. hook->bounce_addr = VirtualAlloc((LPVOID)address, pagesize,
  133. MEM_RESERVE | MEM_COMMIT,
  134. PAGE_EXECUTE_READWRITE);
  135. if (hook->bounce_addr)
  136. break;
  137. }
  138. if (!hook->bounce_addr) {
  139. address = (uintptr_t)mbi.AllocationBase + mbi.RegionSize +
  140. pagesize;
  141. for (i = 0; i < 256; i++, address += pagesize) {
  142. hook->bounce_addr =
  143. VirtualAlloc((LPVOID)address, pagesize,
  144. MEM_RESERVE | MEM_COMMIT,
  145. PAGE_EXECUTE_READWRITE);
  146. if (hook->bounce_addr)
  147. break;
  148. }
  149. }
  150. if (!hook->bounce_addr)
  151. return;
  152. if ((hook->func_addr + 5) > (uintptr_t)hook->bounce_addr)
  153. newdiff = hook->func_addr + 5 - (uintptr_t)hook->bounce_addr;
  154. else
  155. newdiff = (uintptr_t)hook->bounce_addr - hook->func_addr + 5;
  156. if (newdiff <= 0x7ffffff0) {
  157. uint8_t *addr = (uint8_t *)hook->bounce_addr;
  158. FillMemory(hook->bounce_addr, pagesize, 0xCC);
  159. *(addr++) = 0xFF;
  160. *(addr++) = 0x25;
  161. *((uint32_t *)addr) = 0;
  162. *((uint64_t *)(addr + 4)) = hook->hook_addr;
  163. hook->hook_addr = (uint64_t)hook->bounce_addr;
  164. *offset = hook->hook_addr - hook->func_addr - JMP_32_SIZE;
  165. hook->is_64bit_jump = false;
  166. }
  167. }
  168. void do_hook(struct func_hook *hook, bool force)
  169. {
  170. intptr_t offset;
  171. if (!force && hook->hooked)
  172. return;
  173. /* copy back the memory that was previously encountered to preserve
  174. * the current hook and any newer hooks on top */
  175. if (hook->started && !force) {
  176. uintptr_t addr;
  177. size_t size;
  178. if (hook->type == HOOKTYPE_REVERSE_CHAIN) {
  179. addr = hook->func_addr - JMP_32_SIZE;
  180. size = JMP_32_SIZE;
  181. } else {
  182. addr = hook->func_addr;
  183. size = patch_size(hook);
  184. }
  185. memcpy((void *)addr, hook->rehook_data, size);
  186. hook->hooked = true;
  187. return;
  188. }
  189. offset = hook->hook_addr - hook->func_addr - JMP_32_SIZE;
  190. #ifdef _WIN64
  191. hook->is_64bit_jump = (llabs(offset) >= 0x7fffffff);
  192. if (hook->is_64bit_jump) {
  193. if (!hook->attempted_bounce) {
  194. hook->attempted_bounce = true;
  195. setup_64bit_bounce(hook, &offset);
  196. }
  197. if (hook->is_64bit_jump) {
  198. rehook64(hook);
  199. return;
  200. }
  201. }
  202. #endif
  203. rehook32(hook, force, offset);
  204. }
  205. void unhook(struct func_hook *hook)
  206. {
  207. uintptr_t addr;
  208. size_t size;
  209. if (!hook->hooked)
  210. return;
  211. if (hook->type == HOOKTYPE_REVERSE_CHAIN) {
  212. size = JMP_32_SIZE;
  213. addr = (hook->func_addr - JMP_32_SIZE);
  214. } else {
  215. size = patch_size(hook);
  216. addr = hook->func_addr;
  217. }
  218. fix_permissions((void *)addr, size);
  219. memcpy(hook->rehook_data, (void *)addr, size);
  220. if (hook->type == HOOKTYPE_FORWARD_OVERWRITE)
  221. memcpy((void *)hook->func_addr, hook->unhook_data, size);
  222. hook->hooked = false;
  223. }