PatchSolution3-generic.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  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-generic.cpp")
  5. #define NKG_CURRENT_SOURCE_LINE() __LINE__
  6. namespace nkg {
  7. const PatchSolution3::KeywordInfo PatchSolution3::Keyword[111] = {
  8. { { 0x4d, 0x49, 0x49 }, 3, STRING_DATA, false },
  9. { { 0x42, 0x49 }, 2, IMM_DATA, false },
  10. { { 0x6a }, 1, IMM_DATA, false },
  11. { { 0x41 }, 1, IMM_DATA, false },
  12. { { 0x4e, 0x42, 0x67, 0x6b }, 4, IMM_DATA, false },
  13. { { 0x71 }, 1, IMM_DATA, false },
  14. { { 0x68, 0x6b, 0x69, 0x47, 0x39, 0x77 }, 6, STRING_DATA, false },
  15. { { 0x30 }, 1, STRING_DATA, true },
  16. { { 0x42 }, 1, IMM_DATA, false },
  17. { { 0x41 }, 1, IMM_DATA, false },
  18. { { 0x51, 0x45, 0x46, 0x41, 0x41, 0x4f, 0x43 }, 7, STRING_DATA, false },
  19. { { 0x41, 0x51, 0x38, 0x41, 0x4d, 0x49 }, 6, STRING_DATA, false },
  20. { { 0x49, 0x42 }, 2, STRING_DATA, false },
  21. { { 0x43, 0x67, 0x4b, 0x43 }, 4, IMM_DATA, false },
  22. { { 0x41, 0x51 }, 2, STRING_DATA, false },
  23. { { 0x45, 0x41, 0x77, 0x31 }, 4, IMM_DATA, false },
  24. { { 0x64, 0x71, 0x46, 0x33 }, 4, IMM_DATA, false },
  25. { { 0x53 }, 1, STRING_DATA, true },
  26. { { 0x6b, 0x43, 0x61, 0x41, 0x41, 0x6d }, 6, STRING_DATA, false },
  27. { { 0x4d, 0x7a, 0x73, 0x38 }, 4, IMM_DATA, false },
  28. { { 0x38, 0x39, 0x49, 0x71 }, 4, IMM_DATA, false },
  29. { { 0x64 }, 1, IMM_DATA, false },
  30. { { 0x57 }, 1, IMM_DATA, false },
  31. { { 0x39, 0x4d, 0x32, 0x64 }, 4, IMM_DATA, false },
  32. { { 0x49, 0x64, 0x68 }, 3, STRING_DATA, false },
  33. { { 0x33, 0x6a }, 2, IMM_DATA, false },
  34. { { 0x47, 0x39, 0x79, 0x50 }, 4, IMM_DATA, false },
  35. { { 0x63, 0x6d }, 2, IMM_DATA, false },
  36. { { 0x4c }, 1, IMM_DATA, false },
  37. { { 0x6e, 0x6d, 0x4a }, 3, STRING_DATA, false },
  38. { { 0x69, 0x47, 0x70, 0x42, 0x46, 0x34, 0x45 }, 7, STRING_DATA, false },
  39. { { 0x39, 0x56, 0x48, 0x53, 0x4d, 0x47 }, 6, STRING_DATA, false },
  40. { { 0x65, 0x38, 0x6f, 0x50, 0x41, 0x79, 0x32, 0x6b }, 8, STRING_DATA, false },
  41. { { 0x4a, 0x44 }, 2, STRING_DATA, false },
  42. { { 0x6d, 0x64 }, 2, IMM_DATA, false },
  43. { { 0x4e, 0x74, 0x34 }, 3, STRING_DATA, false },
  44. { { 0x42, 0x63, 0x45, 0x79, 0x67, 0x76 }, 6, STRING_DATA, false },
  45. { { 0x73, 0x73, 0x45, 0x66, 0x67, 0x69 }, 6, STRING_DATA, false },
  46. { { 0x6e, 0x76, 0x61, 0x35, 0x74 }, 5, STRING_DATA, false },
  47. { { 0x35, 0x6a, 0x6d, 0x33, 0x35, 0x32 }, 6, STRING_DATA, false },
  48. { { 0x55, 0x41 }, 2, IMM_DATA, false },
  49. { { 0x6f, 0x44, 0x6f, 0x73 }, 4, IMM_DATA, false },
  50. { { 0x55, 0x4a }, 2, IMM_DATA, false },
  51. { { 0x6b, 0x54, 0x58, 0x47, 0x51 }, 5, STRING_DATA, false },
  52. { { 0x68, 0x70, 0x41, 0x57, 0x4d, 0x46 }, 6, STRING_DATA, false },
  53. { { 0x34, 0x66, 0x42, 0x6d, 0x42 }, 5, STRING_DATA, false },
  54. { { 0x70, 0x4f, 0x33, 0x45 }, 4, IMM_DATA, false },
  55. { { 0x65, 0x64 }, 2, IMM_DATA, false },
  56. { { 0x47 }, 1, IMM_DATA, false },
  57. { { 0x36, 0x32, 0x72, 0x4f, 0x73, 0x71 }, 6, STRING_DATA, false },
  58. { { 0x4d }, 1, IMM_DATA, false },
  59. { { 0x42, 0x67, 0x6d, 0x53 }, 4, STRING_DATA, false },
  60. { { 0x64 }, 1, IMM_DATA, false },
  61. { { 0x41, 0x79, 0x78, 0x43, 0x53 }, 5, STRING_DATA, false },
  62. { { 0x50 }, 1, IMM_DATA, false },
  63. { { 0x42, 0x52, 0x4a, 0x49, 0x4f }, 5, STRING_DATA, false },
  64. { { 0x46, 0x52, 0x30, 0x51, 0x67, 0x5a, 0x46, 0x62 }, 8, STRING_DATA, false },
  65. { { 0x52 }, 1, IMM_DATA, false },
  66. { { 0x6e, 0x55, 0x30, 0x66 }, 4, STRING_DATA, false },
  67. { { 0x72, 0x6a, 0x33, 0x34 }, 4, IMM_DATA, false },
  68. { { 0x66 }, 1, STRING_DATA, true },
  69. { { 0x69, 0x56, 0x6d, 0x67 }, 4, IMM_DATA, false },
  70. { { 0x59, 0x69, 0x4c, 0x75 }, 4, STRING_DATA, false },
  71. { { 0x5a, 0x53, 0x41, 0x6d }, 4, IMM_DATA, false },
  72. { { 0x49, 0x62 }, 2, IMM_DATA, false },
  73. { { 0x73 }, 1, IMM_DATA, false },
  74. { { 0x38, 0x5a, 0x78, 0x69 }, 4, IMM_DATA, false },
  75. { { 0x48 }, 1, IMM_DATA, false },
  76. { { 0x50, 0x64, 0x70, 0x31 }, 4, IMM_DATA, false },
  77. { { 0x6f, 0x44 }, 2, IMM_DATA, false },
  78. { { 0x34 }, 1, IMM_DATA, false },
  79. { { 0x74, 0x55, 0x70, 0x76, 0x73, 0x46 }, 6, STRING_DATA, false },
  80. { { 0x63, 0x69, 0x34, 0x51, 0x4a, 0x74 }, 6, STRING_DATA, false },
  81. { { 0x59, 0x4e, 0x6a, 0x4e, 0x6e, 0x47, 0x55 }, 7, STRING_DATA, false },
  82. { { 0x32, 0x57, 0x50, 0x48 }, 4, STRING_DATA, false },
  83. { { 0x36, 0x72, 0x76, 0x43, 0x68, 0x47, 0x6c }, 7, STRING_DATA, false },
  84. { { 0x31, 0x49, 0x52, 0x4b, 0x72, 0x78, 0x4d, 0x74 }, 8, STRING_DATA, false },
  85. { { 0x71, 0x4c, 0x69, 0x65, 0x6c }, 5, STRING_DATA, false },
  86. { { 0x73, 0x76, 0x61, 0x6a, 0x55, 0x6a, 0x79, 0x72 }, 8, STRING_DATA, false },
  87. { { 0x67 }, 1, STRING_DATA, true },
  88. { { 0x4f, 0x43, 0x36, 0x4e, 0x6d, 0x79, 0x6d, 0x59 }, 8, STRING_DATA, false },
  89. { { 0x4d }, 1, IMM_DATA, false },
  90. { { 0x76, 0x5a, 0x4e }, 3, STRING_DATA, false },
  91. { { 0x45, 0x52, 0x33, 0x68, 0x74 }, 5, STRING_DATA, false },
  92. { { 0x46 }, 1, IMM_DATA, false },
  93. { { 0x45, 0x74, 0x4c, 0x31 }, 4, STRING_DATA, false },
  94. { { 0x65, 0x51, 0x62, 0x43, 0x79 }, 5, STRING_DATA, false },
  95. { { 0x54, 0x66, 0x44, 0x6d, 0x74, 0x59, 0x79, 0x51 }, 8, STRING_DATA, false },
  96. { { 0x31, 0x57, 0x74, 0x34 }, 4, STRING_DATA, false },
  97. { { 0x4f }, 1, IMM_DATA, false },
  98. { { 0x74, 0x31, 0x32, 0x6c, 0x78, 0x66 }, 6, STRING_DATA, false },
  99. { { 0x30 }, 1, IMM_DATA, false },
  100. { { 0x77, 0x56, 0x49, 0x52, 0x35 }, 5, STRING_DATA, false },
  101. { { 0x6d }, 1, IMM_DATA, false },
  102. { { 0x63, 0x47, 0x4e, 0x37 }, 4, STRING_DATA, false },
  103. { { 0x58, 0x43, 0x58, 0x4a }, 4, STRING_DATA, false },
  104. { { 0x52, 0x48, 0x4f, 0x46 }, 4, IMM_DATA, false },
  105. { { 0x48, 0x53 }, 2, IMM_DATA, false },
  106. { { 0x66 }, 1, IMM_DATA, false },
  107. { { 0x31, 0x67, 0x7a, 0x58, 0x57 }, 5, STRING_DATA, false },
  108. { { 0x61 }, 1, IMM_DATA, false },
  109. { { 0x62 }, 1, IMM_DATA, false },
  110. { { 0x52, 0x53 }, 2, STRING_DATA, false },
  111. { { 0x76, 0x6d, 0x74, 0x31, 0x6e }, 5, STRING_DATA, false },
  112. { { 0x72, 0x6c }, 2, STRING_DATA, true },
  113. { { 0x37, 0x73, 0x57 }, 3, STRING_DATA, false },
  114. { { 0x36, 0x63, 0x6a }, 3, STRING_DATA, false },
  115. { { 0x78, 0x6c, 0x6a, 0x75, 0x75, 0x51, 0x61 }, 7, STRING_DATA, false },
  116. { { 0x77, 0x49, 0x44, 0x41 }, 4, STRING_DATA, false },
  117. { { 0x51, 0x41 }, 2, IMM_DATA, false },
  118. { { 0x42 }, 1, IMM_DATA, false }
  119. };
  120. [[nodiscard]]
  121. bool PatchSolution3::IsPrintable(const void* p, size_t s) noexcept {
  122. auto pb = reinterpret_cast<const uint8_t*>(p);
  123. for (size_t i = 0; i < s; ++i) {
  124. if (isprint(pb[i]) == false) {
  125. return false;
  126. }
  127. }
  128. return true;
  129. }
  130. [[nodiscard]]
  131. CapstoneContext PatchSolution3::GetJumpedBranch(const CapstoneContext& NotJumpedBranch, const cs_insn* lpJxxInsn) const {
  132. CapstoneContext JumpedBranch;
  133. JumpedBranch.lpMachineCode = _Image.RvaToPointer<const void*>(
  134. static_cast<uintptr_t>(lpJxxInsn->detail->x86.operands[0].imm)
  135. );
  136. JumpedBranch.cbMachineCode = NotJumpedBranch.cbMachineCode - (
  137. reinterpret_cast<const uint8_t*>(JumpedBranch.lpMachineCode) -
  138. reinterpret_cast<const uint8_t*>(NotJumpedBranch.lpMachineCode)
  139. );
  140. JumpedBranch.Address = lpJxxInsn->detail->x86.operands[0].imm;
  141. return JumpedBranch;
  142. }
  143. [[nodiscard]]
  144. CapstoneContext PatchSolution3::SelectBranch(const CapstoneContext& NotJumpedBranch, const CapstoneContext& JumpedBranch, size_t KeywordIdx) const {
  145. CapstoneContext A = NotJumpedBranch;
  146. CapstoneContext B = JumpedBranch;
  147. int WeightA = 0;
  148. int WeightB = 0;
  149. auto Disassembler = _Engine.CreateDisassembler();
  150. while (true) {
  151. int WeightAPrev = WeightA;
  152. int WeightBPrev = WeightB;
  153. //
  154. // process NotJumpedBranch
  155. //
  156. Disassembler.SetContext(A);
  157. while (Disassembler.Next()) {
  158. auto lpInsn = Disassembler.GetInstruction();
  159. //
  160. // For all x86 mnemonics, only 'jcc' or 'jmp' starts with 'j' or 'J'.
  161. // So it should be a new branch if we meet them.
  162. //
  163. if (lpInsn->mnemonic[0] == 'j' || lpInsn->mnemonic[0] == 'J') {
  164. auto JumpedBranch = GetJumpedBranch(Disassembler.GetContext(), lpInsn);
  165. if (_stricmp(lpInsn->mnemonic, "jmp") == 0) {
  166. Disassembler.SetContext(JumpedBranch);
  167. } else {
  168. try {
  169. Disassembler.SetContext(SelectBranch(Disassembler.GetContext(), JumpedBranch, KeywordIdx));
  170. } catch (nkg::Exception&) {
  171. // If exception occurs, give up NotJumpedBranch
  172. break;
  173. }
  174. }
  175. } else if (_stricmp(lpInsn->mnemonic, "ret") == 0) {
  176. return JumpedBranch;
  177. } else {
  178. if (CheckIfMatchPattern(lpInsn) == false) {
  179. continue;
  180. }
  181. //
  182. // if match pattern, but keyword doesn't match,
  183. // NotJumpedBranch must not be what we want
  184. //
  185. if (CheckIfFound(lpInsn, KeywordIdx) == false) {
  186. return JumpedBranch;
  187. }
  188. //
  189. // If keyword is succeeded to match
  190. // Add WeightA and stop processing NotJumpedBranch
  191. //
  192. ++WeightA;
  193. break;
  194. }
  195. }
  196. A = Disassembler.GetContext();
  197. //
  198. // process JumpedBranch
  199. //
  200. Disassembler.SetContext(B);
  201. while (Disassembler.Next()) {
  202. auto lpInsn = Disassembler.GetInstruction();
  203. //
  204. // For all x86 mnemonics, only 'jcc' or 'jmp' starts with 'j' or 'J'.
  205. // So it should be a new branch if we meet them.
  206. //
  207. if (lpInsn->mnemonic[0] == 'j' || lpInsn->mnemonic[0] == 'J') {
  208. auto JumpedBranch = GetJumpedBranch(Disassembler.GetContext(), lpInsn);
  209. if (_stricmp(lpInsn->mnemonic, "jmp") == 0) {
  210. Disassembler.SetContext(JumpedBranch);
  211. } else {
  212. try {
  213. Disassembler.SetContext(SelectBranch(Disassembler.GetContext(), JumpedBranch, KeywordIdx));
  214. } catch (nkg::Exception&) {
  215. // If exception occurs, give up JumpedBranch
  216. break;
  217. }
  218. }
  219. } else if (_stricmp(lpInsn->mnemonic, "ret") == 0) {
  220. return NotJumpedBranch;
  221. } else {
  222. if (CheckIfMatchPattern(lpInsn) == false) {
  223. continue;
  224. }
  225. //
  226. // if match pattern, but keyword doesn't match,
  227. // JumpedBranch must not be what we want
  228. //
  229. if (CheckIfFound(lpInsn, KeywordIdx) == false) {
  230. return NotJumpedBranch;
  231. }
  232. //
  233. // If keyword is succeeded to match
  234. // Add WeightB and stop processing JumpedBranch
  235. //
  236. ++WeightB;
  237. break;
  238. }
  239. }
  240. B = Disassembler.GetContext();
  241. //
  242. // If this happens, it means neither of two branch is our target
  243. if (WeightAPrev == WeightA && WeightBPrev == WeightB) {
  244. throw nkg::Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Branch is not selected."));
  245. }
  246. if (WeightA != WeightB)
  247. return WeightA > WeightB ? NotJumpedBranch : JumpedBranch;
  248. else
  249. ++KeywordIdx;
  250. }
  251. }
  252. [[nodiscard]]
  253. bool PatchSolution3::CheckKey(const RSACipher& Cipher) const noexcept {
  254. //
  255. // Brute-force search, cchString should be 1 or 2
  256. //
  257. auto SearchString = [](const void* lpRange, size_t cbRange, const char* lpString, size_t cchString) -> const char* {
  258. const char* p = reinterpret_cast<const char*>(lpRange);
  259. for (size_t i = 0; i < cbRange; ++i) {
  260. if (p[i] == lpString[0]) {
  261. bool match = true;
  262. __try {
  263. for (size_t j = 1; j < cchString; ++j) {
  264. if (p[i + j] != lpString[j]) {
  265. match = false;
  266. break;
  267. }
  268. }
  269. } __except (EXCEPTION_EXECUTE_HANDLER) {
  270. match = false;
  271. }
  272. if (match && p[i + cchString] == '\x00')
  273. return address_offset_cast<const char*>(lpRange, i);
  274. }
  275. }
  276. return nullptr;
  277. };
  278. auto szPublicKey = Cipher.ExportKeyString<RSAKeyType::PublicKey, RSAKeyFormat::PEM>();
  279. for (auto pos = szPublicKey.find("-----BEGIN PUBLIC KEY-----"); pos != std::string::npos; pos = szPublicKey.find("-----BEGIN PUBLIC KEY-----", pos)) {
  280. szPublicKey.erase(pos, literal_length("-----BEGIN PUBLIC KEY-----"));
  281. }
  282. for (auto pos = szPublicKey.find("-----END PUBLIC KEY-----"); pos != std::string::npos; pos = szPublicKey.find("-----END PUBLIC KEY-----", pos)) {
  283. szPublicKey.erase(pos, literal_length("-----END PUBLIC KEY-----"));
  284. }
  285. for (auto pos = szPublicKey.find("\n"); pos != std::string::npos; pos = szPublicKey.find("\n", pos)) {
  286. szPublicKey.erase(pos, literal_length("\n"));
  287. }
  288. if (szPublicKey.length() != 0x188) {
  289. return false;
  290. }
  291. size_t PublicKeyReadCursor = 0;
  292. auto SectionHeader_rdata = _Image.ImageSectionHeaderByName(".rdata");
  293. auto SectionView_rdata = _Image.ImageSectionView(SectionHeader_rdata);
  294. for (size_t i = 0; i < _countof(_Patch); PublicKeyReadCursor += Keyword[i].Size, ++i) {
  295. if (Keyword[i].NotRecommendedToModify) {
  296. _Patch[i].lpReplaceString = nullptr;
  297. const char* lpReplaceString = nullptr;
  298. const void* lpSearchRange = _Patch[i].lpOriginalString;
  299. size_t cbSearchRange = SectionHeader_rdata->SizeOfRawData - address_delta(_Patch[i].lpOriginalString, SectionView_rdata);
  300. for (size_t offset = 0;;) {
  301. lpReplaceString = SearchString(
  302. address_offset(lpSearchRange, offset),
  303. cbSearchRange - offset,
  304. szPublicKey.c_str() + PublicKeyReadCursor,
  305. Keyword[i].Size
  306. );
  307. if (lpReplaceString == nullptr) {
  308. return false;
  309. }
  310. if (_Image.IsRvaRangeInRelocTable(_Image.PointerToRva(lpReplaceString), Keyword[i].Size + 1)) {
  311. //
  312. // Damn it!
  313. // ReplaceString will be modified during relocation
  314. // We have to find another one
  315. //
  316. ++offset;
  317. } else {
  318. //
  319. // ReplaceString won't be modified during relocation
  320. // which can be used to act as a part of public key string
  321. //
  322. break;
  323. }
  324. }
  325. _Patch[i].lpReplaceString = const_cast<char*>(lpReplaceString);
  326. }
  327. }
  328. return true;
  329. }
  330. void PatchSolution3::MakePatch(const RSACipher& Cipher) const {
  331. for (size_t i = 0; i < _countof(_Patch); ++i) {
  332. if (_Patch[i].lpPatch == nullptr) {
  333. throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("PatchSolution3 has not been ready yet."));
  334. }
  335. }
  336. auto szPublicKey = Cipher.ExportKeyString<RSAKeyType::PublicKey, RSAKeyFormat::PEM>();
  337. for (auto pos = szPublicKey.find("-----BEGIN PUBLIC KEY-----"); pos != std::string::npos; pos = szPublicKey.find("-----BEGIN PUBLIC KEY-----", pos)) {
  338. szPublicKey.erase(pos, literal_length("-----BEGIN PUBLIC KEY-----"));
  339. }
  340. for (auto pos = szPublicKey.find("-----END PUBLIC KEY-----"); pos != std::string::npos; pos = szPublicKey.find("-----END PUBLIC KEY-----", pos)) {
  341. szPublicKey.erase(pos, literal_length("-----END PUBLIC KEY-----"));
  342. }
  343. for (auto pos = szPublicKey.find("\n"); pos != std::string::npos; pos = szPublicKey.find("\n", pos)) {
  344. szPublicKey.erase(pos, literal_length("\n"));
  345. }
  346. _putts(TEXT("*******************************************************"));
  347. _putts(TEXT("* PatchSolution3 *"));
  348. _putts(TEXT("*******************************************************"));
  349. size_t readptr = 0;
  350. for (size_t i = 0; i < _countof(_Patch); readptr += Keyword[i].Size, ++i) {
  351. _tprintf_s(TEXT("[*] +%.8zx: "), address_delta(_Patch[i].lpPatch, _Image.ImageBase()));
  352. PrintBytes(_Patch[i].lpPatch, _Patch[i].cbPatch);
  353. _tprintf_s(TEXT(" ---> "));
  354. if (Keyword[i].NotRecommendedToModify) {
  355. auto offset = _Patch[i].lpReplaceString - _Patch[i].lpOriginalString;
  356. uint64_t disp = 0;
  357. memcpy(&disp, _Patch[i].lpPatch, _Patch[i].cbPatch);
  358. disp += offset;
  359. memcpy(_Patch[i].lpPatch, &disp, _Patch[i].cbPatch);
  360. } else {
  361. memcpy(_Patch[i].lpPatch, szPublicKey.c_str() + readptr, Keyword[i].Size);
  362. }
  363. PrintBytes(_Patch[i].lpPatch, _Patch[i].cbPatch);
  364. _tprintf_s(TEXT("\n"));
  365. }
  366. _putts(TEXT(""));
  367. return;
  368. }
  369. }