PatchSolution3.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. #include "PatchSolutions.hpp"
  2. #include "Misc.hpp"
  3. #include <string.h>
  4. namespace nkg {
  5. PatchSolution3::PatchSolution3(const X64ImageInterpreter& Image) :
  6. m_Image(Image),
  7. m_DisassemblerEngine(CS_ARCH_X86, CS_MODE_64),
  8. m_AssemblerEngine(KS_ARCH_X86, KS_MODE_64),
  9. m_lpfnGenerateKeyA(nullptr),
  10. m_lpfnGenerateKeyB(nullptr)
  11. {
  12. m_DisassemblerEngine.Option(CS_OPT_DETAIL, CS_OPT_ON);
  13. }
  14. void PatchSolution3::ScanInstructions(std::map<X64ImageAddress, X64ImageSize>& Instructions, const section_64* lpSection, const void* lpProcStart) const {
  15. auto Disassembler = m_DisassemblerEngine.CreateDisassembler();
  16. auto lpMachineCode = lpProcStart;
  17. auto cbMachineCode = lpSection->size - ARL::AddressDelta(lpMachineCode, m_Image.ImageSectionView(lpSection));
  18. auto Address = lpSection->addr + ARL::AddressDelta(lpMachineCode, m_Image.ImageSectionView(lpSection));
  19. Disassembler.SetContext({ lpMachineCode, cbMachineCode, Address });
  20. while (Disassembler.Next()) {
  21. auto lpInsn = Disassembler.GetInstruction();
  22. if (Instructions.find(lpInsn->address) == Instructions.end()) {
  23. Instructions.emplace(lpInsn->address, lpInsn->size);
  24. if (lpInsn->mnemonic[0] == 'J' || lpInsn->mnemonic[0] == 'j') {
  25. if (lpInsn->detail->x86.operands[0].type != X86_OP_IMM) { // "jxx reg" / "jxx qword ptr [xxx]" won't be handled.
  26. return;
  27. }
  28. ScanInstructions(Instructions, lpSection, m_Image.ConvertRvaToPtr(lpInsn->detail->x86.operands[0].imm));
  29. if (strcasecmp(lpInsn->mnemonic, "jmp") == 0) {
  30. return;
  31. }
  32. } else if (strcasecmp(lpInsn->mnemonic, "ret") == 0) {
  33. return;
  34. }
  35. } else {
  36. return;
  37. }
  38. }
  39. }
  40. [[nodiscard]]
  41. const char* PatchSolution3::TryResolveStubHelper(const void* lpStubHelperProc) const {
  42. if (auto dyld_info = m_Image.SpecialLoadCommand<LC_DYLD_INFO_ONLY>(); dyld_info) {
  43. auto Disassembler = m_DisassemblerEngine.CreateDisassembler();
  44. // A stub-helper proc must look like:
  45. // push xxxxh;
  46. // jmp loc_xxxxxxxx
  47. // which should be 10 bytes long.
  48. Disassembler.SetContext({ lpStubHelperProc, 10, 0 });
  49. if (Disassembler.Next()) {
  50. auto lpInsn = Disassembler.GetInstruction();
  51. if (strcasecmp(lpInsn->mnemonic, "push") == 0 && lpInsn->detail->x86.operands[0].type == X86_OP_IMM) {
  52. auto pbBindOpcode =
  53. m_Image.ImageOffset<const uint8_t*>(dyld_info->lazy_bind_off) +
  54. lpInsn->detail->x86.operands[0].imm;
  55. while ((*pbBindOpcode & BIND_OPCODE_MASK) != BIND_OPCODE_DONE) {
  56. switch (*pbBindOpcode & BIND_OPCODE_MASK) {
  57. case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: // 0x10
  58. case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: // 0x30
  59. case BIND_OPCODE_SET_TYPE_IMM: // 0x50
  60. case BIND_OPCODE_DO_BIND: // 0x90
  61. case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: // 0xB0
  62. ++pbBindOpcode;
  63. break;
  64. case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: // 0x20
  65. case BIND_OPCODE_SET_ADDEND_SLEB: // 0x60
  66. case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: // 0x70
  67. case BIND_OPCODE_ADD_ADDR_ULEB: // 0x80
  68. case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: // 0xA0
  69. while (*(++pbBindOpcode) & 0x80u) {}
  70. ++pbBindOpcode;
  71. break;
  72. case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: // 0x40
  73. return reinterpret_cast<const char *>(pbBindOpcode + 1);
  74. case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: // 0xC0
  75. //
  76. // This opcode is too rare to appear,
  77. // It is okay to dismiss this opcode
  78. //
  79. return nullptr;
  80. default:
  81. return nullptr;
  82. }
  83. }
  84. }
  85. }
  86. }
  87. return nullptr;
  88. }
  89. [[nodiscard]]
  90. bool PatchSolution3::FindPatchOffset() noexcept {
  91. try {
  92. auto section__text = m_Image.ImageSection("__TEXT", "__text");
  93. auto section__cstring = m_Image.ImageSection("__TEXT", "__cstring");
  94. auto section__stubs = m_Image.ImageSection("__TEXT", "__stubs");
  95. auto sectionview__stubs = m_Image.ImageSectionView(section__stubs);
  96. auto Disassembler = m_DisassemblerEngine.CreateDisassembler();
  97. auto Assembler = m_AssemblerEngine.CreateAssembler();
  98. void* lpfnGenerateKeyA = nullptr;
  99. void* lpfnGenerateKeyB = nullptr;
  100. std::optional<X64ImageAddress> StdStringAppendStubRva;
  101. std::vector<uint8_t> fnNewGenerateKeyA;
  102. std::vector<uint8_t> fnNewGenerateKeyB;
  103. //
  104. // find fnGenerateKeyA
  105. //
  106. if (auto lpKeyword = m_Image.SearchSection(section__cstring, [](const void* base, size_t i, size_t size) {
  107. static const char Keyword[] = "K\xd8\x00MjZAGa6R";
  108. if (i + sizeof(Keyword) <= size) {
  109. return memcmp(ARL::AddressOffset(base, i), Keyword, sizeof(Keyword)) == 0;
  110. } else {
  111. return false;
  112. }
  113. }); lpKeyword) {
  114. if (auto lpXrefKeyword = m_Image.SearchSection(section__text, [section__text, KeywordRva = m_Image.ConvertPtrToRva(lpKeyword)](const void* base, size_t i, size_t size) {
  115. if (i + sizeof(uint32_t) <= size) {
  116. auto rip = section__text->addr + (i + 4);
  117. auto off = ARL::AddressRead<uint32_t>(base, i);
  118. return rip + off == KeywordRva;
  119. } else {
  120. return false;
  121. }
  122. }); lpXrefKeyword) {
  123. static const uint8_t FunctionHeader[] = {
  124. 0x55, // push rbp
  125. 0x48, 0x89, 0xe5 // mov rbp, rsp
  126. };
  127. for (int i = -0x90; i + static_cast<int>(sizeof(FunctionHeader)) < 0; ++i) {
  128. if (memcmp(ARL::AddressOffset(lpXrefKeyword, i), FunctionHeader, sizeof(FunctionHeader)) == 0) {
  129. lpfnGenerateKeyA = ARL::AddressOffset(lpXrefKeyword, i);
  130. break;
  131. }
  132. }
  133. if (lpfnGenerateKeyA == nullptr) {
  134. throw ARL::Exception(__FILE__, __LINE__, "fnGenerateKeyA is not found.");
  135. }
  136. } else {
  137. throw ARL::Exception(__FILE__, __LINE__, "fnGenerateKeyA is not found.");
  138. }
  139. } else {
  140. throw ARL::Exception(__FILE__, __LINE__, "fnGenerateKeyA is not found.");
  141. }
  142. //
  143. // find fnGenerateKeyB
  144. //
  145. if (auto lpKeyword = m_Image.SearchSection(section__cstring, [](const void* base, size_t i, size_t size) {
  146. static const char Keyword[] = "me30I";
  147. if (i + sizeof(Keyword) <= size) {
  148. return memcmp(ARL::AddressOffset(base, i), Keyword, sizeof(Keyword)) == 0;
  149. } else {
  150. return false;
  151. }
  152. }); lpKeyword) {
  153. if (auto lpXrefKeyword = m_Image.SearchSection(section__text, [section__text, KeywordRva = m_Image.ConvertPtrToRva(lpKeyword)](const void* base, size_t i, size_t size) {
  154. if (i + sizeof(uint32_t) <= size) {
  155. auto rip = section__text->addr + (i + 4);
  156. auto off = ARL::AddressRead<uint32_t>(base, i);
  157. return rip + off == KeywordRva;
  158. } else {
  159. return false;
  160. }
  161. }); lpXrefKeyword) {
  162. static const uint8_t FunctionHeader[] = {
  163. 0x55, // push rbp
  164. 0x48, 0x89, 0xe5 // mov rbp, rsp
  165. };
  166. for (int i = -0x9A; i + static_cast<int>(sizeof(FunctionHeader)) < 0; ++i) {
  167. if (memcmp(ARL::AddressOffset(lpXrefKeyword, i), FunctionHeader, sizeof(FunctionHeader)) == 0) {
  168. lpfnGenerateKeyB = ARL::AddressOffset(lpXrefKeyword, i);
  169. }
  170. }
  171. if (lpfnGenerateKeyB == nullptr) {
  172. throw ARL::Exception(__FILE__, __LINE__, "fnGenerateKeyB is not found.");
  173. }
  174. } else {
  175. throw ARL::Exception(__FILE__, __LINE__, "fnGenerateKeyB is not found.");
  176. }
  177. } else {
  178. throw ARL::Exception(__FILE__, __LINE__, "fnGenerateKeyB is not found.");
  179. }
  180. //
  181. // find std::string::append(const char*)
  182. //
  183. Disassembler.SetContext({ sectionview__stubs, section__stubs->size, section__stubs->addr });
  184. while (Disassembler.Next()) {
  185. auto lpInsn = Disassembler.GetInstruction();
  186. //
  187. // As far as I know, all stub functions have a pattern looking like:
  188. // jmp qword ptr [RIP + xxxx]
  189. //
  190. if (strcasecmp(lpInsn->mnemonic, "jmp") == 0 && lpInsn->detail->x86.operands[0].type == X86_OP_MEM && lpInsn->detail->x86.operands[0].mem.base == X86_REG_RIP) {
  191. try {
  192. X64ImageAddress la_symbol_ptr_rva = Disassembler.GetContext().Address + lpInsn->detail->x86.operands[0].mem.disp;
  193. X64ImageOffset la_symbol_ptr_offset = m_Image.ConvertRvaToOffset(la_symbol_ptr_rva);
  194. X64ImageAddress stub_helper_rva = ARL::AddressRead<uint64_t>(m_Image.ImageBase(), la_symbol_ptr_offset);
  195. X64ImageOffset stub_helper_offset = m_Image.ConvertRvaToOffset(stub_helper_rva);
  196. //
  197. // __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKcm
  198. // is the mangled name of "std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::append(char const*, size_t)",
  199. // which is, as known as, "std::string::append(const char*, size_t)"
  200. // You can demangle it by c++flit
  201. // e.g.
  202. // c++filt -_ '__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKcm'
  203. //
  204. auto lpszSymbolName = TryResolveStubHelper(m_Image.ImageOffset(stub_helper_offset));
  205. if (lpszSymbolName && strcmp(lpszSymbolName, "__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKcm") == 0) {
  206. StdStringAppendStubRva = Disassembler.GetInstructionContext().Address;
  207. break;
  208. }
  209. } catch (...) {
  210. continue;
  211. }
  212. }
  213. }
  214. if (StdStringAppendStubRva.has_value() == false) {
  215. throw ARL::Exception(__FILE__, __LINE__, "std::string::append(const char*) is not found.");
  216. }
  217. fnNewGenerateKeyA = Assembler.GenerateMachineCode(
  218. [&StdStringAppendStubRva]() -> std::string {
  219. const char asm_template[] =
  220. "push rbp;"
  221. "mov rbp, rsp;"
  222. "xor rax, rax;" // initialize std::string with null
  223. "mov qword ptr[rdi], rax;"
  224. "mov qword ptr[rdi + 0x8], rax;"
  225. "mov qword ptr[rdi + 0x10], rax;"
  226. "mov edx, 0x188;"
  227. "lea rsi, qword ptr [rip + KeyA];"
  228. "call 0x%.16llx;" // filled with address to std::string::append(const char*, size_t)
  229. "leave;"
  230. "ret;"
  231. "KeyA:";
  232. std::string asm_string;
  233. int l = snprintf(nullptr, 0, asm_template, StdStringAppendStubRva.value());
  234. if (l < 0) {
  235. std::terminate();
  236. }
  237. asm_string.resize(l + 1);
  238. l = snprintf(asm_string.data(), asm_string.length(), asm_template, StdStringAppendStubRva.value());
  239. if (l < 0) {
  240. std::terminate();
  241. }
  242. while (asm_string.back() == '\x00') {
  243. asm_string.pop_back();
  244. }
  245. return asm_string;
  246. }().c_str(),
  247. m_Image.ConvertPtrToRva(lpfnGenerateKeyA)
  248. );
  249. fnNewGenerateKeyB = Assembler.GenerateMachineCode(
  250. [&StdStringAppendStubRva]() -> std::string {
  251. const char asm_template[] =
  252. "push rbp;"
  253. "mov rbp, rsp;"
  254. "xor rax, rax;" // initialize std::string with null
  255. "mov qword ptr[rdi], rax;"
  256. "mov qword ptr[rdi + 0x8], rax;"
  257. "mov qword ptr[rdi + 0x10], rax;"
  258. "mov edx, 0x188;"
  259. "lea rsi, qword ptr [rip + KeyB];"
  260. "call 0x%.16llx;" // filled with address to std::string::append(const char*, size_t)
  261. "leave;"
  262. "ret;"
  263. "KeyB:";
  264. std::string asm_string;
  265. int l = snprintf(nullptr, 0, asm_template, StdStringAppendStubRva.value());
  266. if (l < 0) {
  267. std::terminate();
  268. }
  269. asm_string.resize(l + 1);
  270. l = snprintf(asm_string.data(), asm_string.length(), asm_template, StdStringAppendStubRva.value());
  271. if (l < 0) {
  272. std::terminate();
  273. }
  274. while (asm_string.back() == '\x00') {
  275. asm_string.pop_back();
  276. }
  277. return asm_string;
  278. }().c_str(),
  279. m_Image.ConvertPtrToRva(lpfnGenerateKeyB)
  280. );
  281. {
  282. std::map<X64ImageAddress, X64ImageSize> InstructionMap;
  283. ScanInstructions(InstructionMap, section__text, lpfnGenerateKeyA);
  284. // merging
  285. for (auto it = InstructionMap.begin(); it != InstructionMap.end(); ++it) {
  286. for (auto next_it = std::next(it); it->first + it->second == next_it->first; next_it = InstructionMap.erase(next_it)) {
  287. it->second += next_it->second;
  288. }
  289. }
  290. if (auto it = InstructionMap.find(m_Image.ConvertPtrToRva(lpfnGenerateKeyA)); it != InstructionMap.end()) {
  291. if (fnNewGenerateKeyA.size() + 0x188 > it->second) {
  292. throw ARL::Exception(__FILE__, __LINE__, "No enough space.");
  293. }
  294. } else {
  295. throw ARL::AssertionError(__FILE__, __LINE__, "Something unexpected happened.");
  296. }
  297. }
  298. {
  299. std::map<X64ImageAddress, X64ImageSize> InstructionMap;
  300. ScanInstructions(InstructionMap, section__text, lpfnGenerateKeyB);
  301. // merging
  302. for (auto it = InstructionMap.begin(); it != InstructionMap.end(); ++it) {
  303. for (auto next_it = std::next(it); it->first + it->second == next_it->first; next_it = InstructionMap.erase(next_it)) {
  304. it->second += next_it->second;
  305. }
  306. }
  307. if (auto it = InstructionMap.find(m_Image.ConvertPtrToRva(lpfnGenerateKeyB)); it != InstructionMap.end()) {
  308. if (fnNewGenerateKeyB.size() + 0x188 > it->second) {
  309. throw ARL::Exception(__FILE__, __LINE__, "No enough space.");
  310. }
  311. } else {
  312. throw ARL::AssertionError(__FILE__, __LINE__, "Something unexpected happened.");
  313. }
  314. }
  315. m_lpfnGenerateKeyA = lpfnGenerateKeyA;
  316. m_lpfnGenerateKeyB = lpfnGenerateKeyB;
  317. m_fnNewGenerateKeyA = std::move(fnNewGenerateKeyA);
  318. m_fnNewGenerateKeyB = std::move(fnNewGenerateKeyB);
  319. printf("[+] PatchSolution3 ...... Ready to apply.\n");
  320. printf(" fnGenerateKeyA RVA = 0x%.16llx\n", m_Image.ConvertPtrToRva(m_lpfnGenerateKeyA));
  321. printf(" fnGenerateKeyB RVA = 0x%.16llx\n", m_Image.ConvertPtrToRva(m_lpfnGenerateKeyB));
  322. printf(" std::string::append(const char*, size_t) RVA = 0x%.16llx\n", StdStringAppendStubRva.value());
  323. return true;
  324. } catch (...) {
  325. printf("[-] PatchSolution3 ...... Omitted.\n");
  326. return false;
  327. }
  328. }
  329. [[nodiscard]]
  330. bool PatchSolution3::CheckKey(const RSACipher& Cipher) const noexcept {
  331. return Cipher.Bits() == 2048;
  332. }
  333. void PatchSolution3::MakePatch(const RSACipher& Cipher) const {
  334. if (m_lpfnGenerateKeyA && m_lpfnGenerateKeyB && m_fnNewGenerateKeyA.size() && m_fnNewGenerateKeyB.size()) {
  335. //
  336. // Prepare public key string
  337. //
  338. auto szKeyA = Cipher.ExportKeyString<RSAKeyType::PublicKey, RSAKeyFormat::PEM>();
  339. for (auto pos = szKeyA.find("-----BEGIN PUBLIC KEY-----"); pos != std::string::npos; pos = szKeyA.find("-----BEGIN PUBLIC KEY-----", pos)) {
  340. szKeyA.erase(pos, strlen("-----BEGIN PUBLIC KEY-----"));
  341. }
  342. for (auto pos = szKeyA.find("-----END PUBLIC KEY-----"); pos != std::string::npos; pos = szKeyA.find("-----END PUBLIC KEY-----", pos)) {
  343. szKeyA.erase(pos, strlen("-----END PUBLIC KEY-----"));
  344. }
  345. for (auto pos = szKeyA.find('\n'); pos != std::string::npos; pos = szKeyA.find('\n', pos)) {
  346. szKeyA.erase(pos, 1);
  347. }
  348. if (szKeyA.size() < 0x188) {
  349. szKeyA.append(0x188 - szKeyA.size(), '\x00');
  350. }
  351. auto szKeyB = std::string(0x188, '\x00');
  352. puts("**************************************************************");
  353. puts("* PatchSolution3 *");
  354. puts("**************************************************************");
  355. printf("[*] Previous:\n");
  356. Misc::PrintMemory(m_lpfnGenerateKeyA, m_fnNewGenerateKeyA.size() + 0x188, m_Image.ImageBase());
  357. memcpy(m_lpfnGenerateKeyA, m_fnNewGenerateKeyA.data(), m_fnNewGenerateKeyA.size());
  358. memcpy(ARL::AddressOffset(m_lpfnGenerateKeyA, m_fnNewGenerateKeyA.size()), szKeyA.data(), 0x188);
  359. printf("[*] After:\n");
  360. Misc::PrintMemory(m_lpfnGenerateKeyA, m_fnNewGenerateKeyA.size() + 0x188, m_Image.ImageBase());
  361. printf("\n");
  362. printf("[*] Previous:\n");
  363. Misc::PrintMemory(m_lpfnGenerateKeyB, m_fnNewGenerateKeyB.size() + 0x188, m_Image.ImageBase());
  364. memcpy(m_lpfnGenerateKeyB, m_fnNewGenerateKeyB.data(), m_fnNewGenerateKeyB.size());
  365. memcpy(ARL::AddressOffset(m_lpfnGenerateKeyB, m_fnNewGenerateKeyB.size()), szKeyB.data(), 0x188);
  366. printf("[*] After:\n");
  367. Misc::PrintMemory(m_lpfnGenerateKeyB, m_fnNewGenerateKeyB.size() + 0x188, m_Image.ImageBase());
  368. printf("\n");
  369. } else {
  370. throw ARL::Exception(__FILE__, __LINE__, "PatchSolution3: not ready yet.");
  371. }
  372. }
  373. }