Jelajahi Sumber

Add support for Navicat Premium 12.1.11 x64

Double Sine 7 tahun lalu
induk
melakukan
5ffe7a198e

+ 146 - 0
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>();
+        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<uint32_t*>(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<uint32_t*>(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<uint8_t>();
+
+        RSAPublicKeyPEM = cipher->ExportKeyString<RSACipher::KeyType::PublicKey, RSACipher::KeyFormat::PEM>();
+        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;
+    }
+}

+ 34 - 16
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 <Navicat installation path> [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<RSACipher::KeyType::PrivateKey, RSACipher::KeyFormat::NotSpecified>("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;
 }
 

+ 75 - 0
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<typename _Type, bool _Ascending = true>
+    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;
+    };
 }

+ 1 - 0
navicat-patcher/navicat-patcher.vcxproj

@@ -169,6 +169,7 @@
     <ClCompile Include="Helper.cpp" />
     <ClCompile Include="Solution0.cpp" />
     <ClCompile Include="Solution1.cpp" />
+    <ClCompile Include="Solution2.cpp" />
     <ClCompile Include="_tmain.cpp" />
   </ItemGroup>
   <ItemGroup>

+ 3 - 0
navicat-patcher/navicat-patcher.vcxproj.filters

@@ -27,6 +27,9 @@
     <ClCompile Include="Helper.cpp">
       <Filter>源文件</Filter>
     </ClCompile>
+    <ClCompile Include="Solution2.cpp">
+      <Filter>源文件</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="def.hpp">