Jelajahi Sumber

refact navicat-patcher

Double Sine 7 tahun lalu
induk
melakukan
8e39d6c877

+ 2 - 1
navicat-patcher/EncryptPublicKey.cpp

@@ -3,8 +3,9 @@
 
 namespace patcher {
 
+    static Navicat11Crypto cipher("23970790", 8);
+
     std::string EncryptPublicKey(const char* public_key, size_t len) {
-        Navicat11Crypto cipher("23970790", 8);
         auto&& temp = cipher.EncryptString(public_key, len);
         return std::string(temp.begin(), temp.end());
     }

+ 266 - 0
navicat-patcher/RSACipher.hpp

@@ -0,0 +1,266 @@
+#pragma once
+#include <openssl/err.h>
+#include <openssl/bio.h>
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+
+#include <string>
+
+#ifdef _DEBUG
+#pragma comment(lib, "libcryptoMTd.lib")
+#else
+#pragma comment(lib, "libcryptoMT.lib")
+#endif
+#pragma comment(lib, "WS2_32.lib")      // some symbol are used in OpenSSL static lib
+#pragma comment(lib, "Crypt32.lib")     // some symbol are used in OpenSSL static lib
+
+class RSACipher {
+private:
+    RSA * _RsaObj;
+
+    RSACipher() : _RsaObj(nullptr) {}
+    RSACipher(RSA* lpRsa) : _RsaObj(lpRsa) {}
+
+    RSACipher(const RSACipher&) = delete;
+    RSACipher(RSACipher&&) = delete;
+    RSACipher& operator=(const RSACipher&) = delete;
+    RSACipher& operator=(RSACipher&&) = delete;
+
+public:
+
+    enum class KeyType {
+        PrivateKey,
+        PublicKey
+    };
+
+    enum class KeyFormat {
+        NotSpecified,
+        PEM,
+        PKCS1
+    };
+
+    ~RSACipher() {
+        if (_RsaObj)
+            RSA_free(_RsaObj);
+        _RsaObj = nullptr;
+    }
+
+    static RSACipher* Create() {
+        RSACipher* aCipher = new RSACipher(RSA_new());
+        if (aCipher->_RsaObj == nullptr) {
+            delete aCipher;
+            aCipher = nullptr;
+        }
+        return aCipher;
+    }
+
+    bool GenerateKey(int bits, unsigned long long e = RSA_F4) {
+        bool bSuccess = false;
+        BIGNUM* bn_e = nullptr;
+
+        bn_e = BN_new();
+        if (bn_e == nullptr)
+            goto ON_RSACipher_GenerateKey0_ERROR;
+
+        if (!BN_set_word(bn_e, e))
+            goto ON_RSACipher_GenerateKey0_ERROR;
+
+        if (!RSA_generate_key_ex(_RsaObj, bits, bn_e, nullptr))
+            goto ON_RSACipher_GenerateKey0_ERROR;
+
+        bSuccess = true;
+
+    ON_RSACipher_GenerateKey0_ERROR:
+        if (bn_e)
+            BN_free(bn_e);
+        return bSuccess;
+    }
+
+    template<KeyType _Type, KeyFormat _Format = KeyFormat::NotSpecified>
+    bool ExportKeyToFile(const std::string& filename) {
+        static_assert(
+            _Type == KeyType::PrivateKey || (_Format == KeyFormat::PEM || _Format == KeyFormat::PKCS1),
+            "Not supported format."
+        );
+
+        bool bSuccess = false;
+        BIO* bio_file = nullptr;
+
+        bio_file = BIO_new_file(filename.c_str(), "w");
+        if (bio_file == nullptr)
+            goto ON_RSACipher_ExportKeyToFile_0_ERROR;
+
+        if (_Type == KeyType::PrivateKey) {
+            bSuccess = PEM_write_bio_RSAPrivateKey(bio_file, _RsaObj, nullptr, nullptr, 0, nullptr, nullptr) ? true : false;
+        } else {
+            if (_Format == KeyFormat::PEM)
+                bSuccess = PEM_write_bio_RSA_PUBKEY(bio_file, _RsaObj) ? true : false;
+            else if (_Format == KeyFormat::PKCS1)
+                bSuccess = PEM_write_bio_RSAPublicKey(bio_file, _RsaObj) ? true : false;
+        }
+
+    ON_RSACipher_ExportKeyToFile_0_ERROR:
+        return bSuccess;
+    }
+
+    template<KeyType _Type, KeyFormat _Format = KeyFormat::NotSpecified>
+    std::string ExportKeyString() {
+        static_assert(
+            _Type == KeyType::PrivateKey || (_Format == KeyFormat::PEM || _Format == KeyFormat::PKCS1),
+            "Not supported format."
+        );
+
+        std::string KeyString;
+        BIO* bio_mem = nullptr;
+        int len = 0;
+        const char* lpdata = nullptr;
+
+        bio_mem = BIO_new(BIO_s_mem());
+        if (bio_mem == nullptr)
+            goto ON_RSACipher_ExportKeyString_0_ERROR;
+
+        if (_Type == KeyType::PrivateKey) {
+            if (!PEM_write_bio_RSAPrivateKey(bio_mem, _RsaObj, nullptr, nullptr, 0, nullptr, nullptr))
+                goto ON_RSACipher_ExportKeyString_0_ERROR;
+        } else {
+            if (_Format == KeyFormat::PEM) {
+                if (!PEM_write_bio_RSA_PUBKEY(bio_mem, _RsaObj))
+                    goto ON_RSACipher_ExportKeyString_0_ERROR;
+            } else if (_Format == KeyFormat::PKCS1) {
+                if (!PEM_write_bio_RSAPublicKey(bio_mem, _RsaObj))
+                    goto ON_RSACipher_ExportKeyString_0_ERROR;
+            }
+        }
+
+        len = BIO_get_mem_data(bio_mem, &lpdata);
+
+        KeyString.resize(len);
+        memcpy(KeyString.data(), lpdata, len);
+
+    ON_RSACipher_ExportKeyString_0_ERROR:
+        if (bio_mem)
+            BIO_free_all(bio_mem);
+        return KeyString;
+    }
+
+    template<KeyType _Type, KeyFormat _Format = KeyFormat::NotSpecified>
+    bool ImportKeyFromFile(const std::string& filename) {
+        static_assert(
+            _Type == KeyType::PrivateKey || (_Format == KeyFormat::PEM || _Format == KeyFormat::PKCS1),
+            "Not supported format."
+        );
+
+        bool bSuccess = false;
+        BIO* bio_file = nullptr;
+        RSA* _newRsaObj = nullptr;
+
+        bio_file = BIO_new_file(filename.c_str(), "r");
+        if (bio_file == nullptr)
+            goto ON_RSACipher_ImportKeyFromFile_0_ERROR;
+
+        if (_Type == KeyType::PrivateKey) {
+            _newRsaObj = PEM_read_bio_RSAPrivateKey(bio_file, nullptr, nullptr, nullptr);
+        } else {
+            if (_Format == KeyFormat::PEM)
+                _newRsaObj = PEM_read_bio_RSA_PUBKEY(bio_file, nullptr, nullptr, nullptr);
+            else if (_Format == KeyFormat::PKCS1)
+                _newRsaObj = PEM_read_bio_RSAPublicKey(bio_file, nullptr, nullptr, nullptr);
+        }
+
+        if (_newRsaObj) {
+            RSA_free(_RsaObj);
+            _RsaObj = _newRsaObj;
+            bSuccess = true;
+        }
+
+    ON_RSACipher_ImportKeyFromFile_0_ERROR:
+        if (bio_file)
+            BIO_free_all(bio_file);
+        return bSuccess;
+    }
+
+    template<KeyType _Type, KeyFormat _Format = KeyFormat::NotSpecified>
+    bool ImportKeyString(const std::string& KeyString) {
+        static_assert(
+            _Type == KeyType::PrivateKey || (_Format == KeyFormat::PEM || _Format == KeyFormat::PKCS1),
+            "Not supported format."
+        );
+
+        bool bSuccess = false;
+        BIO* bio_mem = nullptr;
+        RSA* _newRsaObj = nullptr;
+
+        bio_mem = BIO_new(BIO_s_mem());
+        if (bio_mem == nullptr)
+            goto ON_RSACipher_ImportKeyString_0_ERROR;
+
+        BIO_puts(bio_mem, KeyString.c_str());
+
+        if (_Type == KeyType::PrivateKey) {
+            _newRsaObj = PEM_read_bio_RSAPrivateKey(bio_mem, nullptr, nullptr, nullptr);
+        } else {
+            if (_Format == KeyFormat::PEM)
+                _newRsaObj = PEM_read_bio_RSA_PUBKEY(bio_mem, nullptr, nullptr, nullptr);
+            else if (_Format == KeyFormat::PKCS1)
+                _newRsaObj = PEM_read_bio_RSAPublicKey(bio_mem, nullptr, nullptr, nullptr);
+        }
+
+        if (_newRsaObj) {
+            RSA_free(_RsaObj);
+            _RsaObj = _newRsaObj;
+            bSuccess = true;
+        }
+
+    ON_RSACipher_ImportKeyString_0_ERROR:
+        if (bio_mem)
+            BIO_free_all(bio_mem);
+        return bSuccess;
+    }
+
+    template<KeyType _Type = KeyType::PublicKey>
+    int Encrypt(const void* from, int len, void* to, int padding) {
+        int write_bytes = 0;
+
+        if (_Type == KeyType::PrivateKey) {
+            write_bytes = RSA_private_encrypt(len,
+                                              reinterpret_cast<const unsigned char*>(from),
+                                              reinterpret_cast<unsigned char*>(to),
+                                              _RsaObj,
+                                              padding);
+        } else {
+            write_bytes = RSA_public_encrypt(len,
+                                             reinterpret_cast<const unsigned char*>(from),
+                                             reinterpret_cast<unsigned char*>(to),
+                                             _RsaObj,
+                                             padding);
+        }
+
+        if (write_bytes == -1)
+            write_bytes = 0;
+        return write_bytes;
+    }
+
+    template<KeyType _Type = KeyType::PrivateKey>
+    int Decrypt(const void* from, int len, void* to, int padding) {
+        int write_bytes = 0;
+
+        if (_Type == KeyType::PrivateKey) {
+            write_bytes = RSA_private_decrypt(len,
+                                              reinterpret_cast<const unsigned char*>(from),
+                                              reinterpret_cast<unsigned char*>(to),
+                                              _RsaObj,
+                                              padding);
+        } else {
+            write_bytes = RSA_public_decrypt(len,
+                                             reinterpret_cast<const unsigned char*>(from),
+                                             reinterpret_cast<unsigned char*>(to),
+                                             _RsaObj,
+                                             padding);
+        }
+
+        if (write_bytes == -1)
+            write_bytes = 0;
+        return write_bytes;
+    }
+
+};

+ 212 - 37
navicat-patcher/Solution0.cpp

@@ -3,57 +3,232 @@
 // Solution0 is for navicat premium of which the version < 12.0.25
 namespace patcher::Solution0 {
 
-    BOOL Do(LPCTSTR navicat_exe_path, LPCTSTR prepared_key_file) {
-        if (!BackupFile(navicat_exe_path))
-            return FALSE;
-
-        RSA* PrivateKey = nullptr;
-        if (prepared_key_file == nullptr) {
-            PrivateKey = GenerateRSAKey();
-            if (PrivateKey == nullptr)
-                return FALSE;
-
-            if (!WriteRSAPrivateKeyToFile(TEXT("RegPrivateKey.pem"), PrivateKey)) {
-                RSA_free(PrivateKey);
-                return FALSE;
+    static std::Tstring InstallationPath;
+
+    static const CHAR Keyword[] =
+        "-----BEGIN PUBLIC KEY-----\r\n"
+        "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I\r\n"
+        "qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv\r\n"
+        "a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF\r\n"
+        "R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2\r\n"
+        "WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt\r\n"
+        "YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ\r\n"
+        "awIDAQAB\r\n"
+        "-----END PUBLIC KEY-----\r\n";
+
+    static const DWORD KeywordLength = sizeof(Keyword) - 1;
+
+    static LPCTSTR PossibleName[3] = {
+        TEXT("navicat.exe"),
+        TEXT("modeler.exe"),
+        TEXT("rviewer.exe")
+    };
+
+    static LPCTSTR TargetName = NULL;
+
+    static HMODULE hTarget = NULL;
+
+    BOOL Init(const std::Tstring& Path) {
+        BOOL bSuccess = FALSE;
+        DWORD dwLastError = ERROR_SUCCESS;
+        DWORD attr = INVALID_FILE_ATTRIBUTES;
+
+        attr = GetFileAttributes(Path.c_str());
+        if (attr == INVALID_FILE_ATTRIBUTES) {
+            dwLastError = GetLastError();
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("Failed @ GetFileAttributes. CODE: 0x%08X\n"), dwLastError);
+            goto ON_Init_ERROR;
+        }
+
+        if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("Error: Path is not a directory.\n"));
+            goto ON_Init_ERROR;
+        }
+
+        InstallationPath = Path;
+        if (InstallationPath.back() != TEXT('\\') && InstallationPath.back() != TEXT('/'))
+            InstallationPath.push_back(TEXT('\\'));
+
+        bSuccess = TRUE;
+
+    ON_Init_ERROR:
+        return bSuccess;
+    }
+
+    BOOL CheckKey(RSACipher* cipher) {
+        return TRUE;
+    }
+
+    BOOL FindTargetFile() {
+        BOOL bSuccess = FALSE;
+        DWORD dwLastError = ERROR_SUCCESS;
+
+        for (size_t i = 0; i < _countof(PossibleName); ++i) {
+            std::Tstring&& PossibleFileName = InstallationPath + PossibleName[i];
+            
+            hTarget = LoadLibrary(PossibleFileName.c_str());
+            if (hTarget == NULL && (dwLastError = GetLastError()) != ERROR_MOD_NOT_FOUND) {
+                _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+                _tprintf_s(TEXT("Unexpected Error @ LoadLibrary. CODE: 0x%08X\n"), dwLastError);
+                goto ON_FindTargetFile_ERROR;
+            }
+            if (hTarget) {
+                _tprintf_s(TEXT("Target has been found: %s\n"), PossibleName[i]);
+                TargetName = PossibleName[i];
+                bSuccess = TRUE;
+                goto ON_FindTargetFile_ERROR;
             }
+        }
+
+    ON_FindTargetFile_ERROR:
+        return bSuccess;
+    }
+
+    BOOL CheckFile() {
+        BOOL bFound = FALSE;
+        DWORD dwLastError = ERROR_SUCCESS;
+        HRSRC hRes = NULL;
+        HGLOBAL hGLobal = NULL;
+        PVOID lpData = NULL;
+        DWORD dwSize = 0;
+
+        if (hTarget == NULL) {
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("Error: Target has not been set yet.\n"));
+            goto ON_CheckFile_ERROR;
+        }
+
+        hRes = FindResource(hTarget, TEXT("ACTIVATIONPUBKEY"), RT_RCDATA);
+        if (hRes == NULL) {
+            dwLastError = GetLastError();
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("Failed @ FindResource. CODE: 0x%08X\n"), dwLastError);
+            goto ON_CheckFile_ERROR;
+        }
+
+        hGLobal = LoadResource(hTarget, hRes);
+        if (hGLobal == NULL) {
+            dwLastError = GetLastError();
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("Failed @ LoadResource. CODE: 0x%08X\n"), dwLastError);
+            goto ON_CheckFile_ERROR;
+        }
+
+        lpData = LockResource(hGLobal);
+        if (lpData == NULL) {
+            dwLastError = GetLastError();
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("Failed @ LockResource. CODE: 0x%08X\n"), dwLastError);
+            goto ON_CheckFile_ERROR;
+        }
+
+        dwSize = SizeofResource(hTarget, hRes);
+        if (dwSize == 0) {
+            dwLastError = GetLastError();
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("Failed @ SizeofResource. CODE: 0x%08X\n"), dwLastError);
+            goto ON_CheckFile_ERROR;
+        }
+
+        if (dwSize == KeywordLength && memcmp(lpData, Keyword, KeywordLength) == 0) {
+            FreeLibrary(hTarget);
+            hTarget = NULL;
+            bFound = TRUE;
         } else {
-            PrivateKey = ReadRSAPrivateKeyFromFile(prepared_key_file);
-            if (PrivateKey == nullptr)
-                return FALSE;
+            FreeLibrary(hTarget);
+            hTarget = NULL;
+            TargetName = NULL;
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("ERROR: Resource doest not match.\n"));
+            goto ON_CheckFile_ERROR;
         }
 
