PatchSolution3-i386.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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-i386.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_32),
  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_32),
  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 KeywordType::IMM_DATA
  25. // except pattern "mov [ebp - 0x4], IMM"
  26. // 2. push IMM (IMM must consist of printable chars) // for KeywordType::IMM_DATA
  27. // 3. push offset MEM (MEM must point to a non-empty printable string) // for KeywordType::STRING_DATA
  28. //
  29. if (_stricmp(lpInsn->mnemonic, "mov") == 0) {
  30. // filter the case "mov [ebp - 0x4], IMM"
  31. // because IMM may consist of printable chars in that case, which will mislead us.
  32. //
  33. // Here I use "> -0x30" to intensify condition, instead of "== -0x4"
  34. if (lpInsn->detail->x86.operands[0].type == X86_OP_MEM &&
  35. lpInsn->detail->x86.operands[0].mem.base == X86_REG_EBP &&
  36. lpInsn->detail->x86.operands[0].mem.disp > -0x30)
  37. {
  38. return false;
  39. }
  40. if (lpInsn->detail->x86.operands[1].type != X86_OP_IMM) {
  41. return false;
  42. }
  43. auto pbImmValue = lpInsn->bytes + lpInsn->detail->x86.encoding.imm_offset;
  44. auto cbImmValue = lpInsn->detail->x86.encoding.imm_size;
  45. // each bytes of imm must be printable;
  46. return IsPrintable(pbImmValue, cbImmValue);
  47. } else if (_stricmp(lpInsn->mnemonic, "push") == 0) {
  48. if (lpInsn->detail->x86.operands[0].type != X86_OP_IMM) {
  49. return false;
  50. }
  51. // test if match pattern 2
  52. auto pbImmValue = lpInsn->bytes + lpInsn->detail->x86.encoding.imm_offset;
  53. auto cbImmValue = lpInsn->detail->x86.encoding.imm_size;
  54. if (IsPrintable(pbImmValue, cbImmValue)) {
  55. return true;
  56. }
  57. // test if match pattern 3
  58. auto StringRva = static_cast<uintptr_t>(
  59. lpInsn->detail->x86.operands[0].imm - _Image.ImageNtHeaders()->OptionalHeader.ImageBase
  60. );
  61. try {
  62. auto StringPtr = _Image.RvaToPointer<const char*>(StringRva);
  63. auto StringLength = strlen(StringPtr);
  64. // StringPtr must have at least one char
  65. // every char in StringPtr must be printable, otherwise pattern mismatches
  66. return StringLength && IsPrintable(StringPtr, StringLength);
  67. } catch (nkg::Exception&) {
  68. // If not found, pattern mismatches
  69. return false;
  70. }
  71. } else {
  72. return false;
  73. }
  74. }
  75. [[nodiscard]]
  76. bool PatchSolution3::CheckIfFound(const cs_insn* lpInsn, size_t KeywordIdx) const noexcept {
  77. // the instruction we're interested in has one of the following patterns:
  78. // 1. mov PTR [MEM], IMM (IMM must consist of printable chars) // for KeywordType::IMM_DATA
  79. // except pattern "mov [ebp - 0x4], IMM"
  80. // 2. push IMM (IMM must consist of printable chars) // for KeywordType::IMM_DATA
  81. // 3. push offset MEM (MEM must point to a non-empty printable string) // for KeywordType::STRING_DATA
  82. //
  83. auto& op_count = lpInsn->detail->x86.op_count;
  84. auto& operands = lpInsn->detail->x86.operands;
  85. if (op_count < 1 || operands[op_count - 1].type != X86_OP_IMM) {
  86. return false;
  87. }
  88. if (Keyword[KeywordIdx].Type == IMM_DATA) {
  89. static_assert(sizeof(operands[op_count - 1].imm) == sizeof(Keyword[KeywordIdx].Value));
  90. return
  91. operands[op_count - 1].imm == *reinterpret_cast<const int64_t*>(Keyword[KeywordIdx].Value) &&
  92. lpInsn->detail->x86.encoding.imm_size == Keyword[KeywordIdx].Size;
  93. } else if (Keyword[KeywordIdx].Type == STRING_DATA) {
  94. auto StringRva = static_cast<uintptr_t>(
  95. operands[op_count - 1].imm - _Image.ImageNtHeaders()->OptionalHeader.ImageBase
  96. );
  97. try {
  98. auto StringPtr = _Image.RvaToPointer<const char*>(StringRva);
  99. return
  100. strncmp(StringPtr, reinterpret_cast<const char*>(Keyword[KeywordIdx].Value), Keyword[KeywordIdx].Size) == 0 &&
  101. StringPtr[Keyword[KeywordIdx].Size] == '\x00';
  102. } catch (nkg::Exception&) {
  103. return false;
  104. }
  105. } else {
  106. return false;
  107. }
  108. }
  109. [[nodiscard]]
  110. PatchSolution3::PatchInfo PatchSolution3::CreatePatchPoint(const void* lpOpcode, const cs_insn* lpInsn, size_t KeywordIdx) const noexcept {
  111. PatchInfo NewPatch;
  112. NewPatch.OpcodeRva = lpInsn->address;
  113. NewPatch.lpOpcode = const_cast<void*>(lpOpcode);
  114. if (Keyword[KeywordIdx].Type == IMM_DATA) {
  115. NewPatch.lpPatch = address_offset(NewPatch.lpOpcode, lpInsn->detail->x86.encoding.imm_offset);
  116. NewPatch.cbPatch = lpInsn->detail->x86.encoding.imm_size;
  117. NewPatch.lpOriginalString = nullptr;
  118. } else {
  119. auto StringRva = static_cast<uintptr_t>(
  120. lpInsn->detail->x86.operands[0].imm - _Image.ImageNtHeaders()->OptionalHeader.ImageBase
  121. );
  122. NewPatch.lpOriginalString = _Image.RvaToPointer<char*>(StringRva);
  123. if (Keyword[KeywordIdx].NotRecommendedToModify) {
  124. NewPatch.lpPatch = address_offset(NewPatch.lpOpcode, lpInsn->detail->x86.encoding.imm_offset);
  125. NewPatch.cbPatch = lpInsn->detail->x86.encoding.imm_size;
  126. } else {
  127. NewPatch.lpPatch = reinterpret_cast<uint8_t*>(NewPatch.lpOriginalString);
  128. NewPatch.cbPatch = Keyword[KeywordIdx].Size;
  129. }
  130. }
  131. NewPatch.lpReplaceString = nullptr;
  132. return NewPatch;
  133. }
  134. [[nodiscard]]
  135. bool PatchSolution3::FindPatchOffset() noexcept {
  136. try {
  137. static const uint8_t HeaderOfTargetFunction[] = {
  138. 0x55, // push ebp
  139. 0x8B, 0xEC, // mov ebp, esp
  140. 0x6A, 0xFF // push 0xffffffff
  141. };
  142. PatchInfo Patch[_countof(_Patch)] = {};
  143. const uint8_t* lpTargetFunction = nullptr;
  144. auto lptargetFunctionHint = _Image.SearchSection<const uint8_t*>(".text", [&lpTargetFunction](const uint8_t* p) {
  145. __try {
  146. if (*reinterpret_cast<const uint32_t*>(p) == 0x6b67424e) {
  147. auto i = p - 0x1B0;
  148. for (; i < p; ++i) {
  149. if (memcmp(i, HeaderOfTargetFunction, sizeof(HeaderOfTargetFunction)) == 0) {
  150. lpTargetFunction = i;
  151. return true;
  152. }
  153. }
  154. }
  155. return false;
  156. } __except (EXCEPTION_EXECUTE_HANDLER) {
  157. return false;
  158. }
  159. });
  160. size_t KeywordIndex = 0;
  161. CapstoneDisassembler Disassembler = _Engine.CreateDisassembler();
  162. Disassembler.SetContext(CapstoneContext{ lpTargetFunction, 0x9014, _Image.PointerToRva(lpTargetFunction) });
  163. while (Disassembler.Next()) {
  164. auto lpInsn = Disassembler.GetInstruction();
  165. if (lpInsn->mnemonic[0] == 'j' || lpInsn->mnemonic[0] == 'J') {
  166. auto JumpedBranch = GetJumpedBranch(Disassembler.GetContext(), lpInsn);
  167. if (_stricmp(lpInsn->mnemonic, "jmp") == 0) {
  168. Disassembler.SetContext(JumpedBranch);
  169. } else {
  170. Disassembler.SetContext(SelectBranch(Disassembler.GetContext(), JumpedBranch, KeywordIndex));
  171. }
  172. } else if (_stricmp(lpInsn->mnemonic, "ret") == 0) {
  173. return false;
  174. } else {
  175. if (CheckIfMatchPattern(lpInsn) == false) {
  176. continue;
  177. }
  178. if (CheckIfFound(lpInsn, KeywordIndex) == false) {
  179. return false;
  180. }
  181. Patch[KeywordIndex] = CreatePatchPoint(Disassembler.GetInstructionContext().lpMachineCode, lpInsn, KeywordIndex);
  182. ++KeywordIndex;
  183. }
  184. if (KeywordIndex == _countof(Patch)) {
  185. break;
  186. }
  187. }
  188. if (KeywordIndex != _countof(Patch)) {
  189. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Some patches are not found."));
  190. }
  191. LOG_SUCCESS(0, "PatchSolution3 ...... Ready to apply");
  192. for (size_t i = 0; i < _countof(Patch); ++i) {
  193. _Patch[i] = Patch[i];
  194. LOG_HINT(4, "[%3zu] Instruction RVA = 0x%.8llx, Patch Offset = +0x%.8zx", i, _Patch[i].OpcodeRva, address_delta(_Patch[i].lpPatch, _Image.ImageBase()));
  195. }
  196. return true;
  197. } catch (nkg::Exception&) {
  198. memset(_Patch, 0, sizeof(_Patch));
  199. LOG_FAILURE(0, "PatchSolution3 ...... Omitted");
  200. return false;
  201. }
  202. }
  203. }