PatchSolution3-amd64.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. #include "PatchSolutions.hpp"
  2. #undef NKG_CURRENT_SOURCE_FILE
  3. #undef NKG_CURRENT_SOURCE_LINE
  4. #define NKG_CURRENT_SOURCE_FILE() TEXT(".\\navicat-patcher\\PatchSolution3-amd64.cpp")
  5. #define NKG_CURRENT_SOURCE_LINE() __LINE__
  6. namespace nkg {
  7. PatchSolution3::PatchSolution3(const ImageInterpreter& Image) :
  8. _Image(Image),
  9. _Engine(CS_ARCH_X86, CS_MODE_64),
  10. _Patch{}
  11. {
  12. _Engine.Option(CS_OPT_DETAIL, CS_OPT_ON);
  13. }
  14. PatchSolution3::PatchSolution3(const ImageInterpreter* lpImage) :
  15. _Image(*lpImage),
  16. _Engine(CS_ARCH_X86, CS_MODE_64),
  17. _Patch{}
  18. {
  19. _Engine.Option(CS_OPT_DETAIL, CS_OPT_ON);
  20. }
  21. [[nodiscard]]
  22. bool PatchSolution3::CheckIfMatchPattern(const cs_insn* lpInsn) const noexcept {
  23. // the instruction we're interested in has one of the following patterns:
  24. // 1. mov PTR [MEM], IMM (IMM must consist of printable chars) // for IMM_DATA
  25. // 2. lea REG, PTR [MEM] (MEM must point to a non-empty printable string) // for STRING_DATA
  26. if (_stricmp(lpInsn->mnemonic, "mov") == 0) {
  27. if (lpInsn->detail->x86.operands[1].type != X86_OP_IMM) {
  28. return false;
  29. }
  30. auto pbImmValue = lpInsn->bytes + lpInsn->detail->x86.encoding.imm_offset;
  31. auto cbImmValue = lpInsn->detail->x86.encoding.imm_size;
  32. return IsPrintable(pbImmValue, cbImmValue);
  33. } else if (_stricmp(lpInsn->mnemonic, "lea") == 0) {
  34. // as far as I know, all strings are loaded by "lea REG, QWORD PTR [RIP + disp]"
  35. // so operands[1] must look like "[RIP + disp]"
  36. if (lpInsn->detail->x86.operands[1].mem.base != X86_REG_RIP) {
  37. return false;
  38. }
  39. // scale must 1, otherwise pattern mismatches
  40. if (lpInsn->detail->x86.operands[1].mem.scale != 1) {
  41. return false;
  42. }
  43. auto StringRva = static_cast<uintptr_t>(
  44. lpInsn->address + lpInsn->size + // Next RIP
  45. lpInsn->detail->x86.operands[1].mem.disp
  46. );
  47. try {
  48. auto StringPtr = _Image.RvaToPointer<const char*>(StringRva);
  49. auto StringLength = strlen(StringPtr);
  50. // StringPtr must have at least one char
  51. // every char in StringPtr must be printable, otherwise pattern mismatches
  52. return StringLength && IsPrintable(StringPtr, StringLength);
  53. } catch (nkg::Exception&) {
  54. // If not found, pattern mismatches
  55. return false;
  56. }
  57. } else {
  58. return false;
  59. }
  60. }
  61. [[nodiscard]]
  62. bool PatchSolution3::CheckIfFound(const cs_insn* lpInsn, size_t KeywordIdx) const noexcept {
  63. // the instruction we're interested in has one of the following patterns:
  64. // 1. mov PTR [MEM], IMM (IMM must consist of printable chars) // for IMM_DATA
  65. // 2. lea REG, PTR [MEM] (MEM must point to a non-empty printable string) // for STRING_DATA
  66. auto& op_count = lpInsn->detail->x86.op_count;
  67. auto& operands = lpInsn->detail->x86.operands;
  68. if (op_count != 2) {
  69. return false;
  70. }
  71. if (Keyword[KeywordIdx].Type == IMM_DATA && operands[1].type == X86_OP_IMM) {
  72. static_assert(sizeof(operands[1].imm) == sizeof(Keyword[KeywordIdx].Value));
  73. return
  74. operands[1].imm == *reinterpret_cast<const int64_t*>(Keyword[KeywordIdx].Value) &&
  75. lpInsn->detail->x86.encoding.imm_size == Keyword[KeywordIdx].Size;
  76. } else if (Keyword[KeywordIdx].Type == STRING_DATA && operands[1].type == X86_OP_MEM) {
  77. auto StringRva = static_cast<uintptr_t>(
  78. lpInsn->address + lpInsn->size + // Next RIP
  79. operands[1].mem.disp
  80. );
  81. try {
  82. auto StringPtr = _Image.RvaToPointer<const char*>(StringRva);
  83. return
  84. strncmp(StringPtr, reinterpret_cast<const char*>(Keyword[KeywordIdx].Value), Keyword[KeywordIdx].Size) == 0 &&
  85. StringPtr[Keyword[KeywordIdx].Size] == '\x00';
  86. } catch (nkg::Exception&) {
  87. return false;
  88. }
  89. } else {
  90. return false;
  91. }
  92. }
  93. [[nodiscard]]
  94. PatchSolution3::PatchInfo PatchSolution3::CreatePatchPoint(const void* lpOpcode, const cs_insn* lpInsn, size_t KeywordIdx) const noexcept {
  95. PatchInfo NewPatch;
  96. NewPatch.OpcodeRva = lpInsn->address;
  97. NewPatch.lpOpcode = const_cast<void*>(lpOpcode);
  98. if (lpInsn->detail->x86.operands[1].type == X86_OP_MEM) {
  99. auto StringRva = static_cast<uintptr_t>(
  100. lpInsn->address + lpInsn->size + // Next RIP
  101. lpInsn->detail->x86.operands[1].mem.disp
  102. );
  103. NewPatch.lpOriginalString = _Image.RvaToPointer<char*>(StringRva);
  104. if (Keyword[KeywordIdx].NotRecommendedToModify) {
  105. NewPatch.lpPatch = address_offset(NewPatch.lpOpcode, lpInsn->detail->x86.encoding.disp_offset);
  106. NewPatch.cbPatch = lpInsn->detail->x86.encoding.disp_size;
  107. } else {
  108. NewPatch.lpPatch = reinterpret_cast<uint8_t*>(NewPatch.lpOriginalString);
  109. NewPatch.cbPatch = Keyword[KeywordIdx].Size;
  110. }
  111. } else { // X86_OP_IMM
  112. NewPatch.lpPatch = address_offset(NewPatch.lpOpcode, lpInsn->detail->x86.encoding.imm_offset);
  113. NewPatch.cbPatch = lpInsn->detail->x86.encoding.imm_size;
  114. NewPatch.lpOriginalString = nullptr;
  115. }
  116. NewPatch.lpReplaceString = nullptr;
  117. return NewPatch;
  118. }
  119. [[nodiscard]]
  120. bool PatchSolution3::FindPatchOffset() noexcept {
  121. try {
  122. static const uint8_t HeaderOfTargetFunction[] = {
  123. 0x40, 0x55, // push rbp
  124. 0x48, 0x8D, 0xAC, 0x24, 0x70, 0xBC, 0xFF, 0xFF, // lea rbp, [rsp-4390h]
  125. 0xB8, 0x90, 0x44, 0x00, 0x00 // mov eax, 4490h
  126. };
  127. PatchInfo Patch[_countof(_Patch)] = {};
  128. const uint8_t* lpTargetFunction = nullptr;
  129. auto lptargetFunctionHint = _Image.SearchSection<const uint8_t*>(".text", [&lpTargetFunction](const uint8_t* p) {
  130. __try {
  131. if (*reinterpret_cast<const uint32_t*>(p) == 0x6b67424e) {
  132. auto i = p - 0x250;
  133. for (; i < p; ++i) {
  134. if (memcmp(i, HeaderOfTargetFunction, sizeof(HeaderOfTargetFunction)) == 0) {
  135. lpTargetFunction = i;
  136. return true;
  137. }
  138. }
  139. }
  140. return false;
  141. } __except (EXCEPTION_EXECUTE_HANDLER) {
  142. return false;
  143. }
  144. });
  145. size_t KeywordIndex = 0;
  146. CapstoneDisassembler Disassembler = _Engine.CreateDisassembler();
  147. Disassembler.SetContext(CapstoneContext{ lpTargetFunction, 0xcd03, _Image.PointerToRva(lpTargetFunction) });
  148. while (Disassembler.Next()) {
  149. auto lpInsn = Disassembler.GetInstruction();
  150. if (lpInsn->mnemonic[0] == 'j' || lpInsn->mnemonic[0] == 'J') {
  151. auto JumpedBranch = GetJumpedBranch(Disassembler.GetContext(), lpInsn);
  152. if (_stricmp(lpInsn->mnemonic, "jmp") == 0) {
  153. Disassembler.SetContext(JumpedBranch);
  154. } else {
  155. Disassembler.SetContext(SelectBranch(Disassembler.GetContext(), JumpedBranch, KeywordIndex));
  156. }
  157. } else if (_stricmp(lpInsn->mnemonic, "ret") == 0) {
  158. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Reach end of function."));
  159. } else {
  160. if (CheckIfMatchPattern(lpInsn) == false) {
  161. continue;
  162. }
  163. if (CheckIfFound(lpInsn, KeywordIndex) == false) {
  164. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Missing a patch."));
  165. }
  166. Patch[KeywordIndex] = CreatePatchPoint(Disassembler.GetInstructionContext().lpMachineCode, lpInsn, KeywordIndex);
  167. ++KeywordIndex;
  168. }
  169. if (KeywordIndex == _countof(Patch)) {
  170. break;
  171. }
  172. }
  173. if (KeywordIndex != _countof(Patch)) {
  174. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Some patches are not found."));
  175. }
  176. LOG_SUCCESS(0, "PatchSolution3 ...... Ready to apply");
  177. for (size_t i = 0; i < _countof(Patch); ++i) {
  178. _Patch[i] = Patch[i];
  179. LOG_HINT(4, "[%3zu] Instruction RVA = 0x%.8llx, Patch Offset = +0x%.8zx", i, _Patch[i].OpcodeRva, address_delta(_Patch[i].lpPatch, _Image.ImageBase()));
  180. }
  181. return true;
  182. } catch (nkg::Exception&) {
  183. memset(_Patch, 0, sizeof(_Patch));
  184. LOG_FAILURE(0, "PatchSolution3 ...... Omitted");
  185. return false;
  186. }
  187. }
  188. }