-        char* pem_pubkey = GetPEMText(PrivateKey);
-        if (pem_pubkey == nullptr)
-            return FALSE;
+    ON_CheckFile_ERROR:
+        return bFound;
+    }
 
-        RSA_free(PrivateKey);   // we do not need it anymore
+    BOOL BackupFile() {
+        BOOL bSuccess = FALSE;
+        DWORD dwLastError = ERROR_SUCCESS;
+        std::Tstring&& TargetFileName = InstallationPath + TargetName;
+        std::Tstring&& BackupFileName = InstallationPath + TargetName + TEXT(".backup");
 
-        HANDLE hUpdater = BeginUpdateResource(navicat_exe_path, FALSE);
-        if (hUpdater == NULL) {
-            _tprintf_s(TEXT("Cannot modify navicat.exe. CODE: 0x%08x @[patcher::Solution0::Do -> BeginUpdateResource]\r\n"), GetLastError());
+        if (!CopyFile(TargetFileName.c_str(), BackupFileName.c_str(), TRUE)) {
+            dwLastError = GetLastError();
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("Failed @ CopyFile. CODE: 0x%08X\n"), dwLastError);
+            goto ON_BackupFile_ERROR;
+        }
+
+        bSuccess = TRUE;
+    ON_BackupFile_ERROR:
+        return bSuccess;
+    }
 
-            delete[] pem_pubkey;
-            return FALSE;
+    BOOL Do(RSACipher* cipher) {
+        BOOL bSuccess = FALSE;
+        DWORD dwLastError = ERROR_SUCCESS;
+        std::string RSAPublicKeyPEM;
+        std::Tstring&& TargetFileName = InstallationPath + TargetName;
+        HANDLE hUpdater = NULL;
+
+        RSAPublicKeyPEM = cipher->ExportKeyString<RSACipher::KeyType::PublicKey, RSACipher::KeyFormat::PEM>();
+        if (RSAPublicKeyPEM.empty()) {
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("ERROR: cipher->ExportKeyString failed.\n"));
+            goto ON_Do_ERROR;
+        }
+
+        [](std::string& str, const std::string& OldSub, const std::string& NewSub) {
+            std::string::size_type pos = 0;
+            std::string::size_type srclen = OldSub.size();
+            std::string::size_type dstlen = NewSub.size();
+
+            while ((pos = str.find(OldSub, pos)) != std::string::npos) {
+                str.replace(pos, srclen, NewSub);
+                pos += dstlen;
+            }
+        } (RSAPublicKeyPEM, "\n", "\r\n");  // replace '\n' to '\r\n'
+
+        if (RSAPublicKeyPEM.length() != KeywordLength) {
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("ERROR: Public key length does not match.\n"));
+            goto ON_Do_ERROR;
+        }
+
+        hUpdater = BeginUpdateResource(TargetFileName.c_str(), FALSE);
+        if (hUpdater == NULL) {
+            dwLastError = GetLastError();
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("Failed @ BeginUpdateResource. CODE: 0x%08X\n"), dwLastError);
+            goto ON_Do_ERROR;
         }
 
         if (!UpdateResource(hUpdater,
                             RT_RCDATA,
                             TEXT("ACTIVATIONPUBKEY"),
                             MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
-                            pem_pubkey, strlen(pem_pubkey))) {
-            _tprintf_s(TEXT("Cannot replace public key. CODE: 0x%08x @[patcher::Solution0::Do -> UpdateResource]\r\n"), GetLastError());
+                            (LPVOID)RSAPublicKeyPEM.c_str(), 
+                            KeywordLength)) {
+            dwLastError = GetLastError();
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("Failed @ UpdateResource. CODE: 0x%08X\n"), dwLastError);
+            goto ON_Do_ERROR;
+        } 
+        
+        bSuccess = TRUE;
 
-            EndUpdateResource(hUpdater, TRUE);
-            delete[] pem_pubkey;
-            return FALSE;
-        } else {
-            _tprintf_s(TEXT("@[patcher::Solution0::Do]: Public key has been replaced by:\r\n%hs"), pem_pubkey);
+    ON_Do_ERROR:
+        EndUpdateResource(hUpdater, !bSuccess);
+        return bSuccess;
+    }
 
-            EndUpdateResource(hUpdater, FALSE);
-            delete[] pem_pubkey;
-            return TRUE;
+    VOID Finalize() {
+        if (hTarget) {
+            FreeLibrary(hTarget);
+            hTarget = NULL;
         }
     }
-
-}
+}

+ 320 - 339
navicat-patcher/Solution1.cpp

