Solution2.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. #include "def.hpp"
  2. namespace Patcher {
  3. const char Solution2::KeywordsMeta[KeywordsCount + 1] =
  4. "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I"
  5. "qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv"
  6. "a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF"
  7. "R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2"
  8. "WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt"
  9. "YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ"
  10. "awIDAQAB";
  11. uint8_t Solution2::Keywords[KeywordsCount][5];
  12. #if defined(_M_X64)
  13. void Solution2::BuildKeywords() noexcept {
  14. for (size_t i = 0; i < KeywordsCount; ++i) {
  15. Keywords[i][0] = 0x83; // Keywords[i] = asm('xor eax, KeywordsMeta[i]') +
  16. Keywords[i][1] = 0xf0;
  17. Keywords[i][2] = KeywordsMeta[i];
  18. Keywords[i][3] = 0x88; // asm_prefix('mov byte ptr ds:xxxxxxxxxxxxxxxx, al')
  19. Keywords[i][4] = 0x05;
  20. }
  21. }
  22. bool Solution2::FindPatchOffset() noexcept {
  23. PIMAGE_SECTION_HEADER textSection = nullptr;
  24. uint8_t* pTargetFileView = pTargetFile->GetView<uint8_t>();
  25. uint8_t* ptextSectionData = nullptr;
  26. off_t Offsets[KeywordsCount];
  27. memset(Offsets, -1, sizeof(Offsets));
  28. textSection = Helper::ImageSectionHeader(pTargetFileView, ".text");
  29. if (textSection == nullptr)
  30. return false;
  31. ptextSectionData = pTargetFileView + textSection->PointerToRawData;
  32. BuildKeywords();
  33. // Find offsets
  34. {
  35. size_t FirstKeywordCounter = 0;
  36. uint32_t Hints[9];
  37. DWORD PossibleRangeStart = 0xffffffff;
  38. DWORD PossibleRangeEnd;
  39. for (DWORD i = 0; i < textSection->SizeOfRawData; ++i) {
  40. if (memcmp(ptextSectionData + i, Keywords[0], sizeof(Keywords[0])) == 0) {
  41. Hints[FirstKeywordCounter++] =
  42. *reinterpret_cast<uint32_t*>(ptextSectionData + i + sizeof(Keywords[0])) +
  43. i + sizeof(Keywords[0]) + sizeof(uint32_t);
  44. if (i < PossibleRangeStart)
  45. PossibleRangeStart = i;
  46. }
  47. }
  48. PossibleRangeStart -= 0x1000;
  49. PossibleRangeEnd = PossibleRangeStart + 0x100000;
  50. // Keywords[0] should occur 9 times.
  51. // Because there's only 9 'M' chars in `KeywordsMeta`.
  52. if (FirstKeywordCounter != 9)
  53. return false;
  54. Helper::QuickSort(Hints, 0, _countof(Hints));
  55. // assert
  56. // if not satisfied, refuse to patch
  57. if (Hints[8] - Hints[0] != 0x18360F8F8 - 0x18360F7D0)
  58. return false;
  59. for (size_t i = 0; i < KeywordsCount; ++i) {
  60. if (Offsets[i] != -1)
  61. continue;
  62. for (DWORD j = PossibleRangeStart; j < PossibleRangeEnd; ++j) {
  63. if (memcmp(ptextSectionData + j, Keywords[i], sizeof(Keywords[i])) == 0) {
  64. off_t index =
  65. *reinterpret_cast<uint32_t*>(ptextSectionData + j + sizeof(Keywords[i])) +
  66. j + sizeof(Keywords[i]) + sizeof(uint32_t) - Hints[0];
  67. if (0 <= index && index < KeywordsCount && KeywordsMeta[index] == KeywordsMeta[i]) {
  68. Offsets[index] = textSection->PointerToRawData + j;
  69. }
  70. }
  71. }
  72. // if not found, refuse to patch
  73. if (Offsets[i] == -1)
  74. return false;
  75. }
  76. }
  77. static_assert(sizeof(PatchOffsets) == sizeof(Offsets), "static_assert failure!");
  78. memcpy(PatchOffsets, Offsets, sizeof(PatchOffsets));
  79. for (size_t i = 0; i < KeywordsCount; ++i)
  80. _tprintf_s(TEXT("MESSAGE: [Solution2] Keywords[%zu] has been found: offset = +0x%08lx.\n"),
  81. i, PatchOffsets[i]);
  82. return true;
  83. }
  84. #else
  85. void Solution2::BuildKeywords() noexcept {
  86. for (size_t i = 0; i < KeywordsCount; ++i) {
  87. switch (i % 3) {
  88. case 0:
  89. Keywords[i][0] = 0x83; // Keywords[i] = asm('xor edx, KeywordsMeta[i]') +
  90. Keywords[i][1] = 0xf2;
  91. Keywords[i][2] = KeywordsMeta[i];
  92. Keywords[i][3] = 0x88; // asm_prefix('mov byte ptr ds:xxxxxxxx, dl')
  93. Keywords[i][4] = 0x15;
  94. break;
  95. case 1:
  96. Keywords[i][0] = 0x83; // Keywords[i] = asm('xor eax, KeywordsMeta[i]') +
  97. Keywords[i][1] = 0xf0;
  98. Keywords[i][2] = KeywordsMeta[i];
  99. Keywords[i][3] = 0xa2; // asm_prefix('mov byte ptr ds:xxxxxxxx, al')
  100. break;
  101. default:
  102. Keywords[i][0] = 0x83; // Keywords[i] = asm('xor ecx, KeywordsMeta[i]') +
  103. Keywords[i][1] = 0xf1;
  104. Keywords[i][2] = KeywordsMeta[i];
  105. Keywords[i][3] = 0x88; // asm_prefix('mov byte ptr ds:xxxxxxxx, cl')
  106. Keywords[i][4] = 0x0D;
  107. break;
  108. }
  109. }
  110. }
  111. bool Solution2::FindPatchOffset() noexcept {
  112. PIMAGE_SECTION_HEADER textSection = nullptr;
  113. uint8_t* pTargetFileView = pTargetFile->GetView<uint8_t>();
  114. uint8_t* ptextSectionData = nullptr;
  115. off_t Offsets[KeywordsCount];
  116. memset(Offsets, -1, sizeof(Offsets));
  117. textSection = Helper::ImageSectionHeader(pTargetFileView, ".text");
  118. if (textSection == nullptr)
  119. return false;
  120. ptextSectionData = pTargetFileView + textSection->PointerToRawData;
  121. BuildKeywords();
  122. // Find offsets
  123. {
  124. size_t FirstKeywordCounter = 0;
  125. uint32_t Hints[3];
  126. DWORD PossibleRangeStart = 0xffffffff;
  127. DWORD PossibleRangeEnd;
  128. for (DWORD i = 0; i < textSection->SizeOfRawData; ++i) {
  129. if (memcmp(ptextSectionData + i, Keywords[0], sizeof(Keywords[0])) == 0) {
  130. Hints[FirstKeywordCounter++] =
  131. *reinterpret_cast<uint32_t*>(ptextSectionData + i + sizeof(Keywords[0]));
  132. if (i < PossibleRangeStart)
  133. PossibleRangeStart = i;
  134. }
  135. }
  136. PossibleRangeStart -= 0x1000;
  137. PossibleRangeEnd = PossibleRangeStart + 0x100000;
  138. // Keywords[0] should occur 3 times.
  139. if (FirstKeywordCounter != 3)
  140. return false;
  141. Helper::QuickSort(Hints, 0, _countof(Hints));
  142. // assert
  143. // if not satisfied, refuse to patch
  144. if (Hints[2] - Hints[0] != 0x127382BE - 0x12738210)
  145. return false;
  146. for (size_t i = 0; i < KeywordsCount; ++i) {
  147. uint8_t CurrentKeyword[9];
  148. size_t CurrentKeywordSize = i % 3 == 1 ? 4 : 5;
  149. memcpy(CurrentKeyword, Keywords[i], CurrentKeywordSize);
  150. *reinterpret_cast<uint32_t*>(CurrentKeyword + CurrentKeywordSize) = Hints[0] + i;
  151. CurrentKeywordSize += sizeof(uint32_t);
  152. for (DWORD j = PossibleRangeStart; j < PossibleRangeEnd; ++j) {
  153. if (memcmp(ptextSectionData + j, CurrentKeyword, CurrentKeywordSize) == 0) {
  154. Offsets[i] = textSection->PointerToRawData + j;
  155. break;
  156. }
  157. }
  158. // if not found, refuse to patch
  159. if (Offsets[i] == -1)
  160. return false;
  161. }
  162. }
  163. static_assert(sizeof(PatchOffsets) == sizeof(Offsets), "static_assert failure!");
  164. memcpy(PatchOffsets, Offsets, sizeof(PatchOffsets));
  165. for (size_t i = 0; i < KeywordsCount; ++i)
  166. _tprintf_s(TEXT("MESSAGE: [Solution2] Keywords[%zu] has been found: offset = +0x%08lx.\n"),
  167. i, PatchOffsets[i]);
  168. return true;
  169. }
  170. #endif
  171. bool Solution2::MakePatch(RSACipher* cipher) const {
  172. std::string RSAPublicKeyPEM;
  173. uint8_t* pTargetFileView = pTargetFile->GetView<uint8_t>();
  174. RSAPublicKeyPEM = cipher->ExportKeyString<RSACipher::KeyType::PublicKey, RSACipher::KeyFormat::PEM>();
  175. if (RSAPublicKeyPEM.empty()) {
  176. REPORT_ERROR("ERROR: cipher->ExportKeyString failed.");
  177. return false;
  178. }
  179. RSAPublicKeyPEM.erase(RSAPublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"), 26);
  180. RSAPublicKeyPEM.erase(RSAPublicKeyPEM.find("-----END PUBLIC KEY-----"), 24);
  181. {
  182. std::string::size_type pos = 0;
  183. while ((pos = RSAPublicKeyPEM.find("\n", pos)) != std::string::npos) {
  184. RSAPublicKeyPEM.erase(pos, 1);
  185. }
  186. }
  187. if (RSAPublicKeyPEM.length() != KeywordsCount) {
  188. REPORT_ERROR("ERROR: Public key length does not match.");
  189. return false;
  190. }
  191. PRINT_MESSAGE("//");
  192. PRINT_MESSAGE("// Begin Solution2");
  193. PRINT_MESSAGE("//");
  194. for (size_t i = 0; i < KeywordsCount; ++i) {
  195. _tprintf_s(TEXT("@+0x%08X: %02X %02X %02X --> "),
  196. PatchOffsets[i],
  197. pTargetFileView[PatchOffsets[i]],
  198. pTargetFileView[PatchOffsets[i] + 1],
  199. pTargetFileView[PatchOffsets[i] + 2]);
  200. pTargetFileView[PatchOffsets[i] + 2] = RSAPublicKeyPEM[i];
  201. _tprintf_s(TEXT("%02X %02X %02X\n"),
  202. pTargetFileView[PatchOffsets[i]],
  203. pTargetFileView[PatchOffsets[i] + 1],
  204. pTargetFileView[PatchOffsets[i] + 2]);
  205. }
  206. PRINT_MESSAGE("");
  207. return true;
  208. }
  209. }