From 5ffe7a198edc3ed07bda149b2e66d511abca75f5 Mon Sep 17 00:00:00 2001 From: Double Sine Date: Thu, 13 Dec 2018 18:41:23 +0800 Subject: [PATCH] Add support for Navicat Premium 12.1.11 x64 --- navicat-patcher/Solution2.cpp | 146 ++++++++++++++++++ navicat-patcher/_tmain.cpp | 50 ++++-- navicat-patcher/def.hpp | 75 +++++++++ navicat-patcher/navicat-patcher.vcxproj | 1 + .../navicat-patcher.vcxproj.filters | 3 + 5 files changed, 259 insertions(+), 16 deletions(-) create mode 100644 navicat-patcher/Solution2.cpp diff --git a/navicat-patcher/Solution2.cpp b/navicat-patcher/Solution2.cpp new file mode 100644 index 0000000..5465c2a --- /dev/null +++ b/navicat-patcher/Solution2.cpp @@ -0,0 +1,146 @@ +#include "def.hpp" + +namespace Patcher { + + const char Solution2::KeywordsMeta[KeywordsCount + 1] = + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I" + "qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv" + "a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF" + "R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2" + "WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt" + "YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ" + "awIDAQAB"; + + uint8_t Solution2::Keywords[KeywordsCount][5]; + + void Solution2::BuildKeywords() noexcept { + for (size_t i = 0; i < KeywordsCount; ++i) { + Keywords[i][0] = 0x83; // Keywords[i] = asm('xor eax, KeywordsMeta[i]') + + Keywords[i][1] = 0xf0; + Keywords[i][2] = KeywordsMeta[i]; + Keywords[i][3] = 0x88; // asm_prefix('mov byte ptr ds:xxxxxxxxxxxxxxxx, al') + Keywords[i][4] = 0x05; + } + } + + bool Solution2::FindPatchOffset() noexcept { + PIMAGE_SECTION_HEADER textSection = nullptr; + uint8_t* pTargetFileView = pTargetFile->GetView(); + uint8_t* ptextSectionData = nullptr; + off_t Offsets[KeywordsCount]; + memset(Offsets, -1, sizeof(Offsets)); + + textSection = Helper::ImageSectionHeader(pTargetFileView, ".text"); + if (textSection == nullptr) + return false; + ptextSectionData = pTargetFileView + textSection->PointerToRawData; + + BuildKeywords(); + + // Find offsets + { + size_t FirstKeywordCounter = 0; + uint32_t Hints[9]; + DWORD PossibleRangeStart = 0xffffffff; + DWORD PossibleRangeEnd; + for (DWORD i = 0; i < textSection->SizeOfRawData; ++i) { + if (memcmp(ptextSectionData + i, Keywords[0], 5) == 0) { + Hints[FirstKeywordCounter++] = + *reinterpret_cast(ptextSectionData + i + sizeof(Keywords[0])) + + i + sizeof(Keywords[0]) + sizeof(uint32_t); + if (i < PossibleRangeStart) + PossibleRangeStart = i; + } + } + + PossibleRangeStart -= 0x1000; + PossibleRangeEnd = PossibleRangeStart + 0x100000; + + // Keywords[0] should occur 9 times. + // Because there's only 9 'M' chars in `KeywordsMeta`. + if (FirstKeywordCounter != 9) + return false; + + Helper::QuickSort(Hints, 0, _countof(Hints)); + + // assert + // if not satisfied, refuse to patch + if (Hints[8] - Hints[0] != 0x18360F8F8 - 0x18360F7D0) + return false; + + for (size_t i = 0; i < KeywordsCount; ++i) { + if (Offsets[i] != -1) + continue; + + for (DWORD j = PossibleRangeStart; j < PossibleRangeEnd; ++j) { + if (memcmp(ptextSectionData + j, Keywords[i], sizeof(Keywords[i])) == 0) { + off_t index = + *reinterpret_cast(ptextSectionData + j + sizeof(Keywords[i])) + + j + sizeof(Keywords[i]) + sizeof(uint32_t) - Hints[0]; + + if (0 <= index && index < KeywordsCount && KeywordsMeta[index] == KeywordsMeta[i]) { + Offsets[index] = textSection->PointerToRawData + j; + } + } + } + + // if not found, refuse to patch + if (Offsets[i] == -1) + return false; + } + } + + static_assert(sizeof(PatchOffsets) == sizeof(Offsets), "static_assert failure!"); + memcpy(PatchOffsets, Offsets, sizeof(PatchOffsets)); + + for (size_t i = 0; i < KeywordsCount; ++i) + _tprintf_s(TEXT("MESSAGE: [Solution2] Keywords[%zu] has been found: offset = +0x%08lx.\n"), + i, PatchOffsets[i]); + + return true; + } + + bool Solution2::MakePatch(RSACipher* cipher) const { + std::string RSAPublicKeyPEM; + uint8_t* pTargetFileView = pTargetFile->GetView(); + + RSAPublicKeyPEM = cipher->ExportKeyString(); + if (RSAPublicKeyPEM.empty()) { + REPORT_ERROR("ERROR: cipher->ExportKeyString failed."); + return false; + } + + RSAPublicKeyPEM.erase(RSAPublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"), 26); + RSAPublicKeyPEM.erase(RSAPublicKeyPEM.find("-----END PUBLIC KEY-----"), 24); + { + std::string::size_type pos = 0; + while ((pos = RSAPublicKeyPEM.find("\n", pos)) != std::string::npos) { + RSAPublicKeyPEM.erase(pos, 1); + } + } + + if (RSAPublicKeyPEM.length() != KeywordsCount) { + REPORT_ERROR("ERROR: Public key length does not match."); + return false; + } + + PRINT_MESSAGE("//"); + PRINT_MESSAGE("// Begin Solution2"); + PRINT_MESSAGE("//"); + for (size_t i = 0; i < KeywordsCount; ++i) { + _tprintf_s(TEXT("@+0x%08X: %02X %02X %02X --> "), + PatchOffsets[i], + pTargetFileView[PatchOffsets[i]], + pTargetFileView[PatchOffsets[i] + 1], + pTargetFileView[PatchOffsets[i] + 2]); + pTargetFileView[PatchOffsets[i] + 2] = RSAPublicKeyPEM[i]; + _tprintf_s(TEXT("%02X %02X %02X\n"), + pTargetFileView[PatchOffsets[i]], + pTargetFileView[PatchOffsets[i] + 1], + pTargetFileView[PatchOffsets[i] + 2]); + } + + PRINT_MESSAGE(""); + return true; + } +} diff --git a/navicat-patcher/_tmain.cpp b/navicat-patcher/_tmain.cpp index 744f5d5..ff67010 100644 --- a/navicat-patcher/_tmain.cpp +++ b/navicat-patcher/_tmain.cpp @@ -1,5 +1,7 @@ #include "def.hpp" +#define SAFE_DELETE(x) { delete x; x = nullptr; } + static void help() { PRINT_MESSAGE("Usage:"); PRINT_MESSAGE(" navicat-patcher.exe [RSA-2048 PEM file]"); @@ -42,7 +44,8 @@ static DWORD BackupFile(std::Tstring& from, std::Tstring& to) { static BOOL LoadKey(RSACipher* cipher, LPTSTR filename, Patcher::Solution* pSolution0, - Patcher::Solution* pSolution1) { + Patcher::Solution* pSolution1, + Patcher::Solution* pSolution2) { if (filename) { std::string PrivateKeyFileName; @@ -57,7 +60,8 @@ static BOOL LoadKey(RSACipher* cipher, LPTSTR filename, } if (pSolution0 && !pSolution0->CheckKey(cipher) || - pSolution1 && !pSolution1->CheckKey(cipher)) { + pSolution1 && !pSolution1->CheckKey(cipher) || + pSolution2 && !pSolution2->CheckKey(cipher)) { REPORT_ERROR("ERROR: The RSA private key you provide cannot be used."); return FALSE; } @@ -68,7 +72,8 @@ static BOOL LoadKey(RSACipher* cipher, LPTSTR filename, do { cipher->GenerateKey(2048); } while (pSolution0 && !pSolution0->CheckKey(cipher) || - pSolution1 && !pSolution1->CheckKey(cipher)); // re-generate RSA key if one of CheckKey return FALSE + pSolution1 && !pSolution1->CheckKey(cipher) || + pSolution2 && !pSolution2->CheckKey(cipher)); // re-generate RSA key if one of CheckKey return false if (!cipher->ExportKeyToFile("RegPrivateKey.pem")) { REPORT_ERROR("ERROR: Failed to save RSA private key."); @@ -100,6 +105,7 @@ int _tmain(int argc, TCHAR* argv[]) { FileMapper* pLibcc = nullptr; Patcher::Solution* pSolution0 = nullptr; Patcher::Solution* pSolution1 = nullptr; + Patcher::Solution* pSolution2 = nullptr; DWORD ErrorCode; @@ -113,6 +119,7 @@ int _tmain(int argc, TCHAR* argv[]) { pLibcc = new FileMapper(); pSolution0 = new Patcher::Solution0(); pSolution1 = new Patcher::Solution1(); + pSolution2 = new Patcher::Solution2(); if (!SetPath(argv[1])) { PRINT_MESSAGE("The path you specified:"); @@ -180,10 +187,9 @@ FindLibcc: PRINT_MESSAGE("MESSAGE: libcc.dll has been found."); } else if (ErrorCode == ERROR_FILE_NOT_FOUND) { PRINT_MESSAGE("MESSAGE: libcc.dll is not found. Solution1 and Solution2 will be omitted."); - delete pSolution1; - pSolution1 = nullptr; - delete pLibcc; - pLibcc = nullptr; + SAFE_DELETE(pSolution2); + SAFE_DELETE(pSolution1); + SAFE_DELETE(pLibcc); } else if (ErrorCode == ERROR_ACCESS_DENIED) { PRINT_MESSAGE("ERROR: Cannot open libcc.dll for ERROR_ACCESS_DENIED."); PRINT_MESSAGE("Please re-run with Administrator privilege."); @@ -196,6 +202,7 @@ FindLibcc: SearchPublicKey: pSolution0->SetFile(pMainApp); if (pSolution1) pSolution1->SetFile(pLibcc); + if (pSolution2) pSolution2->SetFile(pLibcc); PRINT_MESSAGE(""); if (!pSolution0->FindPatchOffset()) { @@ -207,13 +214,18 @@ SearchPublicKey: if (pSolution1 && !pSolution1->FindPatchOffset()) { PRINT_MESSAGE("MESSAGE: Cannot find RSA public key in libcc.dll. Solution1 will be omitted."); pSolution1->SetFile(nullptr); - delete pSolution1; - pSolution1 = nullptr; + SAFE_DELETE(pSolution1); + } + + if (pSolution2 && !pSolution2->FindPatchOffset()) { + PRINT_MESSAGE("MESSAGE: Cannot find RSA public key in libcc.dll. Solution2 will be omitted."); + pSolution2->SetFile(nullptr); + SAFE_DELETE(pSolution2); } LoadingKey: PRINT_MESSAGE(""); - if (!LoadKey(cipher, argc == 3 ? argv[2] : nullptr, pSolution0, pSolution1)) + if (!LoadKey(cipher, argc == 3 ? argv[2] : nullptr, pSolution0, pSolution1, pSolution2)) goto ON_tmain_ERROR; BackupFiles: @@ -242,7 +254,7 @@ BackupFiles: goto ON_tmain_ERROR; } - if (pSolution1) { + if (pSolution1 || pSolution2) { ErrorCode = BackupFile(InstallationPath + LibccName, InstallationPath + LibccName + TEXT(".backup")); if (ErrorCode == ERROR_SUCCESS) { PRINT_MESSAGE("MESSAGE: libcc.dll has been backed up successfully."); @@ -269,16 +281,22 @@ MakingPatch: if (pSolution1 && !pSolution1->MakePatch(cipher)) goto ON_tmain_ERROR; + if (pSolution2 && !pSolution2->MakePatch(cipher)) + goto ON_tmain_ERROR; + PRINT_MESSAGE("Solution0 has been done successfully."); if (pSolution1) PRINT_MESSAGE("Solution1 has been done successfully."); + if (pSolution2) + PRINT_MESSAGE("Solution2 has been done successfully."); ON_tmain_ERROR: - delete pSolution1; - delete pSolution0; - delete pLibcc; - delete pMainApp; - delete cipher; + SAFE_DELETE(pSolution2); + SAFE_DELETE(pSolution1); + SAFE_DELETE(pSolution0); + SAFE_DELETE(pLibcc); + SAFE_DELETE(pMainApp); + SAFE_DELETE(cipher); return 0; } diff --git a/navicat-patcher/def.hpp b/navicat-patcher/def.hpp index 1bf47a6..f125ef5 100644 --- a/navicat-patcher/def.hpp +++ b/navicat-patcher/def.hpp @@ -29,6 +29,48 @@ namespace Helper { void PrintMemory(const void* from, const void* to, const void* base = nullptr); PIMAGE_SECTION_HEADER ImageSectionHeader(PVOID lpBase, LPCSTR lpSectionName); + + template + void QuickSort(_Type* pArray, off_t begin, off_t end) { + if (end - begin <= 1) + return; + + off_t i = begin; + off_t j = end - 1; + _Type seperator = static_cast<_Type&&>(pArray[begin]); + + while (i < j) { + if (_Ascending) { + while (i < j && seperator <= pArray[j]) + --j; + + if (i < j) + pArray[i++] = static_cast<_Type&&>(pArray[j]); + + while (i < j && pArray[i] <= seperator) + ++i; + + if (i < j) + pArray[j--] = static_cast<_Type&&>(pArray[i]); + } else { + while (i < j && seperator >= pArray[j]) + --j; + + if (i < j) + pArray[i++] = static_cast<_Type&&>(pArray[j]); + + while (i < j && pArray[i] >= seperator) + ++i; + + if (i < j) + pArray[j--] = static_cast<_Type&&>(pArray[i]); + } + } + + pArray[i] = static_cast<_Type&&>(seperator); + QuickSort<_Type, _Ascending>(pArray, begin, i); + QuickSort<_Type, _Ascending>(pArray, i + 1, end); + } } #define REPORT_ERROR(msg) Helper::ErrorReport(TEXT(__FUNCTION__), __LINE__, TEXT(msg)) @@ -199,4 +241,37 @@ namespace Patcher { // Return true if success, otherwise return false virtual bool MakePatch(RSACipher* cipher) const override; }; + + class Solution2 : public Solution { + private: + static constexpr size_t KeywordsCount = 0x188; + static const char KeywordsMeta[KeywordsCount + 1]; + static uint8_t Keywords[KeywordsCount][5]; + + FileMapper* pTargetFile; + off_t PatchOffsets[KeywordsCount]; + + void BuildKeywords() noexcept; + public: + Solution2() : + pTargetFile(nullptr) { + memset(PatchOffsets, -1, sizeof(PatchOffsets)); + } + + virtual void SetFile(FileMapper* pLibccFile) noexcept override { + pTargetFile = pLibccFile; + } + + // Solution2 has no requirements for an RSA-2048 key + virtual bool CheckKey(RSACipher* cipher) const noexcept override { + return true; + } + + // Return true if found, otherwise return false + virtual bool FindPatchOffset() noexcept override; + + // Make a patch based on an RSA private key given + // Return true if success, otherwise return false + virtual bool MakePatch(RSACipher* cipher) const override; + }; } diff --git a/navicat-patcher/navicat-patcher.vcxproj b/navicat-patcher/navicat-patcher.vcxproj index dfd52de..3ce9893 100644 --- a/navicat-patcher/navicat-patcher.vcxproj +++ b/navicat-patcher/navicat-patcher.vcxproj @@ -169,6 +169,7 @@ + diff --git a/navicat-patcher/navicat-patcher.vcxproj.filters b/navicat-patcher/navicat-patcher.vcxproj.filters index 2d72240..a3a4832 100644 --- a/navicat-patcher/navicat-patcher.vcxproj.filters +++ b/navicat-patcher/navicat-patcher.vcxproj.filters @@ -27,6 +27,9 @@ 源文件 + + 源文件 +