@@ -3,323 +3,365 @@
 // Solution1 is for navicat premium of which the version = 12.0.25
 namespace patcher::Solution1 {
 
-    static BOOL CheckRSAKeyIsAppropriate(RSA* PrivateKey) {
-        char* pem_pubkey = GetPEMText(PrivateKey);
-        if (pem_pubkey == nullptr)
+    static std::Tstring InstallationPath;
+
+    static const CHAR Keyword0[] = 
+        "D75125B70767B94145B47C1CB3C0755E"
+        "7CCB8825C5DCE0C58ACF944E08280140"
+        "9A02472FAFFD1CD77864BB821AE36766"
+        "FEEDE6A24F12662954168BFA314BD950"
+        "32B9D82445355ED7BC0B880887D650F5";
+
+    static const DWORD KeywordSize0 = sizeof(Keyword0) - 1;
+
+    static const uint8_t Keyword1[] = {
+        0xFE, 0xEA, 0xBC, 0x01
+    };
+
+    static const DWORD KeywordSize1 = sizeof(Keyword1);
+
+    static const CHAR Keyword2[] =
+        "E1CED09B9C2186BF71A70C0FE2F1E0AE"
+        "F3BD6B75277AAB20DFAF3D110F75912B"
+        "FB63AC50EC4C48689D1502715243A79F"
+        "39FF2DE2BF15CE438FF885745ED54573"
+        "850E8A9F40EE2FF505EB7476F95ADB78"
+        "3B28CA374FAC4632892AB82FB3BF4715"
+        "FCFE6E82D03731FC3762B6AAC3DF1C3B"
+        "C646FE9CD3C62663A97EE72DB932A301"
+        "312B4A7633100C8CC357262C39A2B3A6"
+        "4B224F5276D5EDBDF0804DC3AC4B8351"
+        "62BB1969EAEBADC43D2511D6E0239287"
+        "81B167A48273B953378D3D2080CC0677"
+        "7E8A2364F0234B81064C5C739A8DA28D"
+        "C5889072BF37685CBC94C2D31D0179AD"
+        "86D8E3AA8090D4F0B281BE37E0143746"
+        "E6049CCC06899401264FA471C016A96C"
+        "79815B55BBC26B43052609D9D175FBCD"
+        "E455392F10E51EC162F51CF732E6BB39"
+        "1F56BBFD8D957DF3D4C55B71CEFD54B1"
+        "9C16D458757373E698D7E693A8FC3981"
+        "5A8BF03BA05EA8C8778D38F9873D62B4"
+        "460F41ACF997C30E7C3AF025FA171B5F"
+        "5AD4D6B15E95C27F6B35AD61875E5505"
+        "449B4E";
+
+    static const DWORD KeywordSize2 = sizeof(Keyword2) - 1;
+
+    static const uint8_t Keyword3[] = {
+        0x59, 0x08, 0x01, 0x00
+    };
+
+    static const DWORD KeywordSize3 = sizeof(Keyword3);
+
+    static const CHAR Keyword4[] = "92933";
+
+    static const DWORD KeywordSize4 = sizeof(Keyword4) - 1;
+
+    static DWORD KeywordOffset[5] = { -1, -1, -1, -1, -1 };
+
+    static LPCTSTR TargetName = TEXT("libcc.dll");
+
+    static HANDLE hTarget = INVALID_HANDLE_VALUE;
+    static HANDLE hTargetMap = NULL;
+    static PVOID lpFileContent = NULL;
+
+    BOOL Init(const std::Tstring& Path) {
+        BOOL bSuccess = FALSE;
+        DWORD dwLastError = ERROR_SUCCESS;
+        DWORD attr = INVALID_FILE_ATTRIBUTES;
+
+        attr = GetFileAttributes(Path.c_str());
+        if (attr == INVALID_FILE_ATTRIBUTES) {
+            dwLastError = GetLastError();
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("Failed @ GetFileAttributes. CODE: 0x%08X\n"), dwLastError);
+            goto ON_Init_ERROR;
+        }
+
+        if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("Error: Path is not a directory.\n"));
+            goto ON_Init_ERROR;
+        }
+
+        InstallationPath = Path;
+        if (InstallationPath.back() != TEXT('\\') && InstallationPath.back() != TEXT('/'))
+            InstallationPath.push_back(TEXT('\\'));
+
+        bSuccess = TRUE;
+
+    ON_Init_ERROR:
+        return bSuccess;
+    }
+
+    BOOL CheckKey(RSACipher* cipher) {
+        BOOL bOk = FALSE;
+        std::string RSAPublicKeyPEM;
+
+        RSAPublicKeyPEM = cipher->ExportKeyString<RSACipher::KeyType::PublicKey, RSACipher::KeyFormat::PEM>();
+        if (RSAPublicKeyPEM.empty()) {
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("ERROR: cipher->ExportKeyString failed.\n"));
             return FALSE;
+        }
 
-        std::string encrypted_pem_text = EncryptPublicKey(pem_pubkey, strlen(pem_pubkey));
-        delete[] pem_pubkey;
+        [](std::string& str, const std::string& OldSub, const std::string& NewSub) {
+            std::string::size_type pos = 0;
+            std::string::size_type srclen = OldSub.size();
+            std::string::size_type dstlen = NewSub.size();
+
+            while ((pos = str.find(OldSub, pos)) != std::string::npos) {
+                str.replace(pos, srclen, NewSub);
+                pos += dstlen;
+            }
+        } (RSAPublicKeyPEM, "\n", "\r\n");  // replace '\n' to '\r\n'
+
+        std::string encrypted_pem_text = EncryptPublicKey(RSAPublicKeyPEM.c_str(),
+                                                          RSAPublicKeyPEM.length());
 
         if (encrypted_pem_text[160] > '9' || encrypted_pem_text[160] < '1') 
             return FALSE;
+
         for (int i = 1; i < 8; ++i)
             if (encrypted_pem_text[160 + i] > '9' || encrypted_pem_text[160 + i] < '0') 
                 return FALSE;
 
         if (encrypted_pem_text[910] > '9' || encrypted_pem_text[910] < '1') 
             return FALSE;
+
         for (int i = 1; i < 5; ++i)
             if (encrypted_pem_text[910 + i] > '9' || encrypted_pem_text[910 + i] < '0')
                 return FALSE;
-            
+
         return TRUE;
     }
 
