From 70f49762ccda53c03d682312313bdb4232594764 Mon Sep 17 00:00:00 2001 From: Double Sine Date: Sat, 28 Sep 2019 00:49:34 +0800 Subject: [PATCH] Add PatchSolution4 for Navicat Modeler 3 --- navicat-patcher/CapstoneDisassembler.cpp | 4 + navicat-patcher/CapstoneDisassembler.hpp | 2 + navicat-patcher/ExceptionKeystone.hpp | 37 +++++ navicat-patcher/KeystoneAssembler.cpp | 44 +++++ navicat-patcher/KeystoneAssembler.hpp | 39 +++++ navicat-patcher/PatchSolution4-amd64.cpp | 150 ++++++++++++++++++ navicat-patcher/PatchSolution4-generic.cpp | 72 +++++++++ navicat-patcher/PatchSolution4-i386.cpp | 145 +++++++++++++++++ navicat-patcher/PatchSolutions.hpp | 33 ++++ navicat-patcher/ResourceTraitsKeystone.hpp | 33 ++++ navicat-patcher/_tmain.cpp | 27 +++- navicat-patcher/navicat-patcher.vcxproj | 13 ++ .../navicat-patcher.vcxproj.filters | 21 +++ 13 files changed, 613 insertions(+), 7 deletions(-) create mode 100644 navicat-patcher/ExceptionKeystone.hpp create mode 100644 navicat-patcher/KeystoneAssembler.cpp create mode 100644 navicat-patcher/KeystoneAssembler.hpp create mode 100644 navicat-patcher/PatchSolution4-amd64.cpp create mode 100644 navicat-patcher/PatchSolution4-generic.cpp create mode 100644 navicat-patcher/PatchSolution4-i386.cpp create mode 100644 navicat-patcher/ResourceTraitsKeystone.hpp diff --git a/navicat-patcher/CapstoneDisassembler.cpp b/navicat-patcher/CapstoneDisassembler.cpp index 3937427..f619e4e 100644 --- a/navicat-patcher/CapstoneDisassembler.cpp +++ b/navicat-patcher/CapstoneDisassembler.cpp @@ -86,6 +86,10 @@ namespace nkg { } } + const char* CapstoneEngine::GetRegisterName(unsigned int reg_id) const noexcept { + return cs_reg_name(Get(), reg_id); + } + [[nodiscard]] CapstoneDisassembler CapstoneEngine::CreateDisassembler() const { return CapstoneDisassembler(*this); diff --git a/navicat-patcher/CapstoneDisassembler.hpp b/navicat-patcher/CapstoneDisassembler.hpp index ee9721f..ca9f0f4 100644 --- a/navicat-patcher/CapstoneDisassembler.hpp +++ b/navicat-patcher/CapstoneDisassembler.hpp @@ -51,6 +51,8 @@ namespace nkg { void Option(cs_opt_type Type, cs_opt_value Value); + const char* GetRegisterName(unsigned int reg_id) const noexcept; + [[nodiscard]] CapstoneDisassembler CreateDisassembler() const; }; diff --git a/navicat-patcher/ExceptionKeystone.hpp b/navicat-patcher/ExceptionKeystone.hpp new file mode 100644 index 0000000..25d60f6 --- /dev/null +++ b/navicat-patcher/ExceptionKeystone.hpp @@ -0,0 +1,37 @@ +#pragma once +#include +#include + +namespace nkg { + + class KeystoneError final : public Exception { + private: + + ks_err _ErrorCode; + std::xstring _ErrorString; + + public: + + KeystoneError(PCTSTR SourceFile, SIZE_T SourceLine, ks_err KeystoneErrorCode, PCTSTR CustomMessage) noexcept : + Exception(SourceFile, SourceLine, CustomMessage), + _ErrorCode(KeystoneErrorCode), + _ErrorString(std::xstring_extension{}, ks_strerror(KeystoneErrorCode), CP_UTF8) {} + + [[nodiscard]] + virtual bool HasErrorCode() const noexcept override { + return true; + } + + [[nodiscard]] + virtual ULONG_PTR ErrorCode() const noexcept override { + return _ErrorCode; + } + + [[nodiscard]] + virtual PCTSTR ErrorString() const noexcept override { + return _ErrorString.c_str(); + } + }; + +} + diff --git a/navicat-patcher/KeystoneAssembler.cpp b/navicat-patcher/KeystoneAssembler.cpp new file mode 100644 index 0000000..0f105df --- /dev/null +++ b/navicat-patcher/KeystoneAssembler.cpp @@ -0,0 +1,44 @@ +#include "KeystoneAssembler.hpp" + +#undef NKG_CURRENT_SOURCE_FILE +#undef NKG_CURRENT_SOURCE_LINE +#define NKG_CURRENT_SOURCE_FILE() TEXT(".\\navicat-patcher\\KeystoneAssembler.cpp") +#define NKG_CURRENT_SOURCE_LINE() __LINE__ + +namespace nkg { + + KeystoneAssembler::KeystoneAssembler(const KeystoneEngine& Engine) noexcept : + _Engine(Engine) {} + + [[nodiscard]] + std::vector KeystoneAssembler::GenerateMachineCode(const char* AssemblyCode, uint64_t Address) const { + ResourceOwned pbMachineCode(KeystoneMallocTraits{}); + size_t cbMachineCode = 0; + size_t InstructionsProcessed = 0; + + if (ks_asm(_Engine, AssemblyCode, Address, pbMachineCode.GetAddressOf(), &cbMachineCode, &InstructionsProcessed) != 0) { + throw KeystoneError(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ks_errno(_Engine), TEXT("ks_asm failed.")); + } + + return std::vector(pbMachineCode.Get(), pbMachineCode.Get() + cbMachineCode); + } + + KeystoneEngine::KeystoneEngine(ks_arch ArchType, ks_mode Mode) { + auto err = ks_open(ArchType, Mode, GetAddressOf()); + if (err != KS_ERR_OK) { + throw KeystoneError(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, TEXT("ks_open failed.")); + } + } + + void KeystoneEngine::Option(ks_opt_type Type, ks_opt_value Value) { + auto err = ks_option(Get(), Type, Value); + if (err != KS_ERR_OK) { + throw KeystoneError(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, TEXT("ks_option failed.")); + } + } + + KeystoneAssembler KeystoneEngine::CreateAssembler() const { + return KeystoneAssembler(*this); + } +} + diff --git a/navicat-patcher/KeystoneAssembler.hpp b/navicat-patcher/KeystoneAssembler.hpp new file mode 100644 index 0000000..6e5145b --- /dev/null +++ b/navicat-patcher/KeystoneAssembler.hpp @@ -0,0 +1,39 @@ +#pragma once +#include "ExceptionKeystone.hpp" +#include +#include "ResourceTraitsKeystone.hpp" +#include + +namespace nkg { + + class KeystoneEngine; + + class KeystoneAssembler { + friend class KeystoneEngine; + private: + + const KeystoneEngine& _Engine; + + KeystoneAssembler(const KeystoneEngine& Engine) noexcept; + + public: + + [[nodiscard]] + std::vector GenerateMachineCode(const char* AssemblyCode, uint64_t Address = 0) const; + + }; + + class KeystoneEngine : private ResourceOwned { + friend class KeystoneAssembler; + public: + + KeystoneEngine(ks_arch ArchType, ks_mode Mode); + + void Option(ks_opt_type Type, ks_opt_value Value); + + [[nodiscard]] + KeystoneAssembler CreateAssembler() const; + }; + +} + diff --git a/navicat-patcher/PatchSolution4-amd64.cpp b/navicat-patcher/PatchSolution4-amd64.cpp new file mode 100644 index 0000000..cc54298 --- /dev/null +++ b/navicat-patcher/PatchSolution4-amd64.cpp @@ -0,0 +1,150 @@ +#include "PatchSolutions.hpp" +#include + +#undef NKG_CURRENT_SOURCE_FILE +#undef NKG_CURRENT_SOURCE_LINE +#define NKG_CURRENT_SOURCE_FILE() TEXT(".\\navicat-patcher\\PatchSolution4-amd64.cpp") +#define NKG_CURRENT_SOURCE_LINE() __LINE__ + +namespace nkg { + + PatchSolution4::PatchSolution4(const ImageInterpreter& Image) : + _Image(Image), + _DisassemblyEngine(CS_ARCH_X86, CS_MODE_64), + _AssemblyEngine(KS_ARCH_X86, KS_MODE_64), + _pbPatchMachineCode(nullptr), + _pbPatchNewPublicKey(nullptr) { _DisassemblyEngine.Option(CS_OPT_DETAIL, CS_OPT_ON); } + + PatchSolution4::PatchSolution4(const ImageInterpreter* Image) : + _Image(*Image), + _DisassemblyEngine(CS_ARCH_X86, CS_MODE_64), + _AssemblyEngine(KS_ARCH_X86, KS_MODE_64), + _pbPatchMachineCode(nullptr), + _pbPatchNewPublicKey(nullptr) { _DisassemblyEngine.Option(CS_OPT_DETAIL, CS_OPT_ON); } + + bool PatchSolution4::FindPatchOffset() noexcept { + try { + _pbPatchMachineCode = _Image.SearchSection(".text", [](const uint8_t* p) { + __try { + return + p[0] == 0x48 && p[1] == 0x8d && // prefix of "lea rcx, [rbp+5Fh+var_38]" + p[4] == 0x48 && p[5] == 0x83 && // prefix of "cmp [rbp+5Fh+var_20], 10h" + p[9] == 0x48 && p[10] == 0x0f && p[11] == 0x43 && // prefix of "cmovnb rcx, [rbp+5Fh+var_38]" + p[14] == 0x48 && p[15] == 0x8d && // prefix of "lea rax, [rbp+5Fh+var_58]" + p[18] == 0x48 && p[19] == 0x83 && // prefix of "cmp [rbp+5Fh+var_40], 10h" + p[23] == 0x48 && p[24] == 0x0f && p[25] == 0x43 && // prefix of "cmovnb rax, [rbp+5Fh+var_58]" + p[28] == 0x44 && p[29] == 0x0f && p[30] == 0xb6 && // prefix of "movzx r8d, byte ptr [rax+rdi]" + p[33] == 0x44 && p[34] == 0x02 && // prefix of "add r8b, [rcx+rdi]" + p[37] == 0xba && // prefix of "mov edx, 1" + p[42] == 0x48 && p[43] == 0x8b && // prefix of "mov rcx, rbx" + p[45] == 0xe8; // prefix of "call sub_1806E65F0" + } __except (EXCEPTION_EXECUTE_HANDLER) { + return false; + } + }); + + auto RegisterIndex = X86_REG_INVALID; + auto Disassembler = _DisassemblyEngine.CreateDisassembler(); + Disassembler.SetContext({ _pbPatchMachineCode, 45, _Image.PointerToVa(_pbPatchMachineCode) }); + + if (Disassembler.Next() && Disassembler.Next() && Disassembler.Next() && Disassembler.Next() && Disassembler.Next() && Disassembler.Next() && Disassembler.Next()) { + auto lpInsn = Disassembler.GetInstruction(); + if (_stricmp(lpInsn->mnemonic, "movzx") == 0 && + lpInsn->detail->x86.op_count == 2 && + lpInsn->detail->x86.operands[0].type == X86_OP_REG && + lpInsn->detail->x86.operands[1].type == X86_OP_MEM && + lpInsn->detail->x86.operands[1].size == 1 && + lpInsn->detail->x86.operands[1].mem.scale == 1) + { + RegisterIndex = lpInsn->detail->x86.operands[1].mem.index; + } else { + throw nkg::Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Unexpected machine code.")); + } + } else { + throw nkg::Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Disassemble failed.")); + } + + if (Disassembler.Next() && Disassembler.Next() && Disassembler.Next()) { + auto lpInsn = Disassembler.GetInstruction(); + + // + // The previous instruction of "call sub_1806E65F0" should set RCX register. + // + if (_stricmp(lpInsn->mnemonic, "mov") != 0 || + lpInsn->detail->x86.op_count < 1 || + lpInsn->detail->x86.operands[0].type != X86_OP_REG || + lpInsn->detail->x86.operands[0].reg != X86_REG_RCX) + { + throw nkg::Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Unexpected machine code.")); + } + } else { + throw nkg::Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Disassemble failed.")); + } + + for (size_t i = 0; i < _Image.NumberOfSections(); ++i) { + auto lpSectionHeader = _Image.ImageSectionHeader(i); + if (lpSectionHeader->SizeOfRawData > lpSectionHeader->Misc.VirtualSize) { + auto cbReserved = + lpSectionHeader->SizeOfRawData - + lpSectionHeader->Misc.VirtualSize; + + if (cbReserved >= 0x188) { + _pbPatchNewPublicKey = _Image.ImageSectionView(lpSectionHeader, lpSectionHeader->Misc.VirtualSize); + + auto Assembler = _AssemblyEngine.CreateAssembler(); + _NewMachineCode = Assembler.GenerateMachineCode( + std::xstring::format( + TEXT( + "lea rax, qword ptr[0x%.16llx];" + "mov r8b, byte ptr[rax + %hs];" + "mov edx, 1;" + ), + _Image.PointerToVa(_pbPatchNewPublicKey), + _DisassemblyEngine.GetRegisterName(RegisterIndex) + ).explicit_string().c_str(), + _Image.PointerToVa(_pbPatchMachineCode) + ); + + // >>>>>>>>>>>> .text:00000001819B02C0 48 8D 4D 27 lea rcx, [rbp + 5Fh + var_38] + // .text:00000001819B02C4 48 83 7D 3F 10 cmp[rbp + 5Fh + var_20], 10h + // 42 BYTES .text:00000001819B02C9 48 0F 43 4D 27 cmovnb rcx, [rbp + 5Fh + var_38] + // .text:00000001819B02CE 48 8D 45 07 lea rax, [rbp + 5Fh + var_58] + // THESE CODE .text:00000001819B02D2 48 83 7D 1F 10 cmp[rbp + 5Fh + var_40], 10h + // WILL BE .text:00000001819B02D7 48 0F 43 45 07 cmovnb rax, [rbp + 5Fh + var_58] + // REPLACED .text:00000001819B02DC 44 0F B6 04 38 movzx r8d, byte ptr[rax + rdi] + // .text:00000001819B02E1 44 02 04 39 add r8b, [rcx + rdi] + // <<<<<<<<<<<< .text:00000001819B02E5 BA 01 00 00 00 mov edx, 1 + // .text:00000001819B02EA 48 8B CB mov rcx, rbx + // .text:00000001819B02ED E8 FE 62 D3 FE call sub_1806E65F0 + while (_NewMachineCode.size() < 42) { + _NewMachineCode.emplace_back(0x90); // padding with "nop" + } + + if (_NewMachineCode.size() != 42) { + throw nkg::Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Something unexpected happens.")); + } + + LOG_SUCCESS(0, "PatchSolution4 ...... Ready to apply"); + LOG_HINT(4, "Machine code patch VA = 0x%zx", _Image.PointerToVa(_pbPatchMachineCode)); + LOG_HINT(4, "New public key VA = 0x%zx", _Image.PointerToVa(_pbPatchNewPublicKey)); + LOG_HINT(4, "New public key offset = 0x%zx", _Image.PointerToFileOffset(_pbPatchNewPublicKey)); + + return true; + } + } + } + + throw nkg::Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("No space to store public key.")); + } catch (nkg::Exception&) { + _pbPatchMachineCode = nullptr; + _pbPatchNewPublicKey = nullptr; + _NewMachineCode.clear(); + + LOG_FAILURE(0, "PatchSolution4 ...... Omitted"); + + return false; + } + } + +} + diff --git a/navicat-patcher/PatchSolution4-generic.cpp b/navicat-patcher/PatchSolution4-generic.cpp new file mode 100644 index 0000000..d57584a --- /dev/null +++ b/navicat-patcher/PatchSolution4-generic.cpp @@ -0,0 +1,72 @@ +#include "PatchSolutions.hpp" + +#undef NKG_CURRENT_SOURCE_FILE +#undef NKG_CURRENT_SOURCE_LINE +#define NKG_CURRENT_SOURCE_FILE() TEXT(".\\navicat-patcher\\PatchSolution4-generic.cpp") +#define NKG_CURRENT_SOURCE_LINE() __LINE__ + +namespace nkg { + + bool PatchSolution4::CheckKey(const RSACipher& Cipher) const noexcept { + auto szPublicKey = Cipher.ExportKeyString(); + + for (auto pos = szPublicKey.find("-----BEGIN PUBLIC KEY-----"); pos != std::string::npos; pos = szPublicKey.find("-----BEGIN PUBLIC KEY-----", pos)) { + szPublicKey.erase(pos, literal_length("-----BEGIN PUBLIC KEY-----")); + } + + for (auto pos = szPublicKey.find("-----END PUBLIC KEY-----"); pos != std::string::npos; pos = szPublicKey.find("-----END PUBLIC KEY-----", pos)) { + szPublicKey.erase(pos, literal_length("-----END PUBLIC KEY-----")); + } + + for (auto pos = szPublicKey.find("\n"); pos != std::string::npos; pos = szPublicKey.find("\n", pos)) { + szPublicKey.erase(pos, literal_length("\n")); + } + + return szPublicKey.length() == 0x188; + } + + void PatchSolution4::MakePatch(const RSACipher& Cipher) const { + if (_pbPatchMachineCode == nullptr || _pbPatchNewPublicKey == nullptr || _NewMachineCode.empty()) { + throw Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("PatchSolution4 has not been ready yet.")); + } + + auto szPublicKey = Cipher.ExportKeyString(); + + for (auto pos = szPublicKey.find("-----BEGIN PUBLIC KEY-----"); pos != std::string::npos; pos = szPublicKey.find("-----BEGIN PUBLIC KEY-----", pos)) { + szPublicKey.erase(pos, literal_length("-----BEGIN PUBLIC KEY-----")); + } + + for (auto pos = szPublicKey.find("-----END PUBLIC KEY-----"); pos != std::string::npos; pos = szPublicKey.find("-----END PUBLIC KEY-----", pos)) { + szPublicKey.erase(pos, literal_length("-----END PUBLIC KEY-----")); + } + + for (auto pos = szPublicKey.find("\n"); pos != std::string::npos; pos = szPublicKey.find("\n", pos)) { + szPublicKey.erase(pos, literal_length("\n")); + } + + _putts(TEXT("*******************************************************")); + _putts(TEXT("* PatchSolution4 *")); + _putts(TEXT("*******************************************************")); + + LOG_HINT(0, "Previous:"); + PrintMemory(_pbPatchMachineCode, _NewMachineCode.size(), _Image.ImageBase()); + + memcpy(_pbPatchMachineCode, _NewMachineCode.data(), _NewMachineCode.size()); + + LOG_HINT(0, "After:"); + PrintMemory(_pbPatchMachineCode, _NewMachineCode.size(), _Image.ImageBase()); + + _putts(TEXT("")); + + LOG_HINT(0, "Previous:"); + PrintMemory(_pbPatchNewPublicKey, szPublicKey.size(), _Image.ImageBase()); + + memcpy(_pbPatchNewPublicKey, szPublicKey.data(), szPublicKey.size()); + + LOG_HINT(0, "After:"); + PrintMemory(_pbPatchNewPublicKey, szPublicKey.size(), _Image.ImageBase()); + + _putts(TEXT("")); + } +} + diff --git a/navicat-patcher/PatchSolution4-i386.cpp b/navicat-patcher/PatchSolution4-i386.cpp new file mode 100644 index 0000000..b2634a2 --- /dev/null +++ b/navicat-patcher/PatchSolution4-i386.cpp @@ -0,0 +1,145 @@ +#include "PatchSolutions.hpp" +#include + +#undef NKG_CURRENT_SOURCE_FILE +#undef NKG_CURRENT_SOURCE_LINE +#define NKG_CURRENT_SOURCE_FILE() TEXT(".\\navicat-patcher\\PatchSolution4-i386.cpp") +#define NKG_CURRENT_SOURCE_LINE() __LINE__ + +namespace nkg { + + PatchSolution4::PatchSolution4(const ImageInterpreter& Image) : + _Image(Image), + _DisassemblyEngine(CS_ARCH_X86, CS_MODE_32), + _AssemblyEngine(KS_ARCH_X86, KS_MODE_32), + _pbPatchMachineCode(nullptr), + _pbPatchNewPublicKey(nullptr) { _DisassemblyEngine.Option(CS_OPT_DETAIL, CS_OPT_ON); } + + PatchSolution4::PatchSolution4(const ImageInterpreter* Image) : + _Image(*Image), + _DisassemblyEngine(CS_ARCH_X86, CS_MODE_32), + _AssemblyEngine(KS_ARCH_X86, KS_MODE_32), + _pbPatchMachineCode(nullptr), + _pbPatchNewPublicKey(nullptr) { _DisassemblyEngine.Option(CS_OPT_DETAIL, CS_OPT_ON); } + + bool PatchSolution4::FindPatchOffset() noexcept { + try { + _pbPatchMachineCode = _Image.SearchSection(".text", [](const uint8_t* p) { + __try { + return + p[0] == 0x83 && // prefix of "cmp [ebp+var_30], 10h" + p[4] == 0x8d && // prefix of "lea ecx, [ebp+Dst]" + p[7] == 0x8d && // prefix of "lea eax, [ebp+Memory]" + p[10] == 0x0f && p[11] == 0x43 && // prefix of "cmovnb ecx, [ebp+Dst]" + p[14] == 0x83 && // prefix of "cmp [ebp+var_18], 10h" + p[18] == 0x0f && p[19] == 0x43 && // prefix of "cmovnb eax, [ebp+Memory]" + p[22] == 0x8a && // prefix of "mov dl, [eax+ebx]" + p[25] == 0x02; // prefix of "add dl, [ecx+ebx]" + // p[28] + } __except (EXCEPTION_EXECUTE_HANDLER) { + return false; + } + }); + + auto RegisterIndex = X86_REG_INVALID; + auto RegisterFinalValue = X86_REG_INVALID; + auto Disassembler = _DisassemblyEngine.CreateDisassembler(); + Disassembler.SetContext({ _pbPatchMachineCode, 28, _Image.PointerToVa(_pbPatchMachineCode) }); + + if (Disassembler.Next() && Disassembler.Next() && Disassembler.Next() && Disassembler.Next() && Disassembler.Next() && Disassembler.Next() && Disassembler.Next()) { + auto lpInsn = Disassembler.GetInstruction(); + if (_stricmp(lpInsn->mnemonic, "mov") == 0 && + lpInsn->detail->x86.op_count == 2 && + lpInsn->detail->x86.operands[0].type == X86_OP_REG && + lpInsn->detail->x86.operands[1].type == X86_OP_MEM && + lpInsn->detail->x86.operands[1].size == 1 && + lpInsn->detail->x86.operands[1].mem.scale == 1) + { + RegisterIndex = lpInsn->detail->x86.operands[1].mem.index; + } else { + throw nkg::Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Unexpected machine code.")); + } + } else { + throw nkg::Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Disassemble failed.")); + } + + if (Disassembler.Next()) { + auto lpInsn = Disassembler.GetInstruction(); + if (_stricmp(lpInsn->mnemonic, "add") == 0 && + lpInsn->detail->x86.op_count >= 1 && + lpInsn->detail->x86.operands[0].type == X86_OP_REG && + lpInsn->detail->x86.operands[0].size == 1) + { + RegisterFinalValue = lpInsn->detail->x86.operands[0].reg; + } else { + throw nkg::Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Unexpected machine code.")); + } + } else { + throw nkg::Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Disassemble failed.")); + } + + for (size_t i = 0; i < _Image.NumberOfSections(); ++i) { + auto lpSectionHeader = _Image.ImageSectionHeader(i); + if (lpSectionHeader->SizeOfRawData > lpSectionHeader->Misc.VirtualSize) { + auto cbReserved = + lpSectionHeader->SizeOfRawData - + lpSectionHeader->Misc.VirtualSize; + + if (cbReserved >= 0x188) { + _pbPatchNewPublicKey = _Image.ImageSectionView(lpSectionHeader, lpSectionHeader->Misc.VirtualSize); + + auto Assembler = _AssemblyEngine.CreateAssembler(); + _NewMachineCode = Assembler.GenerateMachineCode( + std::xstring::format( + TEXT( + "call +5;" + "pop eax;" + "add eax, 0x%.8x;" + "mov %hs, byte ptr [eax + %hs];" + ), + _Image.PointerToVa(_pbPatchNewPublicKey) - (_Image.PointerToVa(_pbPatchMachineCode) + 5), + _DisassemblyEngine.GetRegisterName(RegisterFinalValue), + _DisassemblyEngine.GetRegisterName(RegisterIndex) + ).explicit_string().c_str() + ); + + // >>>>>>>>>>>> .text:113FE4A0 83 7D D0 10 cmp [ebp+var_30], 10h + // 28 BYTES .text:113FE4A4 8D 4D BC lea ecx, [ebp+Dst] + // .text:113FE4A7 8D 45 D4 lea eax, [ebp+Memory] + // THESE CODE .text:113FE4AA 0F 43 4D BC cmovnb ecx, [ebp+Dst] + // WILL BE .text:113FE4AE 83 7D E8 10 cmp [ebp+var_18], 10h + // REPLACED .text:113FE4B2 0F 43 45 D4 cmovnb eax, [ebp+Memory] + // .text:113FE4B6 8A 14 18 mov dl, [eax+ebx] + // <<<<<<<<<<<< .text:113FE4B9 02 14 19 add dl, [ecx+ebx] + while (_NewMachineCode.size() < 28) { + _NewMachineCode.emplace_back(0x90); // padding with "nop" + } + + if (_NewMachineCode.size() != 28) { + throw nkg::Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("Something unexpected happens.")); + } + + LOG_SUCCESS(0, "PatchSolution4 ...... Ready to apply"); + LOG_HINT(4, "Machine code patch VA = 0x%zx", _Image.PointerToVa(_pbPatchMachineCode)); + LOG_HINT(4, "New public key VA = 0x%zx", _Image.PointerToVa(_pbPatchNewPublicKey)); + LOG_HINT(4, "New public key offset = 0x%zx", _Image.PointerToFileOffset(_pbPatchNewPublicKey)); + + return true; + } + } + } + + throw nkg::Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("No space to store public key.")); + } catch (nkg::Exception&) { + _pbPatchMachineCode = nullptr; + _pbPatchNewPublicKey = nullptr; + _NewMachineCode.clear(); + + LOG_FAILURE(0, "PatchSolution4 ...... Omitted"); + + return false; + } + } + +} + diff --git a/navicat-patcher/PatchSolutions.hpp b/navicat-patcher/PatchSolutions.hpp index d58cf64..f1f1e27 100644 --- a/navicat-patcher/PatchSolutions.hpp +++ b/navicat-patcher/PatchSolutions.hpp @@ -2,6 +2,7 @@ #include #include "ImageInterpreter.hpp" #include "CapstoneDisassembler.hpp" +#include "KeystoneAssembler.hpp" #include "Misc.hpp" namespace nkg { @@ -198,4 +199,36 @@ namespace nkg { virtual void MakePatch(const RSACipher& Cipher) const override; }; + + // + // PatchSolution3 will replace the RSA public key stored in libcc.dll + // For Navicat Data Modeler 3 + // + class PatchSolution4 : public PatchSolution { + private: + + static const uint8_t KeywordA[0x188]; + static const uint8_t KeywordB[0x188]; + + const ImageInterpreter& _Image; + CapstoneEngine _DisassemblyEngine; + KeystoneEngine _AssemblyEngine; + uint8_t* _pbPatchMachineCode; + uint8_t* _pbPatchNewPublicKey; + std::vector _NewMachineCode; + + public: + + PatchSolution4(const ImageInterpreter& Image); + + PatchSolution4(const ImageInterpreter* lpImage); + + [[nodiscard]] + virtual bool FindPatchOffset() noexcept override; + + [[nodiscard]] + virtual bool CheckKey(const RSACipher& Cipher) const noexcept override; + + virtual void MakePatch(const RSACipher& Cipher) const override; + }; } diff --git a/navicat-patcher/ResourceTraitsKeystone.hpp b/navicat-patcher/ResourceTraitsKeystone.hpp new file mode 100644 index 0000000..892fef8 --- /dev/null +++ b/navicat-patcher/ResourceTraitsKeystone.hpp @@ -0,0 +1,33 @@ +#pragma once +#include + +struct KeystoneHandleTraits { + using HandleType = ks_engine*; + + static inline const HandleType InvalidValue = nullptr; + + [[nodiscard]] + static bool IsValid(const HandleType& Handle) noexcept { + return Handle != InvalidValue; + } + + static void Releasor(const HandleType& Handle) noexcept { + ks_close(Handle); + } +}; + +struct KeystoneMallocTraits { + using HandleType = uint8_t*; + + static inline const HandleType InvalidValue = nullptr; + + [[nodiscard]] + static bool IsValid(const HandleType& Handle) noexcept { + return Handle != InvalidValue; + } + + static void Releasor(const HandleType& Handle) noexcept { + ks_free(Handle); + } +}; + diff --git a/navicat-patcher/_tmain.cpp b/navicat-patcher/_tmain.cpp index 495664c..d0c49b2 100644 --- a/navicat-patcher/_tmain.cpp +++ b/navicat-patcher/_tmain.cpp @@ -131,7 +131,8 @@ static void LoadKey( nkg::PatchSolution* pSolution0, nkg::PatchSolution* pSolution1, nkg::PatchSolution* pSolution2, - nkg::PatchSolution* pSolution3) + nkg::PatchSolution* pSolution3, + nkg::PatchSolution* pSolution4) { if (KeyFilePath.empty() == false) { LOG_HINT(0, "Import RSA-2048 key from %s", KeyFilePath.c_str()); @@ -141,7 +142,8 @@ static void LoadKey( if (pSolution0 && !pSolution0->CheckKey(Cipher) || pSolution1 && !pSolution1->CheckKey(Cipher) || pSolution2 && !pSolution2->CheckKey(Cipher) || - pSolution3 && !pSolution3->CheckKey(Cipher)) + pSolution3 && !pSolution3->CheckKey(Cipher) || + pSolution4 && !pSolution4->CheckKey(Cipher)) { throw nkg::Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("The RSA private key you provide cannot be used.")); } @@ -153,7 +155,8 @@ static void LoadKey( } while (pSolution0 && !pSolution0->CheckKey(Cipher) || pSolution1 && !pSolution1->CheckKey(Cipher) || pSolution2 && !pSolution2->CheckKey(Cipher) || - pSolution3 && !pSolution3->CheckKey(Cipher)); // re-generate RSA key if one of 'CheckKey's return false + pSolution3 && !pSolution3->CheckKey(Cipher) || + pSolution4 && !pSolution4->CheckKey(Cipher)); // re-generate RSA key if one of 'CheckKey's return false } LOG_HINT(0, "Your RSA public key:\n%hs", Cipher.ExportKeyString().c_str()); @@ -207,6 +210,7 @@ int _tmain(int argc, PTSTR argv[]) { ResourceOwned lpSolution1(CppObjectTraits{}); ResourceOwned lpSolution2(CppObjectTraits{}); ResourceOwned lpSolution3(CppObjectTraits{}); + ResourceOwned lpSolution4(CppObjectTraits{}); // // Open main application @@ -316,6 +320,7 @@ int _tmain(int argc, PTSTR argv[]) { lpSolution1.TakeOver(new nkg::PatchSolution1(lpLibccDllInterpreter)); lpSolution2.TakeOver(new nkg::PatchSolution2(lpLibccDllInterpreter)); lpSolution3.TakeOver(new nkg::PatchSolution3(lpLibccDllInterpreter)); + lpSolution4.TakeOver(new nkg::PatchSolution4(lpLibccDllInterpreter)); } // @@ -338,6 +343,10 @@ int _tmain(int argc, PTSTR argv[]) { lpSolution3.Release(); } + if (lpSolution4.IsValid() && lpSolution4->FindPatchOffset() == false) { + lpSolution4.Release(); + } + _putts(TEXT("")); // @@ -345,7 +354,7 @@ int _tmain(int argc, PTSTR argv[]) { // SelectPatchSolutions(lpSolution0, lpSolution1, lpSolution2, lpSolution3); - if (lpSolution0.IsValid() == false && lpSolution1.IsValid() == false && lpSolution2.IsValid() == false && lpSolution3.IsValid() == false) { + if (lpSolution0.IsValid() == false && lpSolution1.IsValid() == false && lpSolution2.IsValid() == false && lpSolution3.IsValid() == false && lpSolution4.IsValid() == false) { throw nkg::Exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), TEXT("No patch applied. Patch abort!")) .AddHint(TEXT("Are you sure your Navicat has not been patched/modified before?")); } @@ -359,14 +368,14 @@ int _tmain(int argc, PTSTR argv[]) { NavicatBackupDetect(MainExePath); } - if (lpSolution1.IsValid() || lpSolution2.IsValid() || lpSolution3.IsValid()) { + if (lpSolution1.IsValid() || lpSolution2.IsValid() || lpSolution3.IsValid() || lpSolution4.IsValid()) { NavicatBackupDetect(LibccDllPath); } // // Loading key // - LoadKey(Cipher, RsaPrivateKeyPath, lpSolution0, lpSolution1, lpSolution2, lpSolution3); + LoadKey(Cipher, RsaPrivateKeyPath, lpSolution0, lpSolution1, lpSolution2, lpSolution3, lpSolution4); if (bDryRun == false) { // @@ -383,7 +392,7 @@ int _tmain(int argc, PTSTR argv[]) { NavicatBackupMake(MainExePath); } - if (lpSolution1.IsValid() || lpSolution2.IsValid() || lpSolution3.IsValid()) { + if (lpSolution1.IsValid() || lpSolution2.IsValid() || lpSolution3.IsValid() || lpSolution4.IsValid()) { NavicatBackupMake(LibccDllPath); } @@ -405,6 +414,10 @@ int _tmain(int argc, PTSTR argv[]) { if (lpSolution3.IsValid()) { lpSolution3->MakePatch(Cipher); } + + if (lpSolution4.IsValid()) { + lpSolution4->MakePatch(Cipher); + } if (RsaPrivateKeyPath.empty()) { LOG_HINT( diff --git a/navicat-patcher/navicat-patcher.vcxproj b/navicat-patcher/navicat-patcher.vcxproj index 03e555f..a266297 100644 --- a/navicat-patcher/navicat-patcher.vcxproj +++ b/navicat-patcher/navicat-patcher.vcxproj @@ -168,6 +168,7 @@ + @@ -189,16 +190,28 @@ true true + + true + true + + + + true + true + + + + diff --git a/navicat-patcher/navicat-patcher.vcxproj.filters b/navicat-patcher/navicat-patcher.vcxproj.filters index edd6146..5d48e2f 100644 --- a/navicat-patcher/navicat-patcher.vcxproj.filters +++ b/navicat-patcher/navicat-patcher.vcxproj.filters @@ -51,6 +51,18 @@ 源文件 + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + @@ -74,5 +86,14 @@ 头文件 + + 头文件 + + + 头文件 + + + 头文件 + \ No newline at end of file