Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

367 lignes
9.7KB

  1. // todo: on weird jump: stop the disassembling there or treat as normal instruction?
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <assert.h>
  5. #include <stdbool.h>
  6. #include <stddef.h>
  7. #ifdef WINDOWS
  8. #include <windows.h>
  9. #else
  10. #include <sys/mman.h>
  11. #include <string.h>
  12. #endif
  13. #include "udis86.h"
  14. #include "list.h"
  15. #include "hook.h"
  16. #include "misc.h"
  17. #define MINIMUM_REQUIRED_FUNCTION_LENGTH_SHORT_HOOK 5
  18. #define MINIMUM_REQUIRED_FUNCTION_LENGTH_LONG_HOOK 16
  19. #define MAXIMUM_DISTANCE_FOR_SHORT_HOOK 0xFFFFFFFF
  20. #define PAGE_BOUNDARY 0x1000
  21. /**
  22. \brief Estimates the function size by checking opcodes until
  23. * an instruction that signifies the end of the function or
  24. * MINIMUM_REQUIRED_FUNCTION_LENGTH_LONG_HOOK bytes were found
  25. \return 0 on error
  26. */
  27. static size_t estimate_function_length(void* function);
  28. /**
  29. \brief Either builds the trampoline
  30. \param functionSize How much of the function shall be copied
  31. \param trampolineSize out parameter
  32. */
  33. static void* build_trampoline(void* function, size_t functionSize, size_t* trampolineSize);
  34. /**
  35. \brief jmp +X == +X + sizeof(jump type) + eip
  36. */
  37. static size_t get_jump_offset(ud_t* ud, size_t offsetOfInstr);
  38. /**
  39. \todo add loop & jecx (and possibly more) - though both are probably only used to jump back
  40. and currently is_jump is only called to from functions that don't care about that
  41. */
  42. static bool is_jump(enum ud_mneomic_code op);
  43. /**
  44. \brief Writes
  45. jmp [$+0] // $+0 == addr
  46. addr: xxx
  47. */
  48. static size_t write_x64_jump(unsigned char* where, void* toWhere);
  49. static bool is_end_of_function(enum ud_mneomic_code opc, ud_t* ud, size_t furthestJump, size_t instrOffset);
  50. static bool loops_into_overwritten_code(void* function);
  51. static size_t write_jcc_jump(const uint8_t* instruction, void* whereToWrite, void* originalAddr);
  52. static void fix_jcc_jumps(struct ListHead* instructions, struct ListHead* jccs);
  53. static int32_t get_rip_delta(ud_t* ud);
  54. static size_t write_instr_with_rip_delta(ud_t* ud, uint64_t absoluteAddr, const uint8_t* opcBuf, unsigned char* where);
  55. int hook(void* function, size_t functionLength, void* replacement, void** trampoline)
  56. {
  57. ptrdiff_t d = 0; // function minus replacement
  58. size_t needed = 0,
  59. trampolineSizeNeeded = 0;
  60. assert(function);
  61. assert(replacement);
  62. assert(trampoline);
  63. if(!functionLength)
  64. functionLength = estimate_function_length(function);
  65. /* is it actually possible to hook, space wise? */
  66. d = (ptrdiff_t)function - (ptrdiff_t)replacement;
  67. needed = d > MAXIMUM_DISTANCE_FOR_SHORT_HOOK ?
  68. MINIMUM_REQUIRED_FUNCTION_LENGTH_LONG_HOOK : MINIMUM_REQUIRED_FUNCTION_LENGTH_SHORT_HOOK;
  69. if(functionLength < needed)
  70. return NOT_ENOUGH_SPACE;
  71. if(loops_into_overwritten_code(function))
  72. return LOOPS_INTO_OVERWRITTEN_CODE;
  73. //! fixme: Remove in real code
  74. needed = MINIMUM_REQUIRED_FUNCTION_LENGTH_LONG_HOOK;
  75. printf("---\nNow build the trampoline\n");
  76. if((*trampoline = build_trampoline(function, needed, &trampolineSizeNeeded)) < 0)
  77. return (int)*trampoline;
  78. printf("Needed for trampoline %p: %d (Found %d)\n", *trampoline, trampolineSizeNeeded, functionLength);
  79. #ifdef WINDOWS
  80. FlushInstructionCache(GetCurrentProcess(), function, needed);
  81. #endif
  82. return SUCCESS;
  83. }
  84. static void* build_trampoline(void* function, size_t functionSize, size_t* trampolineSize)
  85. {
  86. ud_t ud;
  87. void* trampoline;
  88. assert(trampolineSize);
  89. ud_init(&ud);
  90. ud_set_input_buffer(&ud, function, (size_t)function | (PAGE_BOUNDARY - 1)); // Can't read further than page boundary - there be dragons
  91. ud_set_pc(&ud, (uint64_t)function);
  92. ud_set_mode(&ud, 64);
  93. ud_set_syntax(&ud, UD_SYN_INTEL);
  94. *trampolineSize = 0;
  95. if(!(trampoline = alloc_rwx(PAGE_BOUNDARY)))
  96. return (void*)CANT_ALLOC;
  97. printf("trampoline @ %p\n", trampoline);
  98. size_t sz = 0;
  99. for(; sz <= functionSize;)
  100. {
  101. size_t instrLen = 0,
  102. offsetOfInstr = sz;
  103. bool neededFixup = false;
  104. uint8_t* addrForInstr = (uint8_t*)trampoline + *trampolineSize;
  105. const uint8_t* buf = NULL;
  106. assert(*trampolineSize < PAGE_BOUNDARY);
  107. if(!(instrLen = ud_disassemble(&ud)) || ud.error)
  108. return 0;
  109. buf = ud_insn_ptr(&ud);
  110. sz += instrLen;
  111. printf("%p %s", ud_insn_off(&ud), ud_insn_asm(&ud));
  112. // Check for XXX [rip + ?]
  113. const struct ud_operand* op = NULL;
  114. for(int i = 0; ; i++, op = ud_insn_opr(&ud, i))
  115. {
  116. if((op->type & UD_OP_REG) == UD_OP_REG)
  117. {
  118. if(op->base != UD_R_RIP)
  119. continue;
  120. neededFixup = true;
  121. int32_t ripDelta = get_rip_delta(&ud);
  122. void* absoluteAddr = ripDelta + ud_insn_off(&ud) + instrLen;
  123. printf("RIP + %x == %p\n", ripDelta, absoluteAddr);
  124. *trampolineSize += write_instr_with_rip_delta(&ud, absoluteAddr, buf, addrForInstr);
  125. break;
  126. }
  127. }
  128. // Check for jmp/jcc that would jump into the void after the trampoline
  129. size_t originalOff;
  130. if((originalOff = get_jump_offset(&ud, offsetOfInstr)) >= functionSize)
  131. {
  132. neededFixup = true;
  133. void* absoluteAddr = originalOff + (uint8_t*)function;
  134. #if 0
  135. printf("\nWould jump behind trampoline (to %p -- offs: %d) into the void\n", absoluteAddr, originalOff);
  136. printf("Instr in Trampoline @ %d jumps %d forward\n", *trampolineSize, originalOff);
  137. printf("%p - %p = %p\n", trampoline, ((ptrdiff_t)function + originalOff), (ptrdiff_t)trampoline - ((ptrdiff_t)function + originalOff));
  138. #endif
  139. // Normal jump
  140. if(buf[0] == 0xEB || buf[0] == 0xE9)
  141. {
  142. *trampolineSize += write_x64_jump(addrForInstr, absoluteAddr);
  143. }
  144. // jcc todo: jecxz, loop(?)
  145. else
  146. {
  147. *trampolineSize += write_jcc_jump(buf, addrForInstr, absoluteAddr);
  148. }
  149. }
  150. // todo: Fix relative calls
  151. printf("\n");
  152. // If it was a normal instruction, just copy it
  153. if(!neededFixup)
  154. {
  155. memcpy(addrForInstr, buf, instrLen);
  156. *trampolineSize += instrLen;
  157. }
  158. }
  159. // Build jmp back to original function
  160. *trampolineSize += write_x64_jump((uint8_t*)trampoline + *trampolineSize, (uint8_t*)function + sz);
  161. return trampoline;
  162. }
  163. static size_t write_instr_with_rip_delta(ud_t* ud, uint64_t value, const uint8_t* opcBuf, unsigned char* where)
  164. {
  165. // 5, d, 15, 1d, 25
  166. printf(value);
  167. /*XXX yyy, [addr]
  168. jmp behind
  169. addr:
  170. value
  171. behind:
  172. */
  173. return 0;
  174. }
  175. static int32_t get_rip_delta(ud_t* ud)
  176. {
  177. const struct ud_operand* op = NULL;
  178. int32_t ret = 0;
  179. for(int i = 0; ; i++, op = ud_insn_opr(ud, i))
  180. {
  181. // Only the RIP offset is interesting
  182. if((op->type & UD_OP_REG) != UD_OP_REG || op->base != UD_R_RIP)
  183. continue;
  184. return op->lval.sdword;
  185. }
  186. return ret;
  187. }
  188. static size_t write_jcc_jump(const uint8_t* instruction, void* whereToWrite, void* originalAddr)
  189. {
  190. /*
  191. jX $+?
  192. is transformed into:
  193. jnX behind
  194. jmp [originalAddr]
  195. originalAddr:
  196. ?
  197. behind:
  198. */
  199. // Stolen from https://github.com/TsudaKageyu/minhook/blob/master/src/trampoline.c
  200. uint8_t cond = ((instruction[0] != 0x0F ? instruction[0] : instruction[1]) & 0x0F);
  201. uint8_t newConditionOpc = 0x71 ^ cond; // Invert the condition in x64 mode to simplify the conditional jump logic.
  202. // jnx behind
  203. char jccTemplate[] = {newConditionOpc, 0x0F};
  204. memcpy((unsigned char*)whereToWrite, jccTemplate, sizeof(jccTemplate));
  205. size_t written = sizeof(jccTemplate);
  206. // jmp [originalAddr]
  207. written += write_x64_jump((unsigned char*)whereToWrite + written, originalAddr);
  208. return written;
  209. }
  210. static size_t get_jump_offset(ud_t* ud, size_t offsetOfInstr)
  211. {
  212. const struct ud_operand* op = NULL;
  213. for(int i = 0; ; i++, op = ud_insn_opr(ud, i))
  214. {
  215. if(op->type == UD_OP_JIMM)
  216. {
  217. return op->lval.sdword + offsetOfInstr + ud_insn_len(ud);
  218. }
  219. // all other types are weird jumps, like jumps to a register or to []
  220. }
  221. return 0;
  222. }
  223. static bool is_jump(enum ud_mneomic_code op)
  224. {
  225. return op >= UD_Ijo && op <= UD_Ijmp;
  226. }
  227. static bool is_end_of_function(enum ud_mneomic_code opc, ud_t* ud, size_t furthestJump, size_t instrOffset)
  228. {
  229. if(opc == UD_Iret && furthestJump <= instrOffset)
  230. return true;
  231. else if(opc == UD_Ijmp)
  232. {
  233. const struct ud_operand* op = ud_insn_opr(ud, 0);
  234. if(op->type == UD_OP_MEM || op->type == UD_OP_REG)
  235. return true;
  236. }
  237. // todo: 0xCC ?
  238. return false;
  239. }
  240. static size_t estimate_function_length(void* function)
  241. {
  242. size_t furthestJump = 0; //! Functions can have multiple return points. The library assumes that if a RET is hit and a jmp/jcc went over it, this RET isn't the last
  243. unsigned char* p = function;
  244. size_t funcLen = 0;
  245. ud_t ud;
  246. ud_init(&ud);
  247. ud_set_input_buffer(&ud, function, (size_t)function | (PAGE_BOUNDARY - 1)); // Can't read further than page boundary - there be dragons
  248. ud_set_pc(&ud, (uint64_t)function);
  249. ud_set_mode(&ud, 64);
  250. ud_set_syntax(&ud, UD_SYN_INTEL);
  251. while(funcLen < MINIMUM_REQUIRED_FUNCTION_LENGTH_LONG_HOOK && !ud_input_end(&ud))
  252. {
  253. size_t instrLen = 0,
  254. offsetOfInstr = funcLen;
  255. if(!(instrLen = ud_disassemble(&ud)) || ud.error)
  256. return 0;
  257. p += instrLen;
  258. funcLen += instrLen;
  259. printf("%p %s", ud_insn_off(&ud), ud_insn_asm(&ud));
  260. enum ud_mneomic_code op = ud_insn_mnemonic(&ud);
  261. if(is_jump(op))
  262. {
  263. size_t off = get_jump_offset(&ud, offsetOfInstr);
  264. if(off > furthestJump)
  265. furthestJump = off;
  266. }
  267. if(is_end_of_function(op, &ud, furthestJump, offsetOfInstr))
  268. {
  269. printf("end of function\n");
  270. break;
  271. }
  272. printf("\n");
  273. }
  274. return funcLen;
  275. }
  276. static size_t write_x64_jump(unsigned char* where, void* toWhere)
  277. {
  278. const char jmpMemTemplate[] = {0x48, 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00};
  279. memcpy(where, jmpMemTemplate, sizeof(jmpMemTemplate));
  280. where += sizeof(jmpMemTemplate);
  281. memcpy(where, &toWhere, sizeof(toWhere));
  282. return sizeof(jmpMemTemplate)+sizeof(toWhere);
  283. }