-    static RSA* GenerateAppropriateRSAKey() {
-        while (true) {
-            RSA* Key = GenerateRSAKey();
-            if (Key == nullptr)
-                return nullptr;
+    static PIMAGE_SECTION_HEADER ImageSectionHeader(PVOID lpBase, LPCSTR lpSectionName) {
+        IMAGE_DOS_HEADER* pFileHeader = NULL;
+        IMAGE_NT_HEADERS* pNtHeader = NULL;
+        IMAGE_SECTION_HEADER* pSectionHeaders = NULL;
 
-            if (CheckRSAKeyIsAppropriate(Key)) {
-                return Key;
-            } else {
-                RSA_free(Key);
-            }
-        }
-    }
+        pFileHeader = (IMAGE_DOS_HEADER*)lpBase;
+        if (pFileHeader->e_magic != IMAGE_DOS_SIGNATURE)
+            return NULL;
 
-    static BOOL FindPatchOffset(LPCTSTR libcc_dll_path, DWORD Offset[5]) {
-        Offset[0] = 0;
-        Offset[1] = 0;
-        Offset[2] = 0;
-        Offset[3] = 0;
-        Offset[4] = 0;
+        pNtHeader = (IMAGE_NT_HEADERS*)((BYTE*)lpBase + pFileHeader->e_lfanew);
+        if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
+            return NULL;
 
-        HANDLE h_libcc = CreateFile(libcc_dll_path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
-        if (h_libcc == INVALID_HANDLE_VALUE) {
-            _tprintf_s(TEXT("Failed to open libcc.dll. CODE: 0x%08x @[FindPatchOffset -> CreateFile]\r\n"), GetLastError());
-            return FALSE;
-        }
+        pSectionHeaders = (IMAGE_SECTION_HEADER*)((BYTE*)pNtHeader +
+                                                  offsetof(IMAGE_NT_HEADERS, OptionalHeader) +
+                                                  pNtHeader->FileHeader.SizeOfOptionalHeader);
+        for (WORD i = 0; i < pNtHeader->FileHeader.NumberOfSections; ++i)
+            if (_stricmp((const char*)pSectionHeaders[i].Name, lpSectionName) == 0)
+                return pSectionHeaders + i;
 
-        // DWORD is enough, you know libcc.dll cannot be larger than 4GB
-        DWORD libcc_size = GetFileSize(h_libcc, nullptr);
-        HANDLE h_libcc_map = CreateFileMapping(h_libcc, nullptr, PAGE_READONLY, 0, 0, nullptr);
-        if (h_libcc_map == NULL) {
-            _tprintf_s(TEXT("Failed to create mapping for libcc.dll. CODE: 0x%08x @[FindPatchOffset -> CreateFileMapping]\r\n"), GetLastError());
-            CloseHandle(h_libcc);
-            return FALSE;
-        }
+        return NULL;
+    }
 
-        const uint8_t* libcc = reinterpret_cast<const uint8_t*>(MapViewOfFile(h_libcc_map, FILE_MAP_READ, 0, 0, 0));
-        if (libcc == nullptr) {
-            _tprintf_s(TEXT("Failed to map libcc.dll. CODE: 0x%08x @[FindPatchOffset -> MapViewOfFile]\r\n"), GetLastError());
-            CloseHandle(h_libcc_map);
-            CloseHandle(h_libcc);
-            return FALSE;
+    BOOL FindTargetFile() {
+        DWORD dwLastError = ERROR_SUCCESS;
+        std::Tstring&& TargetFileName = InstallationPath + TargetName;
+        
+        hTarget = CreateFile(TargetFileName.c_str(),
+                             GENERIC_READ | GENERIC_WRITE,
+                             FILE_SHARE_READ,
+                             NULL,
+                             OPEN_EXISTING,
+                             FILE_ATTRIBUTE_NORMAL,
+                             NULL);
+        if (hTarget == INVALID_HANDLE_VALUE) {
+            dwLastError = GetLastError();
+            if (dwLastError == ERROR_FILE_NOT_FOUND) {
+                return FALSE;
+            } else {
+                _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+                _tprintf_s(TEXT("Unexpected Error @ CreateFile. CODE: 0x%08X\n"), dwLastError);
+                return FALSE;
+            }
         }
+        
+        return TRUE;
+    }
 
-        const IMAGE_DOS_HEADER* libcc_dos_header = reinterpret_cast<const IMAGE_DOS_HEADER*>(libcc);
-
-        // check dos signature
-        if (libcc_dos_header->e_magic != IMAGE_DOS_SIGNATURE) {
-            _tprintf_s(TEXT("libcc.dll does not have a valid DOS header. @[FindPatchOffset]\r\n"));
-            UnmapViewOfFile(libcc);
-            CloseHandle(h_libcc_map);
-            CloseHandle(h_libcc);
-            return FALSE;
+    BOOL FindOffset() {
+        BOOL bSuccess = FALSE;
+        DWORD dwLastError = ERROR_SUCCESS;
+        IMAGE_SECTION_HEADER* textSection = NULL;
+        IMAGE_SECTION_HEADER* rdataSection = NULL;
+
+        hTargetMap = CreateFileMapping(hTarget,
+                                       NULL,
+                                       PAGE_READWRITE,
+                                       0,
+                                       0,
+                                       NULL);
+        if (hTargetMap == NULL) {
+            dwLastError = GetLastError();
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("Failed @ CreateFileMapping. CODE: 0x%08X\n"), dwLastError);
+            goto ON_FindOffset_ERROR;
         }
 
-        const IMAGE_NT_HEADERS* libcc_nt_header = reinterpret_cast<const IMAGE_NT_HEADERS*>(libcc + libcc_dos_header->e_lfanew);
-
-        // check nt signature
-        if (libcc_nt_header->Signature != IMAGE_NT_SIGNATURE) {
-            _tprintf_s(TEXT("libcc.dll does not have a valid NT header. @[FindPatchOffset]\r\n"));
-            UnmapViewOfFile(libcc);
-            CloseHandle(h_libcc_map);
-            CloseHandle(h_libcc);
-            return FALSE;
+        lpFileContent = MapViewOfFile(hTargetMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0);
+        if (lpFileContent == NULL) {
+            dwLastError = GetLastError();
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("Failed @ MapViewOfFile. CODE: 0x%08X\n"), dwLastError);
+            goto ON_FindOffset_ERROR;
         }
 
-        // check if a dll
-        if ((libcc_nt_header->FileHeader.Characteristics & IMAGE_FILE_DLL) == 0) {
-            _tprintf_s(TEXT("libcc.dll is not a DLL file. @[FindPatchOffset]\r\n"));
-            UnmapViewOfFile(libcc);
-            CloseHandle(h_libcc_map);
-            CloseHandle(h_libcc);
-            return FALSE;
+        textSection = ImageSectionHeader(lpFileContent, ".text");
+        if (textSection == NULL) {
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("ERROR: Cannot find .text section.\n"));
+            goto ON_FindOffset_ERROR;
         }
 
-        // check if 32-bits or 64-bits
-#if defined(_M_X64)
-        if (libcc_nt_header->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 || libcc_nt_header->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
-            _tprintf_s(TEXT("libcc.dll is not a 64-bits DLL file. @[FindPatchOffset]\r\n"));
-#elif defined(_M_IX86)
-        if (libcc_nt_header->FileHeader.Machine != IMAGE_FILE_MACHINE_I386 || libcc_nt_header->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
-#else
-#error "unknown arch"
-#endif
-            _tprintf_s(TEXT("libcc.dll is not a 32-bits DLL file. @[FindPatchOffset]\r\n"));
-            UnmapViewOfFile(libcc);
-            CloseHandle(h_libcc_map);
-            CloseHandle(h_libcc);
-            return FALSE;
+        rdataSection = ImageSectionHeader(lpFileContent, ".rdata");
+        if (textSection == NULL) {
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("ERROR: Cannot find .rdata section.\n"));
+            goto ON_FindOffset_ERROR;
         }
 
-        WORD section_num = libcc_nt_header->FileHeader.NumberOfSections;
-        const IMAGE_SECTION_HEADER* libcc_section_headers = reinterpret_cast<const IMAGE_SECTION_HEADER*>(
-            libcc + libcc_dos_header->e_lfanew + sizeof(libcc_nt_header->Signature) + sizeof(libcc_nt_header->FileHeader) + libcc_nt_header->FileHeader.SizeOfOptionalHeader
-        );
-
-        const IMAGE_SECTION_HEADER* rdata_section = nullptr;
-        for (WORD i = 0; i < section_num; ++i) {
-            if (*reinterpret_cast<const uint64_t*>(libcc_section_headers[i].Name) == 0x61746164722e) {   // b'\x00\x00atadr.'
-                rdata_section = libcc_section_headers + i;
+        // -------------------------
+        // try to search keyword0
+        // -------------------------
+        for (DWORD i = 0; i < rdataSection->SizeOfRawData; ++i) {
+            if (memcmp((uint8_t*)lpFileContent + rdataSection->PointerToRawData + i, Keyword0, KeywordSize0) == 0) {
+                KeywordOffset[0] = rdataSection->PointerToRawData + i;
                 break;
             }
         }
-        if (rdata_section == nullptr) {
-            _tprintf_s(TEXT(".rdata section is not found. @[FindPatchOffset]\r\n"));
-            UnmapViewOfFile(libcc);
-            CloseHandle(h_libcc_map);
-            CloseHandle(h_libcc);
-            return FALSE;
+
+        if (KeywordOffset[0] == -1) {
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("ERROR: Cannot find Keyword0.\n"));
+            goto ON_FindOffset_ERROR;
+        } else {
+            _tprintf_s(TEXT("Keyword0 has been found: offset = +0x%08X.\n"), KeywordOffset[0]);
         }
 
-        const IMAGE_SECTION_HEADER* text_section = nullptr;
-        for (WORD i = 0; i < section_num; ++i) {
-            if (*reinterpret_cast<const uint64_t*>(libcc_section_headers[i].Name) == 0x747865742e) {   // b'\x00\x00\x00txet.'
-                text_section = libcc_section_headers + i;
+        // -------------------------
+        // try to search keyword1
+        // -------------------------
+        for (DWORD i = 0; i < textSection->SizeOfRawData; ++i) {
+            if (memcmp((uint8_t*)lpFileContent + textSection->PointerToRawData + i, Keyword1, KeywordSize1) == 0) {
+                KeywordOffset[1] = textSection->PointerToRawData + i;
                 break;
             }
         }
-        if (text_section == nullptr) {
-            _tprintf_s(TEXT(".text section is not found. @[FindPatchOffset]\r\n"));
-            UnmapViewOfFile(libcc);
-            CloseHandle(h_libcc_map);
-            CloseHandle(h_libcc);
-            return FALSE;
+
+        if (KeywordOffset[1] == -1) {
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("ERROR: Cannot find Keyword1.\n"));
+            goto ON_FindOffset_ERROR;
+        } else {
+            _tprintf_s(TEXT("Keyword1 has been found: offset = +0x%08X.\n"), KeywordOffset[1]);
         }
 
-        // search offset[0] 
-        {
-            const uint8_t keyword[] = "D75125B70767B94145B47C1CB3C0755E";
-            const uint8_t* start = libcc + rdata_section->PointerToRawData;
-            DWORD section_size = rdata_section->SizeOfRawData;
-            for (DWORD i = 0; i < section_size; ++i) {
-                if (start[i] == keyword[0]) {
-                    bool found = true;
-                    for (DWORD j = 1; j < sizeof(keyword) - 1; ++j)
-                        if (start[i + j] != keyword[j]) {
-                            found = false;
-                            break;
-                        }
-                    if (found) {
-                        Offset[0] = rdata_section->PointerToRawData + i;
-                        break;
-                    }
-                }
+        // -------------------------
+        // try to search keyword2
+        // -------------------------
+        for (DWORD i = 0; i < rdataSection->SizeOfRawData; ++i) {
+            if (memcmp((uint8_t*)lpFileContent + rdataSection->PointerToRawData + i, Keyword2, KeywordSize2) == 0) {
+                KeywordOffset[2] = rdataSection->PointerToRawData + i;
+                break;
             }
         }
 
-        // search offset[2] 
-        {
-            const uint8_t keyword[] = "E1CED09B9C2186BF71A70C0FE2F1E0AE";
-            const uint8_t* start = libcc + rdata_section->PointerToRawData;
-            DWORD section_size = rdata_section->SizeOfRawData;
-            for (DWORD i = 0; i < section_size; ++i) {
-                if (start[i] == keyword[0]) {
-                    bool found = true;
-                    for (DWORD j = 1; j < sizeof(keyword) - 1; ++j)
-                        if (start[i + j] != keyword[j]) {
-                            found = false;
-                            break;
-                        }
-                    if (found) {
-                        Offset[2] = rdata_section->PointerToRawData + i;
-                        break;
-                    }
-                }
-            }
+        if (KeywordOffset[2] == -1) {
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("ERROR: Cannot find Keyword2.\n"));
+            goto ON_FindOffset_ERROR;
+        } else {
+            _tprintf_s(TEXT("Keyword2 has been found: offset = +0x%08X.\n"), KeywordOffset[2]);
         }
 
-        // search offset[4] 
-        {
-            const uint8_t keyword[] = "92933";
-            const uint8_t* start = libcc + rdata_section->PointerToRawData;
-            DWORD section_size = rdata_section->SizeOfRawData;
-            for (DWORD i = 0; i < section_size; ++i) {
-                if (start[i] == keyword[0]) {
-                    bool found = true;
-                    for (DWORD j = 1; j < sizeof(keyword) - 1; ++j)
-                        if (start[i + j] != keyword[j]) {
-                            found = false;
-                            break;
-                        }
-                    if (found) {
-                        Offset[4] = rdata_section->PointerToRawData + i;
-                        break;
-                    }
-                }
+        // -------------------------
+        // try to search keyword3
+        // -------------------------
+        for (DWORD i = 0; i < textSection->SizeOfRawData; ++i) {
+            if (memcmp((uint8_t*)lpFileContent + textSection->PointerToRawData + i, Keyword3, KeywordSize3) == 0) {
+                KeywordOffset[3] = textSection->PointerToRawData + i;
+                break;
             }
         }
 
-        // search offset[1]
-        {
-            const uint8_t keyword[] = { 0xfe, 0xea, 0xbc, 0x01 };
-            const uint8_t* start = libcc + text_section->PointerToRawData;
-            DWORD section_size = text_section->SizeOfRawData;
-            for (DWORD i = 0; i < section_size; ++i) {
-                if (start[i] == keyword[0]) {
-                    bool found = true;
-                    for (DWORD j = 1; j < sizeof(keyword); ++j)
-                        if (start[i + j] != keyword[j]) {
-                            found = false;
-                            break;
-                        }
-                    if (found) {
-                        Offset[1] = text_section->PointerToRawData + i;
-                        break;
-                    }
-                }
-            }
+        if (KeywordOffset[3] == -1) {
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("ERROR: Cannot find Keyword3.\n"));
+            goto ON_FindOffset_ERROR;
+        } else {
+            _tprintf_s(TEXT("Keyword3 has been found: offset = +0x%08X.\n"), KeywordOffset[3]);
         }
 
-        // search offset[3]
-        {
-            const uint8_t keyword[] = { 0x59, 0x08, 0x01, 0x00 };
-            const uint8_t* start = libcc + text_section->PointerToRawData;
-            DWORD section_size = text_section->SizeOfRawData;
-            for (DWORD i = 0; i < section_size; ++i) {
-                if (start[i] == keyword[0]) {
-                    bool found = true;
-                    for (DWORD j = 1; j < sizeof(keyword); ++j)
-                        if (start[i + j] != keyword[j]) {
-                            found = false;
-                            break;
-                        }
-                    if (found) {
-                        Offset[3] = text_section->PointerToRawData + i;
-                        break;
-                    }
-                }
+        // -------------------------
+        // try to search keyword4
+        // -------------------------
+        for (DWORD i = 0; i < rdataSection->SizeOfRawData; ++i) {
+            if (memcmp((uint8_t*)lpFileContent + rdataSection->PointerToRawData + i, Keyword4, KeywordSize4) == 0) {
+                KeywordOffset[4] = rdataSection->PointerToRawData + i;
+                break;
             }
         }
 
-        UnmapViewOfFile(libcc);
-        CloseHandle(h_libcc_map);
-        CloseHandle(h_libcc);
-
-        if (Offset[0] == 0 || Offset[1] == 0 || Offset[2] == 0 || Offset[3] == 0 || Offset[4] == 0) {
-            _tprintf_s(TEXT("Failed to find all patch offset. Is libcc.dll from official? Or you've patched?\r\n"));
-            return FALSE;
+        if (KeywordOffset[4] == -1) {
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("ERROR: Cannot find Keyword4.\n"));
+            goto ON_FindOffset_ERROR;
         } else {
-            return TRUE;
+            _tprintf_s(TEXT("Keyword4 has been found: offset = +0x%08X.\n"), KeywordOffset[4]);
         }
-    }
-
-    BOOL Do(LPCTSTR libcc_dll_path, LPCTSTR prepared_key_file) {
-//         uint8_t expected_hash[SHA256_DIGEST_LENGTH] = {
-//             0x60, 0x7e, 0x0a, 0x84, 0xc7, 0x59, 0x66, 0xb0,
-//             0x0f, 0x3d, 0x12, 0xfa, 0x83, 0x3e, 0x91, 0xd1,
-//             0x59, 0xe4, 0xf5, 0x1a, 0xc5, 0x1b, 0x6b, 0xa6,
-//             0x6f, 0x98, 0xd0, 0xc3, 0xcb, 0xef, 0xdc, 0xe0
-//         };
-// 
-//         if (!Check_libcc_Hash(libcc_dll_path, expected_hash))
-//             return FALSE;
-
-        DWORD Patch_Offset[5];
-        if (!FindPatchOffset(libcc_dll_path, Patch_Offset)) 
-            return FALSE;
         
-            
+        bSuccess = TRUE;
 
-        if (!BackupFile(libcc_dll_path))
-            return FALSE;
+    ON_FindOffset_ERROR:
+        return bSuccess;
+    }
 
-        RSA* PrivateKey = nullptr;
-        if (prepared_key_file != nullptr) {
-            PrivateKey = ReadRSAPrivateKeyFromFile(prepared_key_file);
-            if (PrivateKey == nullptr)
-                return FALSE;
+    BOOL BackupFile() {
+        BOOL bSuccess = FALSE;
+        DWORD dwLastError = ERROR_SUCCESS;
+        std::Tstring&& TargetFileName = InstallationPath + TargetName;
+        std::Tstring&& BackupFileName = InstallationPath + TargetName + TEXT(".backup");
 
-            if (!CheckRSAKeyIsAppropriate(PrivateKey)) {
-                _tprintf_s(TEXT("The key is not appropriate to use. @[patcher::Solution1::Do -> CheckRSAKeyIsAppropriate]\r\n"));
-                RSA_free(PrivateKey);
-                return FALSE;
-            }
+        if (!CopyFile(TargetFileName.c_str(), BackupFileName.c_str(), TRUE)) {
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("Failed @ CopyFile. CODE: 0x%08X\n"), dwLastError);
+            goto ON_BackupFile_ERROR;
+        }
 
-        } else {
-            PrivateKey = GenerateAppropriateRSAKey();
-            if (PrivateKey == nullptr)
-                return FALSE;
+        bSuccess = TRUE;
+    ON_BackupFile_ERROR:
+        return bSuccess;
+    }
 
-            if (!WriteRSAPrivateKeyToFile(TEXT("RegPrivateKey.pem"), PrivateKey)) {
-                RSA_free(PrivateKey);
-                return FALSE;
-            }
-        }
+    BOOL Do(RSACipher* cipher) {
+        std::string RSAPublicKeyPEM;
+        std::string encrypted_pem_pubkey;
 
-        char* pem_pubkey = GetPEMText(PrivateKey);
-        if (pem_pubkey == nullptr)
+        RSAPublicKeyPEM = cipher->ExportKeyString<RSACipher::KeyType::PublicKey, RSACipher::KeyFormat::PEM>();
+        if (RSAPublicKeyPEM.empty()) {
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("ERROR: cipher->ExportKeyString failed.\n"));
             return FALSE;
+        }
 
-        std::string encrypted_pem_pubkey = EncryptPublicKey(pem_pubkey, strlen(pem_pubkey));
+        [](std::string& str, const std::string& OldSub, const std::string& NewSub) {
+            std::string::size_type pos = 0;
+            std::string::size_type srclen = OldSub.size();
+            std::string::size_type dstlen = NewSub.size();
 
-        delete[] pem_pubkey;    // we do not need it anymore
-        RSA_free(PrivateKey);   // we do not need it anymore
+            while ((pos = str.find(OldSub, pos)) != std::string::npos) {
+                str.replace(pos, srclen, NewSub);
+                pos += dstlen;
+            }
+        } (RSAPublicKeyPEM, "\n", "\r\n");  // replace '\n' to '\r\n'
+
+        encrypted_pem_pubkey = EncryptPublicKey(RSAPublicKeyPEM.c_str(), RSAPublicKeyPEM.length());
 
         // split encrypted_pem_pubkey to 5 part:    |160 chars|8 chars|742 chars|5 chars|5 chars|
         //                                                         |                |
@@ -330,107 +372,46 @@ namespace patcher::Solution1 {
         std::string encrypted_pem_pubkey2(encrypted_pem_pubkey.begin() + 160 + 8, encrypted_pem_pubkey.begin() + 160 + 8 + 742);
         std::string encrypted_pem_pubkey3(encrypted_pem_pubkey.begin() + 160 + 8 + 742, encrypted_pem_pubkey.begin() + 160 + 8 + 742 + 5);
         std::string encrypted_pem_pubkey4(encrypted_pem_pubkey.begin() + 160 + 8 + 742 + 5, encrypted_pem_pubkey.end());
-
         uint32_t imm1 = std::stoul(encrypted_pem_pubkey1.c_str());
         uint32_t imm3 = std::stoul(encrypted_pem_pubkey3.c_str());
 
-        HANDLE hFile = CreateFile(libcc_dll_path, GENERIC_WRITE, NULL, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
-        if (hFile == INVALID_HANDLE_VALUE) {
-            _tprintf_s(TEXT("Failed to open libcc.dll. CODE: 0x%08x @[patcher::Solution1::Do -> CreateFile]\r\n"), GetLastError());
-            return FALSE;
-        }
-        
-        // Start from win8, lpNumberOfBytesWritten parameter in WriteFile can be null if lpOverlapped is null.
-        // But win7 is not. lpNumberOfBytesWritten cannot be null if lpOverlapped is null. 
-        // However MSDN does not mention that.
-        DWORD WrittenBytes;
-
-        // start patch 0
-        _tprintf_s(TEXT("\r\nStart to do patch 0......\r\n"));
-        if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, Patch_Offset[0], nullptr, FILE_BEGIN)) {
-            _tprintf_s(TEXT("Failed to set file pointer. CODE: 0x%08x @[patcher::Solution1::Do -> SetFilePointer]\r\n"), GetLastError());
-            CloseHandle(hFile);
-            return FALSE;
-        }
-        
-        _tprintf_s(TEXT("At offset +0x%08x, write:\r\n\"%hs\"\r\n"), Patch_Offset[0], encrypted_pem_pubkey0.c_str());
-        if (FALSE == WriteFile(hFile, encrypted_pem_pubkey0.c_str(), encrypted_pem_pubkey0.length(), &WrittenBytes, nullptr)) {
-            _tprintf_s(TEXT("Failed to write patch 0. CODE: 0x%08x @[patcher::Solution1::Do -> WriteFile]\r\n"), GetLastError());
-            CloseHandle(hFile);
-            return FALSE;
-        }
+        _tprintf_s(TEXT("\n"));
+        _tprintf_s(TEXT("@Offset +0x%08X, write string:\n"), KeywordOffset[0]);
+        printf_s("\"%s\"\n\n", encrypted_pem_pubkey0.c_str());
+        memcpy((uint8_t*)lpFileContent + KeywordOffset[0], encrypted_pem_pubkey0.c_str(), 160);
 
-        _tprintf_s(TEXT("patch 0 done.....\r\n"));
+        _tprintf_s(TEXT("@Offset +0x%08X, write uint32_t:\n"), KeywordOffset[1]);
+        printf_s("0x%08X\n\n", imm1);
+        memcpy((uint8_t*)lpFileContent + KeywordOffset[1], &imm1, sizeof(uint32_t));
 
-        // start patch 1
-        _tprintf_s(TEXT("\r\nStart to do patch 1.....\r\n"));
-        if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, Patch_Offset[1], nullptr, FILE_BEGIN)) {
-            _tprintf_s(TEXT("Failed to set file pointer. CODE: 0x%08x @[patcher::Solution1::Do -> SetFilePointer]\r\n"), GetLastError());
-            CloseHandle(hFile);
-            return FALSE;
-        }
-
-        _tprintf_s(TEXT("At offset +0x%08x, write immediate value %d (type: uint32_t)\r\n"), Patch_Offset[1], imm1);
-        if (FALSE == WriteFile(hFile, &imm1, sizeof(imm1), &WrittenBytes, nullptr)) {
-            _tprintf_s(TEXT("Failed to write patch 1. CODE: 0x%08x @[patcher::Solution1::Do -> WriteFile]\r\n"), GetLastError());
-            CloseHandle(hFile);
-            return FALSE;
-        }
+        _tprintf_s(TEXT("@Offset +0x%08X, write string:\n"), KeywordOffset[2]);
+        printf_s("\"%s\"\n\n", encrypted_pem_pubkey2.c_str());
+        memcpy((uint8_t*)lpFileContent + KeywordOffset[2], encrypted_pem_pubkey2.c_str(), 742);
 
-        _tprintf_s(TEXT("patch 1 done.....\r\n"));
-
-        // start patch 2
-        _tprintf_s(TEXT("\r\nStart to do patch 2.....\r\n"));
-        if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, Patch_Offset[2], nullptr, FILE_BEGIN)) {
-            _tprintf_s(TEXT("Failed to set file pointer. CODE: 0x%08x @[patcher::Solution1::Do -> SetFilePointer]\r\n"), GetLastError());
-            CloseHandle(hFile);
-            return FALSE;
-        }
-
-        _tprintf_s(TEXT("At offset +0x%08x, write:\r\n\"%hs\"\r\n"), Patch_Offset[2], encrypted_pem_pubkey2.c_str());
-        if (FALSE == WriteFile(hFile, encrypted_pem_pubkey2.c_str(), encrypted_pem_pubkey2.length(), &WrittenBytes, nullptr)) {
-            _tprintf_s(TEXT("Failed to write patch 2. CODE: 0x%08x @[patcher::Solution1::Do -> WriteFile]\r\n"), GetLastError());
-            CloseHandle(hFile);
-            return FALSE;
-        }
+        _tprintf_s(TEXT("@Offset +0x%08X, write uint32_t:\n"), KeywordOffset[3]);
+        printf_s("0x%08X\n\n", imm3);
+        memcpy((uint8_t*)lpFileContent + KeywordOffset[3], &imm3, sizeof(uint32_t));
 
-        _tprintf_s(TEXT("patch 2 done.....\r\n"));
+        _tprintf_s(TEXT("@Offset +0x%08X, write string:\n"), KeywordOffset[4]);
+        printf_s("\"%s\"\n\n", encrypted_pem_pubkey4.c_str());
+        memcpy((uint8_t*)lpFileContent + KeywordOffset[4], encrypted_pem_pubkey4.c_str(), 5);
 
-        // start patch 3
-        _tprintf_s(TEXT("\r\nStart to do patch 3.....\r\n"));
-        if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, Patch_Offset[3], nullptr, FILE_BEGIN)) {
-            _tprintf_s(TEXT("Failed to set file pointer. CODE: 0x%08x @[patcher::Solution1::Do -> SetFilePointer]\r\n"), GetLastError());
-            CloseHandle(hFile);
-            return FALSE;
-        }
+        return TRUE;
+    }
 
-        _tprintf_s(TEXT("At offset +0x%08x, write immediate value %d (type: uint32_t)\r\n"), Patch_Offset[3], imm3);
-        if (FALSE == WriteFile(hFile, &imm3, sizeof(imm3), &WrittenBytes, nullptr)) {
-            _tprintf_s(TEXT("Failed to write patch 3. CODE: 0x%08x @[patcher::Solution1::Do -> WriteFile]\r\n"), GetLastError());
-            CloseHandle(hFile);
-            return FALSE;
+    VOID Finalize() {
+        if (lpFileContent) {
+            UnmapViewOfFile(lpFileContent);
+            lpFileContent = NULL;
         }
-
-        _tprintf_s(TEXT("patch 3 done.....\r\n"));
-
-        // start patch 4
-        _tprintf_s(TEXT("\r\nStart to do patch 4.....\r\n"));
-        if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, Patch_Offset[4], nullptr, FILE_BEGIN)) {
-            _tprintf_s(TEXT("Failed to set file pointer. CODE: 0x%08x @[patcher::Solution1::Do -> SetFilePointer]\r\n"), GetLastError());
-            CloseHandle(hFile);
-            return FALSE;
+        if (hTargetMap) {
+            CloseHandle(hTargetMap);
+            hTargetMap = NULL;
         }
-
-        _tprintf_s(TEXT("At offset +0x%08x, write:\r\n\"%hs\"\r\n"), Patch_Offset[4], encrypted_pem_pubkey4.c_str());
-        if (FALSE == WriteFile(hFile, encrypted_pem_pubkey4.c_str(), encrypted_pem_pubkey4.length(), &WrittenBytes, nullptr)) {
-            _tprintf_s(TEXT("Failed to write patch 4. CODE: 0x%08x @[patcher::Solution1::Do -> WriteFile]\r\n"), GetLastError());
-            CloseHandle(hFile);
-            return FALSE;
+        if (hTarget) {
+            CloseHandle(hTarget);
+            hTarget = INVALID_HANDLE_VALUE;
         }
-
-        _tprintf_s(TEXT("patch 4 done.....\r\n\r\n"));
-
-        return TRUE;
     }
 
 }

+ 144 - 40
navicat-patcher/_tmain.cpp

@@ -1,60 +1,164 @@
 #include "def.hpp"
 
-namespace std {
-#ifdef UNICODE
-    typedef wstring Tstring;
-#else
-    typedef string Tstring;
-#endif
+bool ConvertToUTF8(LPCSTR from, std::string& to) {
+    bool bSuccess = false;
+    int len = 0;
+    LPWSTR lpUnicodeString = nullptr;
+
+    len = MultiByteToWideChar(CP_ACP, NULL, from, -1, NULL, 0);
+    if (len == 0)
+        goto ON_ConvertToUTF8_0_ERROR;
+
+    lpUnicodeString = reinterpret_cast<LPWSTR>(HeapAlloc(GetProcessHeap(),
+                                                         HEAP_ZERO_MEMORY,
+                                                         len * sizeof(WCHAR)));
+    if (lpUnicodeString == nullptr)
+        goto ON_ConvertToUTF8_0_ERROR;
+
+    if (!MultiByteToWideChar(CP_ACP, NULL, from, -1, lpUnicodeString, len))
+        goto ON_ConvertToUTF8_0_ERROR;
+
+    len = WideCharToMultiByte(CP_UTF8, NULL, lpUnicodeString, -1, NULL, 0, NULL, NULL);
+    if (len == 0)
+        goto ON_ConvertToUTF8_0_ERROR;
+
+    to.resize(len);
+    if (!WideCharToMultiByte(CP_UTF8, NULL, lpUnicodeString, -1, to.data(), len, NULL, NULL))
+        goto ON_ConvertToUTF8_0_ERROR;
+
+    while (to.back() == 0)
+        to.pop_back();
+
+    bSuccess = true;
+
+ON_ConvertToUTF8_0_ERROR:
+    if (lpUnicodeString)
+        HeapFree(GetProcessHeap(), NULL, lpUnicodeString);
+    return bSuccess;
+}
+
+bool ConvertToUTF8(LPCWSTR from, std::string& to) {
+    bool bSuccess = false;
+    int len = 0;
+
+    len = WideCharToMultiByte(CP_UTF8, NULL, from, -1, NULL, 0, NULL, NULL);
+    if (len == 0)
+        goto ON_ConvertToUTF8_1_ERROR;
+
+    to.resize(len);
+    if (!WideCharToMultiByte(CP_UTF8, NULL, from, -1, to.data(), len, NULL, NULL))
+        goto ON_ConvertToUTF8_1_ERROR;
+
+    while (to.back() == 0)
+        to.pop_back();
+
+    bSuccess = true;
+
+ON_ConvertToUTF8_1_ERROR:
+    return bSuccess;
+}
+
+bool ConvertToUTF8(std::string& str) {
+    bool bSuccess = false;
+
+    std::string temp;
+    bSuccess = ConvertToUTF8(str.c_str(), temp);
+    if (!bSuccess)
+        return false;
+
+    str = temp;
+    return true;
 }
 
 int _tmain(int argc, TCHAR* argv[]) {
     if (argc != 2 && argc != 3) {
-        _tprintf_s(TEXT("Usage:\r\n"));
-        _tprintf_s(TEXT("    navicat-patcher.exe <Navicat installation path> [RSA-2048 PEM file]\r\n"));
+        _tprintf_s(TEXT("Usage:\n"));
+        _tprintf_s(TEXT("    navicat-patcher.exe <Navicat installation path> [RSA-2048 PEM file]\n"));
         return 0;
     }
 
-    {   // check path validity
-        DWORD attr = GetFileAttributes(argv[1]);
-        if (attr == INVALID_FILE_ATTRIBUTES) {
-            _tprintf_s(TEXT("Failed to get installation path attribute. CODE: 0x%08x @[GetFileAttributes]\r\n"), GetLastError());
-            return 0;
+    RSACipher* cipher = NULL;
+
+    cipher = RSACipher::Create();
+    if (cipher == NULL) {
+        _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+        _tprintf_s(TEXT("RSACipher::Create failed.\n"));
+        goto ON_tmain_ERROR;
+    }
+
+    if (!patcher::Solution0::Init(argv[1]))
+        goto ON_tmain_ERROR;
+    if (!patcher::Solution1::Init(argv[1]))
+        goto ON_tmain_ERROR;
+
+    if (argc == 3) {
+        std::string PrivateKeyFileName;
+
+        if (!ConvertToUTF8(argv[2], PrivateKeyFileName)) {
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("ERROR: ConvertToUTF8 failed.\n"));
+            goto ON_tmain_ERROR;
         }
 
-        if ((attr & FILE_ATTRIBUTE_DIRECTORY) == 0) {
-            _tprintf_s(TEXT("Error: Path is not a directory.\r\n"));
-            return 0;
+        if (!cipher->ImportKeyFromFile<RSACipher::KeyType::PrivateKey, RSACipher::KeyFormat::PEM>(PrivateKeyFileName)) {
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("ERROR: cipher->ImportKeyFromFile failed.\n"));
+            goto ON_tmain_ERROR;
         }
+
+        if (patcher::Solution0::CheckKey(cipher) == FALSE || patcher::Solution1::CheckKey(cipher) == FALSE) {
+            _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+            _tprintf_s(TEXT("ERROR: RSA private key specified cannot be used.\n"));
+            goto ON_tmain_ERROR;
+        }
+    } else {
+        do {
+            cipher->GenerateKey(2048);
+        } while (patcher::Solution0::CheckKey(cipher) && patcher::Solution1::CheckKey(cipher));
+    }
+
+    // ------------------
+    // begin Solution0
+    // ------------------
+    if (!patcher::Solution0::FindTargetFile()) {
+        _tprintf_s(TEXT("@%s LINE: %u\n"), TEXT(__FUNCTION__), __LINE__);
+        _tprintf_s(TEXT("ERROR: Cannot find main program. Are you sure the path you specified is correct?\n"));
+        goto ON_tmain_ERROR;
     }
 
-    std::Tstring navicat_exe_path(argv[1]);
-    if (navicat_exe_path.back() != TEXT('/') && navicat_exe_path.back() != TEXT('\\'))
-        navicat_exe_path.push_back(TEXT('\\'));
-    navicat_exe_path += TEXT("navicat.exe");
+    if (!patcher::Solution0::CheckFile()) 
+        goto ON_tmain_ERROR;
 
-    std::Tstring libcc_dll_path(argv[1]);
-    if (libcc_dll_path.back() != TEXT('/') && libcc_dll_path.back() != TEXT('\\'))
-        libcc_dll_path.push_back(TEXT('\\'));
-    libcc_dll_path += TEXT("libcc.dll");
+    if (!patcher::Solution0::BackupFile())
+        goto ON_tmain_ERROR;
 
-    DWORD NavicatMajorVersion;
-    DWORD NavicatMinorVersion;
-    if (!patcher::GetNavicatVerion(navicat_exe_path.c_str(), &NavicatMajorVersion, &NavicatMinorVersion))
-        return 0;
+    if (!patcher::Solution0::Do(cipher))
+        goto ON_tmain_ERROR;
     
-    BOOL status;
-    if (NavicatMajorVersion <= 0x000C0000 && NavicatMinorVersion < 0x00190000) {                // for navicat ver < 12.0.25
-        status = patcher::Solution0::Do(navicat_exe_path.c_str());
-    } else if (NavicatMajorVersion == 0x000C0000 && NavicatMinorVersion <= 0x001C0000) {        // for navicat ver <= 12.0.28
-        status = patcher::Solution1::Do(libcc_dll_path.c_str(), argc == 3 ? argv[2] : nullptr);
-    } else {
-        _tprintf_s(TEXT("Warning: Unknown version detected, still continue? [y/n] "));
-        TCHAR selection = _gettchar();
-        if (selection == 'Y' || selection == 'y')
-            status = patcher::Solution1::Do(libcc_dll_path.c_str(), argc == 3 ? argv[2] : nullptr);
-    }
+    _tprintf_s(TEXT("Solution0 has been done successfully.\n"));
+    _tprintf_s(TEXT("\n"));
+
+    // ------------------
+    // begin Solution1
+    // ------------------
+    if (!patcher::Solution1::FindTargetFile())
+        goto ON_tmain_ERROR;
+
+    if (!patcher::Solution1::FindOffset())
+        goto ON_tmain_ERROR;
+
+    if (!patcher::Solution1::BackupFile())
+        goto ON_tmain_ERROR;
+
+    if (!patcher::Solution1::Do(cipher))
+        goto ON_tmain_ERROR;
+
+    _tprintf_s(TEXT("Solution1 has been done successfully.\n"));
+    _tprintf_s(TEXT("\n"));
 
-    _tprintf_s(TEXT("%s\r\n"), status == TRUE ? TEXT("Success!") : TEXT("Failed!"));
+ON_tmain_ERROR:
+    patcher::Solution1::Finalize();
+    patcher::Solution0::Finalize();
+    delete cipher;
     return 0;
 }

+ 0 - 249
navicat-patcher/common.cpp

@@ -1,249 +0,0 @@
-#include "def.hpp"
-
-namespace patcher {
-
-    BOOL BackupFile(LPCTSTR file_path) {
-        size_t file_path_len = _tcslen(file_path);
-
-        TCHAR* backup_file_path = new TCHAR[file_path_len + 16]();
-        _stprintf_s(backup_file_path, file_path_len + 16, TEXT("%s%s"), file_path, TEXT(".backup"));
-
-        if (!CopyFile(file_path, backup_file_path, TRUE)) {
-            switch (GetLastError()) {
-                case ERROR_FILE_NOT_FOUND:
-                    _tprintf_s(TEXT("@[BackupFile]: Cannot find %s.\r\n"), file_path);
-                    break;
-                case ERROR_FILE_EXISTS:
-                    _tprintf_s(TEXT("@[BackupFile]: Backup file already exists.\r\n"));
-                    break;
-                case ERROR_ACCESS_DENIED:
-                    _tprintf_s(TEXT("@[BackupFile]: Access denied, please run as administrator.\r\n"));
-                    break;
-                default:
-                    _tprintf_s(TEXT("Unknown error. CODE: 0x%08x @[BackupFile -> CopyFile]\r\n"), GetLastError());
-            }
-            delete[] backup_file_path;
-            return FALSE;
-        }
-
-        _tprintf_s(TEXT("@[BackupFile]: %s has been backed up.\r\n"), file_path);
-        delete[] backup_file_path;
-        return TRUE;
-    }
-
-    RSA* GenerateRSAKey(int bits) {
-        RSA* ret = RSA_generate_key(bits, RSA_F4, nullptr, nullptr);
-        if (ret == nullptr) {
-            _tprintf_s(TEXT("Failed to generate RSA key. CODE: 0x%08x @[GenerateRSAKey -> RSA_generate_key]\r\n"), ERR_get_error());
-            return nullptr;
-        }
-        return ret;
-    }
-
-    BOOL WriteRSAPrivateKeyToFile(LPCTSTR filename, RSA* PrivateKey) {
-#ifdef UNICODE
-        int req_size = WideCharToMultiByte(CP_ACP, 0, filename, -1, nullptr, 0, nullptr, nullptr);
-        if (req_size == 0) {
-            _tprintf_s(TEXT("Failed to convert wchar* to char*. CODE: 0x%08x @[WriteRSAPrivateKeyToFile -> WideCharToMultiByte]\r\n"), GetLastError());
-            return FALSE;
-        }
-
-        char* temp_filename = new char[req_size]();
-        WideCharToMultiByte(CP_ACP, 0, filename, -1, temp_filename, req_size, nullptr, nullptr);
-
-        BIO* b = BIO_new(BIO_s_file());
-        if (b == nullptr) {
-            _tprintf_s(TEXT("Failed to create BIO object. CODE: 0x%08x @[WriteRSAPrivateKeyToFile -> BIO_new]\r\n"), ERR_get_error());
-            delete[] temp_filename;
-            return FALSE;
-        }
-
-        if (1 != BIO_write_filename(b, temp_filename)) {
-            _tprintf_s(TEXT("Failed to set target file of BIO. CODE: 0x%08x @[WriteRSAPrivateKeyToFile -> BIO_write_filename]\r\n"), ERR_get_error());
-
-            BIO_free_all(b);
-            delete[] temp_filename;
-            return FALSE;
-        }
-
-        delete[] temp_filename;
-#else
-        BIO* b = BIO_new(BIO_s_file());
-        if (b == nullptr) {
-            _tprintf_s(TEXT("Failed to create BIO object. CODE: 0x%08x @[WriteRSAPrivateKeyToFile -> BIO_new]\r\n"), ERR_get_error());
-            return FALSE;
-        }
-
-        if (1 != BIO_write_filename(b, filename)) {
-            _tprintf_s(TEXT("Failed to set target file of BIO. CODE: 0x%08x @[WriteRSAPrivateKeyToFile -> BIO_write_filename]\r\n"), ERR_get_error());
-
-            BIO_free_all(b);
-            return FALSE;
-        }
-#endif
-        if (1 != PEM_write_bio_RSAPrivateKey(b, PrivateKey, nullptr, nullptr, 0, nullptr, nullptr)) {
-            _tprintf_s(TEXT("Failed to write RSA private key. CODE: 0x%08x @[WriteRSAPrivateKeyToFile -> PEM_write_bio_RSAPrivateKey]\r\n"), ERR_get_error());
-
-            BIO_free_all(b);
-            return FALSE;
-        } else {
-            BIO_free_all(b);
-            return TRUE;
-        }
-    }
-
-    RSA* ReadRSAPrivateKeyFromFile(LPCTSTR filename) {
-#ifdef UNICODE
-        int req_size = WideCharToMultiByte(CP_ACP, 0, filename, -1, nullptr, 0, nullptr, nullptr);
-        if (req_size == 0) {
-            _tprintf_s(TEXT("Failed to convert wchar* to char*. CODE: 0x%08x @[ReadRSAPrivateKeyFromFile -> WideCharToMultiByte]\r\n"), GetLastError());
-            return FALSE;
-        }
-
-        char* temp_filename = new char[req_size]();
-        WideCharToMultiByte(CP_ACP, 0, filename, -1, temp_filename, req_size, nullptr, nullptr);
-
-        BIO* b = BIO_new(BIO_s_file());
-        if (b == nullptr) {
-            _tprintf_s(TEXT("Failed to create BIO object. CODE: 0x%08x @[ReadRSAPrivateKeyFromFile -> BIO_new]\r\n"), ERR_get_error());
-            delete[] temp_filename;
-            return FALSE;
-        }
-
-        if (1 != BIO_read_filename(b, temp_filename)) {
-            _tprintf_s(TEXT("Failed to set target file of BIO. CODE: 0x%08x @[ReadRSAPrivateKeyFromFile -> BIO_read_filename]\r\n"), ERR_get_error());
-
-            BIO_free_all(b);
-            delete[] temp_filename;
-            return FALSE;
-        }
-
-        delete[] temp_filename;
-#else
-        BIO* b = BIO_new(BIO_s_file());
-        if (b == nullptr) {
-            _tprintf_s(TEXT("Failed to create BIO object. CODE: 0x%08x @[ReadRSAPrivateKeyFromFile -> BIO_new]\r\n"), ERR_get_error());
-            return FALSE;
-        }
-
-        if (1 != BIO_read_filename(b, filename)) {
-            _tprintf_s(TEXT("Failed to set target file of BIO. CODE: 0x%08x @[ReadRSAPrivateKeyFromFile -> BIO_read_filename]\r\n"), ERR_get_error());
-
-            BIO_free_all(b);
-            return FALSE;
-        }
-#endif
-        RSA* ret = PEM_read_bio_RSAPrivateKey(b, nullptr, nullptr, nullptr);
-        if (ret == nullptr) {
-            _tprintf_s(TEXT("Failed to read RSA private key. CODE: 0x%08x @[ReadRSAPrivateKeyFromFile -> PEM_read_bio_RSAPrivateKey]\r\n"), ERR_get_error());
-
-            BIO_free_all(b);
-            return nullptr;
-        } else {
-            BIO_free_all(b);
-            return ret;
-        }
-    }
-
-    BOOL GetNavicatVerion(LPCTSTR exe_path, DWORD* major_ver, DWORD* minor_ver) {
-        DWORD FileVersionInfoSize = GetFileVersionInfoSize(exe_path, nullptr);
-        if (FileVersionInfoSize == 0) {
-            _tprintf_s(TEXT("Failed to get navicat.exe verion info. CODE: 0x%08x @[GetNavicatVerion -> GetFileVersionInfoSize]\r\n"), GetLastError());
-            return FALSE;
-        }
-        LPVOID buf = new unsigned char[FileVersionInfoSize]();
-        if (FALSE == GetFileVersionInfo(exe_path, 0, FileVersionInfoSize, buf)) {
-            _tprintf_s(TEXT("Failed to get navicat.exe verion info. CODE: 0x%08x @[GetNavicatVerion -> GetFileVersionInfo]\r\n"), GetLastError());
-            delete[] buf;
-            return FALSE;
-        }
-
-        LPVOID info_ptr;
-        UINT info_len;
-        if (FALSE == VerQueryValue(buf, TEXT("\\"), &info_ptr, &info_len)) {
-            _tprintf_s(TEXT("Failed to get navicat.exe verion info. CODE: 0x%08x @[GetNavicatVerion -> VerQueryValue]\r\n"), GetLastError());
-            delete[] buf;
-            return FALSE;
-        }
-
-        *major_ver = static_cast<VS_FIXEDFILEINFO*>(info_ptr)->dwFileVersionMS;
-        *minor_ver = static_cast<VS_FIXEDFILEINFO*>(info_ptr)->dwFileVersionLS;
-        delete[] buf;
-        return TRUE;
-    }
-
-    BOOL Check_libcc_Hash(LPCTSTR libcc_dll_path, const uint8_t expected_hash[SHA256_DIGEST_LENGTH]) {
-        HANDLE hFile = CreateFile(libcc_dll_path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
-        if (hFile == INVALID_HANDLE_VALUE) {
-            _tprintf_s(TEXT("Failed to open libcc.dll. CODE: 0x%08x @[Check_libcc_Hash -> CreateFile]\r\n"), GetLastError());
-            return FALSE;
-        }
-
-        LARGE_INTEGER FileSize;
-        if (FALSE == GetFileSizeEx(hFile, &FileSize)) {
-            _tprintf_s(TEXT("Failed to get libcc.dll size. CODE: 0x%08x @[Check_libcc_Hash -> GetFileSizeEx]\r\n"), GetLastError());
-            CloseHandle(hFile);
-            return FALSE;
-        }
-
-        HANDLE hFileMap = CreateFileMapping(hFile, nullptr, PAGE_READONLY, 0, 0, nullptr);
-        if (hFileMap == NULL) {
-            _tprintf_s(TEXT("Failed to create mapping for libcc.dll. CODE: 0x%08x @[Check_libcc_Hash -> CreateFileMapping]\r\n"), GetLastError());
-            CloseHandle(hFile);
-            return FALSE;
-        }
-
-        const uint8_t* libcc_data = reinterpret_cast<const uint8_t*>(MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0));
-        if (libcc_data == nullptr) {
-            _tprintf_s(TEXT("Failed to map libcc.dll. CODE: 0x%08x @[Check_libcc_Hash -> MapViewOfFile]\r\n"), GetLastError());
-            CloseHandle(hFileMap);
-            CloseHandle(hFile);
-            return FALSE;
-        }
-
-        uint8_t real_hash[SHA256_DIGEST_LENGTH] = { };
-
-        SHA256(libcc_data, FileSize.LowPart, real_hash);
-        UnmapViewOfFile(libcc_data);
-        CloseHandle(hFileMap);
-        CloseHandle(hFile);
-
-        if (memcmp(expected_hash, real_hash, SHA256_DIGEST_LENGTH) != 0) {
-            _tprintf_s(TEXT("ERROR: SHA256 do not match.\r\n"));
-            return FALSE;
-        }
-
-        return TRUE;
-    }
-
-    char* GetPEMText(RSA* PrivateKey) {
-        BIO* bio = BIO_new(BIO_s_mem());
-        if (bio == nullptr) {
-            _tprintf_s(TEXT("Cannot create BIO object. CODE: 0x%08x @[GetPEMText -> BIO_new]\r\n"), ERR_get_error());
-            return nullptr;
-        }
-
-        if (1 != PEM_write_bio_RSA_PUBKEY(bio, PrivateKey)) {
-            _tprintf_s(TEXT("Cannot write RSA-2048 public key. CODE: 0x%08x @[GetPEMText -> PEM_write_bio_RSA_PUBKEY]\r\n"), ERR_get_error());
-            BIO_free_all(bio);
-            return nullptr;
-        }
-
-        char* pem_data_ptr;
-        int pem_data_len = BIO_get_mem_data(bio, &pem_data_ptr);
-
-        char* ret = new char[pem_data_len + 256]();
-        for (int i = 0, j = 0; i < pem_data_len; ++i, ++j) {
-            if (pem_data_ptr[i] == '\n') {
-                ret[j++] = '\r';
-                ret[j] = pem_data_ptr[i];
-            } else {
-                ret[j] = pem_data_ptr[i];
-            }
-        }
-
-        BIO_free_all(bio);
-        return ret;
-    }
-
-}

+ 22 - 32
navicat-patcher/def.hpp

@@ -3,49 +3,39 @@
 #include <windows.h>
 #include <string>
 
-#include <openssl/err.h>
-#include <openssl/bio.h>
-#include <openssl/rsa.h>
-#include <openssl/pem.h>
+#include "RSACipher.hpp"
+#pragma comment(lib, "version.lib")     // GetFileVersionInfoSize, GetFileVersionInfo, VerQueryValue are in this lib
 
-// OpenSSL precompiled lib, download from https://www.npcglib.org/~stathis/blog/precompiled-openssl/, MSVC2015 version
-// direct link https://www.npcglib.org/~stathis/downloads/openssl-1.1.0f-vs2015.7z
-// x86: "D:\openssl-1.1.0f-vs2015\include" has been add to include path.    (modify it at project properties if necessary)
-//      "D:\openssl-1.1.0f-vs2015\lib" has been add to library path.        (modify it at project properties if necessary)
-// x64: "D:\openssl-1.1.0f-vs2015\include64" has been add to include path.  (modify it at project properties if necessary)
-//      "D:\openssl-1.1.0f-vs2015\lib64" has been add to library path.      (modify it at project properties if necessary)
-#ifdef _DEBUG
-#pragma comment(lib, "libcryptoMTd.lib")
+namespace std {
+#ifdef UNICODE
+    typedef wstring Tstring;
 #else
-#pragma comment(lib, "libcryptoMT.lib")
-#endif
-#pragma comment(lib, "WS2_32.lib")      // some symbol are used in OpenSSL static lib
-#pragma comment(lib, "Crypt32.lib")     // some symbol are used in OpenSSL static lib
-#pragma comment(lib, "version.lib")     // GetFileVersionInfoSize, GetFileVersionInfo, VerQueryValue are in this lib
+    typedef string Tstring;
+#endif // UNICODE
+}
 
 namespace patcher {
 
-    BOOL BackupFile(LPCTSTR file_path);
-
-    RSA* GenerateRSAKey(int bits = 2048);
-
-    BOOL WriteRSAPrivateKeyToFile(LPCTSTR filename, RSA* PrivateKey);
-    RSA* ReadRSAPrivateKeyFromFile(LPCTSTR filename);
-
-    BOOL GetNavicatVerion(LPCTSTR exe_path, DWORD* major_ver, DWORD* minor_ver);
-
     std::string EncryptPublicKey(const char* public_key, size_t len);
 
-    BOOL Check_libcc_Hash(LPCTSTR libcc_dll_path, const uint8_t expected_hash[SHA256_DIGEST_LENGTH]);
-
-    char* GetPEMText(RSA* PrivateKey);
-
     namespace Solution0 {
-        BOOL Do(LPCTSTR navicat_exe_path, LPCTSTR prepared_key_file = nullptr);
+        BOOL Init(const std::Tstring& Path);
+        BOOL CheckKey(RSACipher* cipher);
+        BOOL FindTargetFile();
+        BOOL CheckFile();
+        BOOL BackupFile();
+        BOOL Do(RSACipher* cipher);
+        VOID Finalize();
     }
 
     namespace Solution1 {
-        BOOL Do(LPCTSTR libcc_dll_path, LPCTSTR prepared_key_file = nullptr);
+        BOOL Init(const std::Tstring& Path);
+        BOOL CheckKey(RSACipher* cipher);
+        BOOL FindTargetFile();
+        BOOL FindOffset();
+        BOOL BackupFile();
+        BOOL Do(RSACipher* cipher);
+        VOID Finalize();
     }
 
 }

+ 9 - 9
navicat-patcher/navicat-patcher.vcxproj

@@ -71,23 +71,23 @@
   <PropertyGroup Label="UserMacros" />
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <LinkIncremental>true</LinkIncremental>
-    <IncludePath>D:\openssl-1.1.0f-vs2015\include;$(IncludePath)</IncludePath>
-    <LibraryPath>D:\openssl-1.1.0f-vs2015\lib;$(LibraryPath)</LibraryPath>
+    <IncludePath>$(SolutionDir)openssl-lib\include;$(IncludePath)</IncludePath>
+    <LibraryPath>$(SolutionDir)openssl-lib\lib;$(LibraryPath)</LibraryPath>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
     <LinkIncremental>true</LinkIncremental>
-    <IncludePath>D:\openssl-1.1.0f-vs2015\include64;$(IncludePath)</IncludePath>
-    <LibraryPath>D:\openssl-1.1.0f-vs2015\lib64;$(LibraryPath)</LibraryPath>
+    <IncludePath>$(SolutionDir)openssl-lib\include64;$(IncludePath)</IncludePath>
+    <LibraryPath>$(SolutionDir)openssl-lib\lib64;$(LibraryPath)</LibraryPath>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <LinkIncremental>false</LinkIncremental>
-    <IncludePath>D:\openssl-1.1.0f-vs2015\include;$(IncludePath)</IncludePath>
-    <LibraryPath>D:\openssl-1.1.0f-vs2015\lib;$(LibraryPath)</LibraryPath>
+    <IncludePath>$(SolutionDir)openssl-lib\include;$(IncludePath)</IncludePath>
+    <LibraryPath>$(SolutionDir)openssl-lib\lib;$(LibraryPath)</LibraryPath>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
     <LinkIncremental>false</LinkIncremental>
-    <IncludePath>D:\openssl-1.1.0f-vs2015\include64;$(IncludePath)</IncludePath>
-    <LibraryPath>D:\openssl-1.1.0f-vs2015\lib64;$(LibraryPath)</LibraryPath>
+    <IncludePath>$(SolutionDir)openssl-lib\include64;$(IncludePath)</IncludePath>
+    <LibraryPath>$(SolutionDir)openssl-lib\lib64;$(LibraryPath)</LibraryPath>
   </PropertyGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
     <ClCompile>
@@ -162,7 +162,6 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
-    <ClCompile Include="common.cpp" />
     <ClCompile Include="EncryptPublicKey.cpp" />
     <ClCompile Include="NavicatCrypto\aes.c" />
     <ClCompile Include="NavicatCrypto\aes_constant.c" />
@@ -178,6 +177,7 @@
     <ClInclude Include="NavicatCrypto\blowfish.h" />
     <ClInclude Include="NavicatCrypto\NavicatCrypto.hpp" />
     <ClInclude Include="NavicatCrypto\sha1.h" />
+    <ClInclude Include="RSACipher.hpp" />
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">

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

@@ -36,9 +36,6 @@
     <ClCompile Include="Solution0.cpp">
       <Filter>源文件</Filter>
     </ClCompile>
-    <ClCompile Include="common.cpp">
-      <Filter>源文件</Filter>
-    </ClCompile>
     <ClCompile Include="Solution1.cpp">
       <Filter>源文件</Filter>
     </ClCompile>
@@ -59,5 +56,8 @@
     <ClInclude Include="def.hpp">
       <Filter>头文件</Filter>
     </ClInclude>
+    <ClInclude Include="RSACipher.hpp">
+      <Filter>头文件</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>