detour.hpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. #pragma once
  2. #include <nall/foreach.hpp>
  3. #include <nall/platform.hpp>
  4. #include <nall/stdint.hpp>
  5. #include <nall/string.hpp>
  6. #include <nall/utf8.hpp>
  7. namespace nall {
  8. #define Copy 0
  9. #define RelNear 1
  10. struct detour {
  11. static auto insert(const string& moduleName, const string& functionName, void*& source, void* target) -> bool;
  12. static auto remove(const string& moduleName, const string& functionName, void*& source) -> bool;
  13. protected:
  14. static auto length(const uint8* function) -> uint;
  15. static auto mirror(uint8* target, const uint8* source) -> uint;
  16. struct opcode {
  17. uint16 prefix;
  18. uint length;
  19. uint mode;
  20. uint16 modify;
  21. };
  22. static opcode opcodes[];
  23. };
  24. //TODO:
  25. //* fs:, gs: should force another opcode copy
  26. //* conditional branches within +5-byte range should fail
  27. detour::opcode detour::opcodes[] = {
  28. {0x50, 1}, //push eax
  29. {0x51, 1}, //push ecx
  30. {0x52, 1}, //push edx
  31. {0x53, 1}, //push ebx
  32. {0x54, 1}, //push esp
  33. {0x55, 1}, //push ebp
  34. {0x56, 1}, //push esi
  35. {0x57, 1}, //push edi
  36. {0x58, 1}, //pop eax
  37. {0x59, 1}, //pop ecx
  38. {0x5a, 1}, //pop edx
  39. {0x5b, 1}, //pop ebx
  40. {0x5c, 1}, //pop esp
  41. {0x5d, 1}, //pop ebp
  42. {0x5e, 1}, //pop esi
  43. {0x5f, 1}, //pop edi
  44. {0x64, 1}, //fs:
  45. {0x65, 1}, //gs:
  46. {0x68, 5}, //push dword
  47. {0x6a, 2}, //push byte
  48. {0x74, 2, RelNear, 0x0f84}, //je near -> je far
  49. {0x75, 2, RelNear, 0x0f85}, //jne near -> jne far
  50. {0x89, 2}, //mov reg,reg
  51. {0x8b, 2}, //mov reg,reg
  52. {0x90, 1}, //nop
  53. {0xa1, 5}, //mov eax,[dword]
  54. {0xeb, 2, RelNear, 0xe9}, //jmp near -> jmp far
  55. };
  56. inline auto detour::insert(const string& moduleName, const string& functionName, void*& source, void* target) -> bool {
  57. HMODULE module = GetModuleHandleW(utf16_t(moduleName));
  58. if(!module) return false;
  59. uint8* sourceData = (uint8_t*)GetProcAddress(module, functionName);
  60. if(!sourceData) return false;
  61. uint sourceLength = detour::length(sourceData);
  62. if(sourceLength < 5) {
  63. //unable to clone enough bytes to insert hook
  64. #if 1
  65. string output = {"detour::insert(", moduleName, "::", functionName, ") failed: "};
  66. for(uint n = 0; n < 16; n++) output.append(hex<2>(sourceData[n]), " ");
  67. output.trimRight(" ", 1L);
  68. MessageBoxA(0, output, "nall::detour", MB_OK);
  69. #endif
  70. return false;
  71. }
  72. auto mirrorData = new uint8[512]();
  73. detour::mirror(mirrorData, sourceData);
  74. DWORD privileges;
  75. VirtualProtect((void*)mirrorData, 512, PAGE_EXECUTE_READWRITE, &privileges);
  76. VirtualProtect((void*)sourceData, 256, PAGE_EXECUTE_READWRITE, &privileges);
  77. uint64_t address = (uint64_t)target - ((uint64_t)sourceData + 5);
  78. sourceData[0] = 0xe9; //jmp target
  79. sourceData[1] = address >> 0;
  80. sourceData[2] = address >> 8;
  81. sourceData[3] = address >> 16;
  82. sourceData[4] = address >> 24;
  83. VirtualProtect((void*)sourceData, 256, privileges, &privileges);
  84. source = (void*)mirrorData;
  85. return true;
  86. }
  87. inline auto detour::remove(const string& moduleName, const string& functionName, void*& source) -> bool {
  88. HMODULE module = GetModuleHandleW(utf16_t(moduleName));
  89. if(!module) return false;
  90. auto sourceData = (uint8*)GetProcAddress(module, functionName);
  91. if(!sourceData) return false;
  92. auto mirrorData = (uint8*)source;
  93. if(mirrorData == sourceData) return false; //hook was never installed
  94. uint length = detour::length(256 + mirrorData);
  95. if(length < 5) return false;
  96. DWORD privileges;
  97. VirtualProtect((void*)sourceData, 256, PAGE_EXECUTE_READWRITE, &privileges);
  98. for(uint n = 0; n < length; n++) sourceData[n] = mirrorData[256 + n];
  99. VirtualProtect((void*)sourceData, 256, privileges, &privileges);
  100. source = (void*)sourceData;
  101. delete[] mirrorData;
  102. return true;
  103. }
  104. inline auto detour::length(const uint8* function) -> uint {
  105. uint length = 0;
  106. while(length < 5) {
  107. detour::opcode *opcode = 0;
  108. foreach(op, detour::opcodes) {
  109. if(function[length] == op.prefix) {
  110. opcode = &op;
  111. break;
  112. }
  113. }
  114. if(opcode == 0) break;
  115. length += opcode->length;
  116. }
  117. return length;
  118. }
  119. inline auto detour::mirror(uint8* target, const uint8* source) -> uint {
  120. const uint8* entryPoint = source;
  121. for(uint n = 0; n < 256; n++) target[256 + n] = source[n];
  122. uint size = detour::length(source);
  123. while(size) {
  124. detour::opcode* opcode = nullptr;
  125. foreach(op, detour::opcodes) {
  126. if(*source == op.prefix) {
  127. opcode = &op;
  128. break;
  129. }
  130. }
  131. switch(opcode->mode) {
  132. case Copy:
  133. for(uint n = 0; n < opcode->length; n++) *target++ = *source++;
  134. break;
  135. case RelNear: {
  136. source++;
  137. uint64_t sourceAddress = (uint64_t)source + 1 + (int8)*source;
  138. *target++ = opcode->modify;
  139. if(opcode->modify >> 8) *target++ = opcode->modify >> 8;
  140. uint64_t targetAddress = (uint64_t)target + 4;
  141. uint64_t address = sourceAddress - targetAddress;
  142. *target++ = address >> 0;
  143. *target++ = address >> 8;
  144. *target++ = address >> 16;
  145. *target++ = address >> 24;
  146. source += 2;
  147. } break;
  148. }
  149. size -= opcode->length;
  150. }
  151. uint64_t address = (entryPoint + detour::length(entryPoint)) - (target + 5);
  152. *target++ = 0xe9; //jmp entryPoint
  153. *target++ = address >> 0;
  154. *target++ = address >> 8;
  155. *target++ = address >> 16;
  156. *target++ = address >> 24;
  157. return source - entryPoint;
  158. }
  159. #undef Implied
  160. #undef RelNear
  161. }