├── .gitignore ├── navicat-patcher ├── KeystoneAssembler.hpp ├── ResourceTraitsUnix.hpp ├── ResourceTraitsCapstone.hpp ├── ResourceTraitsKeystone.hpp ├── ExceptionCapstone.hpp ├── ExceptionKeystone.hpp ├── CapstoneDisassembler.hpp ├── KeystoneAssembler.cpp ├── HelperIsResolvedTo.cpp ├── PatchSolution0.cpp ├── HelperPrintMemory.cpp ├── CapstoneDisassembler.cpp ├── PatchSolutions.hpp ├── X64ImageInterpreter.cpp ├── PatchSolution1.cpp ├── X64ImageInterpreter.hpp ├── PatchSolution2.cpp └── main.cpp ├── common ├── ExceptionSystem.hpp ├── ExceptionOpenssl.hpp ├── Exception.hpp ├── ResourceTraitsOpenssl.hpp ├── ResourceOwned.hpp └── RSACipher.hpp ├── navicat-keygen ├── DESCipher.hpp ├── Base64.cpp ├── NavicatKeygen.hpp └── main.cpp ├── Makefile ├── HOW_DOES_IT_WORK.zh-CN.md ├── README.zh-CN.md ├── HOW_DOES_IT_WORK.md ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | bin/* -------------------------------------------------------------------------------- /navicat-patcher/KeystoneAssembler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "../common/ResourceOwned.hpp" 4 | #include "ResourceTraitsKeystone.hpp" 5 | #include 6 | #include 7 | 8 | class KeystoneAssembler { 9 | private: 10 | 11 | ResourceOwned pvt_Engine; 12 | 13 | KeystoneAssembler() noexcept : 14 | pvt_Engine(KeystoneHandleTraits{}) {} 15 | public: 16 | 17 | [[nodiscard]] 18 | static KeystoneAssembler Create(ks_arch ArchType, ks_mode Mode); 19 | 20 | void Option(ks_opt_type Type, size_t Value); 21 | 22 | [[nodiscard]] 23 | std::vector GenerateOpcode(const char* AssemblyCode, uint64_t Address = 0) const; 24 | 25 | [[nodiscard]] 26 | std::vector GenerateOpcode(const std::string& AssemblyCode, uint64_t Address = 0) const; 27 | }; 28 | 29 | -------------------------------------------------------------------------------- /navicat-patcher/ResourceTraitsUnix.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include // NOLINT 3 | #include 4 | #include 5 | #include "../common/ExceptionSystem.hpp" 6 | 7 | struct FileHandleTraits { 8 | using HandleType = int; 9 | 10 | static inline const HandleType InvalidValue = -1; 11 | 12 | [[nodiscard]] 13 | static bool IsValid(const HandleType& Handle) noexcept { 14 | return Handle != InvalidValue; 15 | } 16 | 17 | static void Releasor(const HandleType& Handle) { 18 | if (close(Handle) != 0) { 19 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 20 | throw nkg::SystemError(__FILE__, __LINE__, errno, "close failed."); 21 | } 22 | } 23 | }; 24 | 25 | struct MapViewTraits { 26 | using HandleType = void*; 27 | 28 | static inline const HandleType InvalidValue = MAP_FAILED; 29 | 30 | [[nodiscard]] 31 | static bool IsValid(const HandleType& Handle) noexcept { 32 | return Handle != InvalidValue; 33 | } 34 | }; 35 | 36 | -------------------------------------------------------------------------------- /common/ExceptionSystem.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Exception.hpp" 3 | #include // NOLINT 4 | 5 | namespace nkg { 6 | 7 | class SystemError final : public Exception { 8 | private: 9 | 10 | int pvt_ErrorCode; 11 | 12 | public: 13 | 14 | SystemError(const char* File, unsigned Line, int ErrorCode, const char* Message) noexcept : 15 | Exception(File, Line, Message), 16 | pvt_ErrorCode(ErrorCode) {} 17 | 18 | [[nodiscard]] 19 | // NOLINTNEXTLINE: mark "virtual" explicitly for more readability 20 | virtual bool HasErrorCode() const noexcept override { 21 | return true; 22 | } 23 | 24 | [[nodiscard]] 25 | // NOLINTNEXTLINE: mark "virtual" explicitly for more readability 26 | virtual intptr_t ErrorCode() const noexcept override { 27 | return pvt_ErrorCode; 28 | } 29 | 30 | [[nodiscard]] 31 | // NOLINTNEXTLINE: mark "virtual" explicitly for more readability 32 | virtual const char* ErrorString() const noexcept override { 33 | return strerror(pvt_ErrorCode); 34 | } 35 | }; 36 | 37 | } -------------------------------------------------------------------------------- /navicat-patcher/ResourceTraitsCapstone.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "ExceptionCapstone.hpp" 4 | 5 | struct CapstoneHandleTraits { 6 | using HandleType = csh; 7 | 8 | static inline const HandleType InvalidValue = 0; 9 | 10 | [[nodiscard]] 11 | static bool IsValid(const HandleType& Handle) noexcept { 12 | return Handle != InvalidValue; 13 | } 14 | 15 | static void Releasor(HandleType& Handle) { 16 | auto err = cs_close(&Handle); 17 | if (err != CS_ERR_OK) { 18 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 19 | throw nkg::CapstoneError(__FILE__, __LINE__, err, "ks_close failed."); 20 | } 21 | } 22 | }; 23 | 24 | struct CapstoneInsnTraits { 25 | using HandleType = cs_insn*; 26 | 27 | static inline const HandleType InvalidValue = nullptr; 28 | 29 | [[nodiscard]] 30 | static bool IsValid(const HandleType& Handle) noexcept { 31 | return Handle != InvalidValue; 32 | } 33 | 34 | static void Releasor(const HandleType& Handle) noexcept { 35 | cs_free(Handle, 1); 36 | } 37 | }; 38 | 39 | -------------------------------------------------------------------------------- /navicat-patcher/ResourceTraitsKeystone.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "ExceptionKeystone.hpp" 4 | 5 | struct KeystoneHandleTraits { 6 | using HandleType = ks_engine*; 7 | 8 | static inline const HandleType InvalidValue = nullptr; 9 | 10 | [[nodiscard]] 11 | static bool IsValid(const HandleType& Handle) noexcept { 12 | return Handle != InvalidValue; 13 | } 14 | 15 | static void Releasor(const HandleType& Handle) { 16 | auto err = ks_close(Handle); 17 | if (err != KS_ERR_OK) { 18 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 19 | throw nkg::KeystoneError(__FILE__, __LINE__, err, "ks_close failed."); 20 | } 21 | } 22 | }; 23 | 24 | struct KeystoneMallocTraits { 25 | using HandleType = uint8_t*; 26 | 27 | static inline const HandleType InvalidValue = nullptr; 28 | 29 | [[nodiscard]] 30 | static bool IsValid(const HandleType& Handle) noexcept { 31 | return Handle != InvalidValue; 32 | } 33 | 34 | static void Releasor(const HandleType& Handle) noexcept { 35 | ks_free(Handle); 36 | } 37 | }; 38 | 39 | -------------------------------------------------------------------------------- /navicat-patcher/ExceptionCapstone.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../common/Exception.hpp" 3 | #include 4 | 5 | namespace nkg { 6 | 7 | class CapstoneError final : public Exception { 8 | private: 9 | 10 | cs_err pvt_ErrorCode; 11 | 12 | public: 13 | 14 | CapstoneError(const char* File, unsigned Line, cs_err ErrorCode, const char* Message) noexcept : 15 | Exception(File, Line, Message), 16 | pvt_ErrorCode(ErrorCode) {} 17 | 18 | [[nodiscard]] 19 | // NOLINTNEXTLINE: mark "virtual" explicitly for more readability 20 | virtual bool HasErrorCode() const noexcept override { 21 | return true; 22 | } 23 | 24 | [[nodiscard]] 25 | // NOLINTNEXTLINE: mark "virtual" explicitly for more readability 26 | virtual intptr_t ErrorCode() const noexcept override { 27 | return pvt_ErrorCode; 28 | } 29 | 30 | [[nodiscard]] 31 | // NOLINTNEXTLINE: mark "virtual" explicitly for more readability 32 | virtual const char* ErrorString() const noexcept override { 33 | return cs_strerror(pvt_ErrorCode); 34 | } 35 | }; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /navicat-patcher/ExceptionKeystone.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../common/Exception.hpp" 3 | #include 4 | 5 | namespace nkg { 6 | 7 | class KeystoneError final : public Exception { 8 | private: 9 | 10 | ks_err pvt_ErrorCode; 11 | 12 | public: 13 | 14 | KeystoneError(const char* File, unsigned Line, ks_err ErrorCode, const char* Message) noexcept : 15 | Exception(File, Line, Message), 16 | pvt_ErrorCode(ErrorCode) {} 17 | 18 | [[nodiscard]] 19 | // NOLINTNEXTLINE: mark "virtual" explicitly for more readability 20 | virtual bool HasErrorCode() const noexcept override { 21 | return true; 22 | } 23 | 24 | [[nodiscard]] 25 | // NOLINTNEXTLINE: mark "virtual" explicitly for more readability 26 | virtual intptr_t ErrorCode() const noexcept override { 27 | return pvt_ErrorCode; 28 | } 29 | 30 | [[nodiscard]] 31 | // NOLINTNEXTLINE: mark "virtual" explicitly for more readability 32 | virtual const char* ErrorString() const noexcept override { 33 | return ks_strerror(pvt_ErrorCode); 34 | } 35 | }; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /navicat-keygen/DESCipher.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | class DESCipher { 7 | private: 8 | DES_cblock _Key; 9 | DES_key_schedule _Schedule; 10 | public: 11 | 12 | DESCipher() noexcept : _Key{}, _Schedule{} {} 13 | 14 | void SetKey(const void* pKey) noexcept { 15 | memcpy(&_Key, pKey, sizeof(_Key)); 16 | DES_set_odd_parity(&_Key); 17 | DES_set_key(&_Key, &_Schedule); 18 | } 19 | 20 | void Clear() noexcept { 21 | OPENSSL_cleanse(&_Key, sizeof(_Key)); 22 | OPENSSL_cleanse(&_Schedule, sizeof(_Schedule)); 23 | } 24 | 25 | void EncryptBlock(void* lpBuffer) noexcept { 26 | DES_cblock block; 27 | DES_ecb_encrypt(reinterpret_cast(lpBuffer), &block, &_Schedule, DES_ENCRYPT); 28 | memcpy(lpBuffer, &block, sizeof(block)); 29 | } 30 | 31 | void DecryptBlock(void* lpBuffer) noexcept { 32 | DES_cblock block; 33 | DES_ecb_encrypt(reinterpret_cast(lpBuffer), &block, &_Schedule, DES_DECRYPT); 34 | memcpy(lpBuffer, &block, sizeof(block)); 35 | } 36 | 37 | ~DESCipher() noexcept { 38 | Clear(); 39 | } 40 | }; 41 | 42 | -------------------------------------------------------------------------------- /common/ExceptionOpenssl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "Exception.hpp" 4 | 5 | namespace nkg { 6 | 7 | class OpensslError final : public Exception { 8 | private: 9 | 10 | unsigned long pvt_ErrorCode; 11 | 12 | public: 13 | 14 | OpensslError(const char* File, unsigned Line, unsigned long ErrorCode, const char* Message) noexcept : 15 | Exception(File, Line, Message), 16 | pvt_ErrorCode(ErrorCode) {} 17 | 18 | [[nodiscard]] 19 | // NOLINTNEXTLINE: mark "virtual" explicitly for more readability 20 | virtual bool HasErrorCode() const noexcept override { 21 | return true; 22 | } 23 | 24 | [[nodiscard]] 25 | // NOLINTNEXTLINE: mark "virtual" explicitly for more readability 26 | virtual intptr_t ErrorCode() const noexcept override { 27 | return pvt_ErrorCode; 28 | } 29 | 30 | [[nodiscard]] 31 | // NOLINTNEXTLINE: mark "virtual" explicitly for more readability 32 | virtual const char* ErrorString() const noexcept override { 33 | ERR_load_crypto_strings(); 34 | return ERR_reason_error_string(pvt_ErrorCode); 35 | } 36 | }; 37 | 38 | } 39 | 40 | 41 | -------------------------------------------------------------------------------- /common/Exception.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include // NOLINT 3 | #include // NOLINT 4 | 5 | namespace nkg { 6 | 7 | class Exception { 8 | private: 9 | 10 | const char* pvt_File; 11 | const char* pvt_Message; 12 | size_t pvt_Line; 13 | 14 | public: 15 | 16 | Exception(const char* File, size_t Line, const char* Message) noexcept : 17 | pvt_File(File), 18 | pvt_Message(Message), 19 | pvt_Line(Line) {} 20 | 21 | [[nodiscard]] 22 | const char* File() const noexcept { 23 | return pvt_File; 24 | } 25 | 26 | [[nodiscard]] 27 | size_t Line() const noexcept { 28 | return pvt_Line; 29 | } 30 | 31 | [[nodiscard]] 32 | const char* Message() const noexcept { 33 | return pvt_Message; 34 | } 35 | 36 | [[nodiscard]] 37 | virtual bool HasErrorCode() const noexcept { 38 | return false; 39 | } 40 | 41 | [[nodiscard]] 42 | virtual intptr_t ErrorCode() const noexcept { 43 | return 0; 44 | } 45 | 46 | [[nodiscard]] 47 | virtual const char* ErrorString() const noexcept { 48 | return nullptr; 49 | } 50 | }; 51 | 52 | } 53 | 54 | -------------------------------------------------------------------------------- /navicat-patcher/CapstoneDisassembler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "../common/ResourceOwned.hpp" 4 | #include "ResourceTraitsCapstone.hpp" 5 | 6 | class CapstoneDisassembler { 7 | public: 8 | 9 | struct Context { 10 | const uint8_t* pbOpcode; 11 | size_t cbOpcode; 12 | uint64_t Address; 13 | }; 14 | 15 | private: 16 | 17 | ResourceOwned pvt_Handle; 18 | ResourceOwned pvt_Insn; 19 | Context pvt_CurrentCtx; 20 | cs_insn* pvt_CurrentInsn; 21 | 22 | CapstoneDisassembler() noexcept : 23 | pvt_Handle(CapstoneHandleTraits{}), 24 | pvt_Insn(CapstoneInsnTraits{}), 25 | pvt_CurrentCtx{}, 26 | pvt_CurrentInsn(nullptr) {} 27 | public: 28 | 29 | [[nodiscard]] 30 | static CapstoneDisassembler Create(cs_arch ArchType, cs_mode Mode); 31 | 32 | void Option(cs_opt_type Type, size_t Value); 33 | 34 | void SetContext(uintptr_t lpOpcode, size_t cbOpcode, uint64_t Address = 0) noexcept ; 35 | 36 | void SetContext(const void* lpOpcode, size_t cbOpcode, uint64_t Address = 0) noexcept; 37 | 38 | [[nodiscard]] 39 | const Context& GetContext() const noexcept; 40 | 41 | [[nodiscard]] 42 | const cs_insn* GetInstruction() const noexcept; 43 | 44 | [[nodiscard]] 45 | Context GetInstructionContext() const noexcept; 46 | 47 | [[nodiscard]] 48 | bool Next() noexcept; 49 | }; 50 | -------------------------------------------------------------------------------- /common/ResourceTraitsOpenssl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | struct OpensslBIOTraits { 6 | using HandleType = BIO*; 7 | 8 | static inline const HandleType InvalidValue = nullptr; 9 | 10 | [[nodiscard]] 11 | static bool IsValid(const HandleType& Handle) noexcept { 12 | return Handle != InvalidValue; 13 | } 14 | 15 | static void Releasor(const HandleType& Handle) noexcept { 16 | BIO_free(Handle); 17 | } 18 | }; 19 | 20 | struct OpensslBIOChainTraits { 21 | using HandleType = BIO*; 22 | 23 | static inline const HandleType InvalidValue = nullptr; 24 | 25 | [[nodiscard]] 26 | static bool IsValid(const HandleType& Handle) noexcept { 27 | return Handle != InvalidValue; 28 | } 29 | 30 | static void Releasor(const HandleType& Handle) noexcept { 31 | BIO_free_all(Handle); 32 | } 33 | }; 34 | 35 | struct OpensslBNTraits { 36 | using HandleType = BIGNUM*; 37 | 38 | static inline const HandleType InvalidValue = nullptr; 39 | 40 | [[nodiscard]] 41 | static bool IsValid(const HandleType& Handle) noexcept { 42 | return Handle != InvalidValue; 43 | } 44 | 45 | static void Releasor(const HandleType& Handle) noexcept { 46 | BN_free(Handle); 47 | } 48 | }; 49 | 50 | struct OpensslRSATraits { 51 | using HandleType = RSA*; 52 | 53 | static inline const HandleType InvalidValue = nullptr; 54 | 55 | [[nodiscard]] 56 | static bool IsValid(const HandleType& Handle) noexcept { 57 | return Handle != InvalidValue; 58 | } 59 | 60 | static void Releasor(const HandleType& Handle) noexcept { 61 | RSA_free(Handle); 62 | } 63 | }; 64 | 65 | -------------------------------------------------------------------------------- /navicat-patcher/KeystoneAssembler.cpp: -------------------------------------------------------------------------------- 1 | #include "KeystoneAssembler.hpp" 2 | 3 | [[nodiscard]] 4 | KeystoneAssembler KeystoneAssembler::Create(ks_arch ArchType, ks_mode Mode) { 5 | KeystoneAssembler NewAssembler; 6 | 7 | auto err = ks_open(ArchType, Mode, NewAssembler.pvt_Engine.GetAddress()); 8 | if (err != KS_ERR_OK) { 9 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 10 | throw nkg::KeystoneError(__FILE__, __LINE__, err, "ks_open failed."); 11 | } 12 | 13 | return NewAssembler; 14 | } 15 | 16 | void KeystoneAssembler::Option(ks_opt_type Type, size_t Value) { 17 | auto err = ks_option(pvt_Engine, Type, Value); 18 | if (err != KS_ERR_OK) { 19 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 20 | throw nkg::KeystoneError(__FILE__, __LINE__, err, "ks_open failed."); 21 | } 22 | } 23 | 24 | [[nodiscard]] 25 | std::vector KeystoneAssembler::GenerateOpcode(const char *AssemblyCode, uint64_t Address) const { 26 | ResourceOwned pbOpcode(KeystoneMallocTraits{}); 27 | size_t cbOpCode = 0; 28 | size_t InstructionsProcessed = 0; 29 | 30 | if (ks_asm(pvt_Engine, AssemblyCode, Address, pbOpcode.GetAddress(), &cbOpCode, &InstructionsProcessed) != 0) { 31 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 32 | throw nkg::KeystoneError(__FILE__, __LINE__, ks_errno(pvt_Engine), "ks_asm failed."); 33 | } 34 | 35 | return std::vector(pbOpcode.Get(), pbOpcode.Get() + cbOpCode); 36 | } 37 | 38 | [[nodiscard]] 39 | std::vector KeystoneAssembler::GenerateOpcode(const std::string& AssemblyCode, uint64_t Address) const { 40 | ResourceOwned pbOpcode(KeystoneMallocTraits{}); 41 | size_t cbOpCode = 0; 42 | size_t InstructionsProcessed = 0; 43 | 44 | if (ks_asm(pvt_Engine, AssemblyCode.c_str(), Address, pbOpcode.GetAddress(), &cbOpCode, &InstructionsProcessed) != 0) { 45 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 46 | throw nkg::KeystoneError(__FILE__, __LINE__, ks_errno(pvt_Engine), "ks_asm failed."); 47 | } 48 | 49 | return std::vector(pbOpcode.Get(), pbOpcode.Get() + cbOpCode); 50 | } 51 | -------------------------------------------------------------------------------- /navicat-keygen/Base64.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../common/Exception.hpp" 5 | #include "../common/ResourceOwned.hpp" 6 | #include "../common/ResourceTraitsOpenssl.hpp" 7 | 8 | std::string base64_encode(const std::vector& bindata) { 9 | ResourceOwned b64(OpensslBIOTraits{}, BIO_new(BIO_f_base64())); 10 | if (b64.IsValid() == false) { 11 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 12 | throw nkg::Exception(__FILE__, __LINE__, "BIO_new failed."); 13 | } 14 | 15 | BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); 16 | 17 | ResourceOwned mem(OpensslBIOTraits{}, BIO_new(BIO_s_mem())); 18 | if (mem.IsValid() == false) { 19 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 20 | throw nkg::Exception(__FILE__, __LINE__, "BIO_new failed."); 21 | } 22 | 23 | BIO_push(b64, mem); 24 | 25 | if (BIO_write(b64, bindata.data(), bindata.size()) != bindata.size()) { 26 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 27 | throw nkg::Exception(__FILE__, __LINE__, "BIO_write failed."); 28 | } 29 | 30 | if (BIO_flush(b64) != 1) { 31 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 32 | throw nkg::Exception(__FILE__, __LINE__, "BIO_flush failed."); 33 | } 34 | 35 | const char* data = nullptr; 36 | auto len = BIO_get_mem_data(mem, &data); 37 | 38 | BIO_pop(b64); 39 | 40 | return std::string(data, len); 41 | } 42 | 43 | std::vector base64_decode(const std::string& ascdata) { 44 | ResourceOwned b64(OpensslBIOTraits{}, BIO_new(BIO_f_base64())); 45 | if (b64.IsValid() == false) { 46 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 47 | throw nkg::Exception(__FILE__, __LINE__, "BIO_new failed."); 48 | } 49 | 50 | BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); 51 | 52 | ResourceOwned mem(OpensslBIOTraits{}, BIO_new_mem_buf(ascdata.c_str(), -1)); 53 | if (mem.IsValid() == false) { 54 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 55 | throw nkg::Exception(__FILE__, __LINE__, "BIO_new failed."); 56 | } 57 | 58 | BIO_push(b64, mem); 59 | 60 | std::vector bindata(ascdata.length() / 4 * 3 + 1); 61 | bindata.resize(BIO_read(b64, bindata.data(), bindata.size())); 62 | 63 | BIO_pop(b64); 64 | 65 | return bindata; 66 | } 67 | -------------------------------------------------------------------------------- /navicat-patcher/HelperIsResolvedTo.cpp: -------------------------------------------------------------------------------- 1 | #include // NOLINT 2 | #include "ExceptionCapstone.hpp" 3 | #include "CapstoneDisassembler.hpp" 4 | #include "X64ImageInterpreter.hpp" 5 | 6 | namespace nkg { 7 | 8 | bool IsResolvedTo(const X64ImageInterpreter& Image, const void* StubHelperProc, const char *Symbol) { 9 | CapstoneDisassembler Disassembler = CapstoneDisassembler::Create(CS_ARCH_X86, CS_MODE_64); 10 | 11 | Disassembler.Option(CS_OPT_DETAIL, CS_OPT_ON); 12 | 13 | // A stub-helper proc must look like: 14 | // push xxxxh; 15 | // jmp loc_xxxxxxxx 16 | // which should be 10 bytes long. 17 | Disassembler.SetContext(StubHelperProc, 10); 18 | 19 | if (Disassembler.Next() == false) { 20 | return false; 21 | } 22 | 23 | auto insn = Disassembler.GetInstruction(); 24 | if (strcasecmp(insn->mnemonic, "push") != 0 || insn->detail->x86.operands[0].type != X86_OP_IMM) { 25 | return false; 26 | } 27 | 28 | auto bind_opcode_offset = static_cast(insn->detail->x86.operands[0].imm); 29 | if (Image.CommandOf() == nullptr) { 30 | return false; 31 | } 32 | 33 | auto bind_opcode_ptr = 34 | Image.ImageOffset(Image.CommandOf()->lazy_bind_off) + 35 | bind_opcode_offset; 36 | 37 | while ((*bind_opcode_ptr & BIND_OPCODE_MASK) != BIND_OPCODE_DONE) { 38 | switch (*bind_opcode_ptr & BIND_OPCODE_MASK) { 39 | case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: // 0x10 40 | case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: // 0x30 41 | case BIND_OPCODE_SET_TYPE_IMM: // 0x50 42 | case BIND_OPCODE_DO_BIND: // 0x90 43 | case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: // 0xB0 44 | ++bind_opcode_ptr; 45 | break; 46 | case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: // 0x20 47 | case BIND_OPCODE_SET_ADDEND_SLEB: // 0x60 48 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: // 0x70 49 | case BIND_OPCODE_ADD_ADDR_ULEB: // 0x80 50 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: // 0xA0 51 | while (*(++bind_opcode_ptr) & 0x80u); 52 | ++bind_opcode_ptr; 53 | break; 54 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: // 0x40 55 | return strcmp(reinterpret_cast(bind_opcode_ptr + 1), Symbol) == 0; 56 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: // 0xC0 57 | // 58 | // This opcode is too rare to appear, 59 | // It is okay to dismiss this opcode 60 | // 61 | return false; 62 | default: 63 | return false; 64 | } 65 | } 66 | 67 | return false; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /navicat-patcher/PatchSolution0.cpp: -------------------------------------------------------------------------------- 1 | #include "PatchSolutions.hpp" 2 | #include 3 | 4 | const char PatchSolution0::Keyword[452] = 5 | "-----BEGIN PUBLIC KEY-----\x00" 6 | "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I\x00" 7 | "qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv\x00" 8 | "a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF\x00" 9 | "R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2\x00" 10 | "WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt\x00" 11 | "YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ\x00" 12 | "awIDAQAB\x00" 13 | "-----END PUBLIC KEY-----\x00"; 14 | 15 | PatchSolution0::PatchSolution0(const X64ImageInterpreter& Image) noexcept : 16 | pvt_Image(Image), 17 | pvt_PatchOffset(X64ImageInterpreter::InvalidOffset) {} 18 | 19 | bool PatchSolution0::FindPatchOffset() noexcept { 20 | try { 21 | pvt_PatchOffset = pvt_Image.SearchSectionOffset("__TEXT", "__cstring", [](const uint8_t* p) { 22 | return memcmp(p, Keyword, sizeof(Keyword) - 1) == 0; 23 | }); 24 | 25 | printf("[+] PatchSolution0 ...... Ready to apply.\n"); 26 | printf(" Keyword offset = +0x%.8x\n", pvt_PatchOffset); 27 | return true; 28 | } catch (...) { 29 | printf("[-] PatchSolution0 ...... Omitted.\n"); 30 | return false; 31 | } 32 | } 33 | 34 | bool PatchSolution0::CheckKey(const RSACipher& RsaCipher) const noexcept { 35 | try { 36 | if (RsaCipher.Bits() != 2048) { 37 | return false; 38 | } 39 | 40 | std::string PublicKeyPEM = RsaCipher.ExportKeyString(); 41 | for (auto& c : PublicKeyPEM) { 42 | if (c == '\n') c = '\x00'; 43 | } 44 | 45 | return PublicKeyPEM.length() == sizeof(Keyword) - 1; 46 | } catch (...) { 47 | return false; 48 | } 49 | } 50 | 51 | void PatchSolution0::MakePatch(const RSACipher& RsaCipher) const { 52 | if (pvt_PatchOffset == X64ImageInterpreter::InvalidOffset) { 53 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 54 | throw nkg::Exception(__FILE__, __LINE__, "PatchSolution0 is not ready."); 55 | } 56 | 57 | std::string PublicKeyPEM = RsaCipher.ExportKeyString(); 58 | for (auto& c : PublicKeyPEM) { 59 | if (c == '\n') c = '\x00'; 60 | } 61 | 62 | auto pbPatch = pvt_Image.ImageOffset(pvt_PatchOffset); 63 | 64 | puts("**************************************************************"); 65 | puts("* PatchSolution0 *"); 66 | puts("**************************************************************"); 67 | printf("@+0x%.8x\n", pvt_PatchOffset); 68 | 69 | puts("Previous:"); 70 | nkg::PrintMemory(pbPatch, pbPatch + sizeof(Keyword), pbPatch); 71 | 72 | memcpy(pbPatch, PublicKeyPEM.data(), PublicKeyPEM.size()); 73 | 74 | puts("After:"); 75 | nkg::PrintMemory(pbPatch, pbPatch + sizeof(Keyword), pbPatch); 76 | 77 | puts(""); 78 | } 79 | -------------------------------------------------------------------------------- /navicat-patcher/HelperPrintMemory.cpp: -------------------------------------------------------------------------------- 1 | #include // NOLINT 2 | #include // NOLINT 3 | #include // NOLINT 4 | #include // NOLINT 5 | #include // NOLINT 6 | 7 | static jmp_buf env; 8 | 9 | static void SIGSEGV_Handler(int sig) { 10 | siglongjmp(env, 1); 11 | } 12 | 13 | // 14 | // read byte(s) at address `p` as __Type to `out` 15 | // succeed if return true, otherwise return false 16 | // 17 | template 18 | static inline bool ProbeForRead(const void* p, void* out) { 19 | int r = sigsetjmp(env, 1); 20 | if (r == 0) { 21 | *reinterpret_cast<__Type*>(out) = *reinterpret_cast(p); 22 | return true; 23 | } else { 24 | return false; 25 | } 26 | } 27 | 28 | // 29 | // Print memory data in [from, to) at least 30 | // If `base` is not nullptr, print address as offset. Otherwise, as absolute address. 31 | // NOTICE: 32 | // `base` must >= `from` 33 | // 34 | namespace nkg { 35 | 36 | void PrintMemory(const void *from, const void *to, const void *base) { 37 | auto start = reinterpret_cast(from); 38 | auto end = reinterpret_cast(to); 39 | auto base_ptr = reinterpret_cast(base); 40 | 41 | if (start >= end) 42 | return; 43 | 44 | while (reinterpret_cast(start) % 16) 45 | start--; 46 | 47 | while (reinterpret_cast(end) % 16) 48 | end++; 49 | 50 | void (*prev_handler)(int) = signal(SIGSEGV, SIGSEGV_Handler); 51 | while (start < end) { 52 | uint16_t value[16] = {}; 53 | 54 | if (base_ptr) { 55 | uintptr_t d = start >= base ? start - base_ptr : base_ptr - start; 56 | if (start >= base) { 57 | printf("+0x%.*zx ", static_cast(2 * sizeof(void *)), d); 58 | } else { 59 | printf("-0x%.*zx ", static_cast(2 * sizeof(void *)), d); 60 | } 61 | } else { 62 | printf("0x%.*zx ", static_cast(2 * sizeof(void *)), reinterpret_cast(start)); 63 | } 64 | 65 | for (int i = 0; i < 16; ++i) { 66 | if (start + i < from) { 67 | printf(" "); 68 | value[i] = 0xfffe; 69 | } else if (ProbeForRead(start + i, value + i)) { 70 | printf("%02x ", value[i]); 71 | } else { 72 | printf("?? "); 73 | value[i] = 0xffff; 74 | } 75 | } 76 | 77 | printf(" "); 78 | 79 | for (int i = 0; i < 16; ++i) { // NOLINT 80 | if (0x20 <= value[i] && value[i] < 0x7f) { 81 | printf("%c", value[i]); 82 | } else if (value[i] == 0xfffe) { 83 | printf(" "); 84 | } else { 85 | printf("."); 86 | } 87 | } 88 | 89 | printf("\n"); 90 | 91 | start += 0x10; 92 | } 93 | signal(SIGSEGV, prev_handler); 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /navicat-patcher/CapstoneDisassembler.cpp: -------------------------------------------------------------------------------- 1 | #include "CapstoneDisassembler.hpp" 2 | 3 | CapstoneDisassembler CapstoneDisassembler::Create(cs_arch ArchType, cs_mode Mode) { 4 | CapstoneDisassembler NewDisassembler; 5 | 6 | auto err = cs_open(ArchType, Mode, NewDisassembler.pvt_Handle.GetAddress()); 7 | if (err != CS_ERR_OK) { 8 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 9 | throw nkg::CapstoneError(__FILE__, __LINE__, err, "cs_open failed."); 10 | } 11 | 12 | NewDisassembler.pvt_Insn.TakeOver(cs_malloc(NewDisassembler.pvt_Handle)); 13 | if (NewDisassembler.pvt_Insn.IsValid() == false) { 14 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 15 | throw nkg::CapstoneError(__FILE__, __LINE__, cs_errno(NewDisassembler.pvt_Handle), "cs_malloc failed."); 16 | } 17 | 18 | return NewDisassembler; 19 | } 20 | 21 | void CapstoneDisassembler::Option(cs_opt_type Type, size_t Value) { 22 | auto err = cs_option(pvt_Handle, Type, Value); 23 | if (err != CS_ERR_OK) { 24 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 25 | throw nkg::CapstoneError(__FILE__, __LINE__, err, "cs_option failed."); 26 | } 27 | 28 | pvt_CurrentInsn = nullptr; 29 | 30 | pvt_Insn.TakeOver(cs_malloc(pvt_Handle)); 31 | if (pvt_Insn.IsValid() == false) { 32 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 33 | throw nkg::CapstoneError(__FILE__, __LINE__, cs_errno(pvt_Handle), "cs_malloc failed."); 34 | } 35 | } 36 | 37 | void CapstoneDisassembler::SetContext(uintptr_t lpOpcode, size_t cbOpcode, uint64_t Address) noexcept { 38 | pvt_CurrentCtx.pbOpcode = reinterpret_cast(lpOpcode); 39 | pvt_CurrentCtx.cbOpcode = cbOpcode; 40 | pvt_CurrentCtx.Address = Address; 41 | pvt_CurrentInsn = nullptr; 42 | } 43 | 44 | void CapstoneDisassembler::SetContext(const void* lpOpcode, size_t cbOpcode, uint64_t Address) noexcept { 45 | pvt_CurrentCtx.pbOpcode = reinterpret_cast(lpOpcode); 46 | pvt_CurrentCtx.cbOpcode = cbOpcode; 47 | pvt_CurrentCtx.Address = Address; 48 | pvt_CurrentInsn = nullptr; 49 | } 50 | 51 | const CapstoneDisassembler::Context& CapstoneDisassembler::GetContext() const noexcept { 52 | return pvt_CurrentCtx; 53 | } 54 | 55 | const cs_insn* CapstoneDisassembler::GetInstruction() const noexcept { 56 | return pvt_CurrentInsn; 57 | } 58 | 59 | CapstoneDisassembler::Context CapstoneDisassembler::GetInstructionContext() const noexcept { 60 | Context CtxOfInsn = {}; 61 | CtxOfInsn.pbOpcode = pvt_CurrentCtx.pbOpcode - pvt_CurrentInsn->size; 62 | CtxOfInsn.cbOpcode = pvt_CurrentCtx.cbOpcode + pvt_CurrentInsn->size; 63 | CtxOfInsn.Address = pvt_CurrentCtx.Address - pvt_CurrentInsn->size; 64 | return CtxOfInsn; 65 | } 66 | 67 | bool CapstoneDisassembler::Next() noexcept { 68 | bool bSucceed = cs_disasm_iter(pvt_Handle, &pvt_CurrentCtx.pbOpcode, &pvt_CurrentCtx.cbOpcode, &pvt_CurrentCtx.Address, pvt_Insn); 69 | if (bSucceed) { 70 | if (pvt_CurrentInsn == nullptr) pvt_CurrentInsn = pvt_Insn.Get(); 71 | } else { 72 | pvt_CurrentInsn = nullptr; 73 | } 74 | return bSucceed; 75 | } 76 | 77 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = g++ 2 | OPENSSL_INCLUDE_PATH = /usr/local/opt/openssl/include 3 | OPENSSL_LIB_PATH = /usr/local/opt/openssl/lib 4 | CAPSTONE_INCLUDE_PATH = #/usr/local/Cellar/capstone/4.0.1/include 5 | CAPSTONE_LIB_PATH = #/usr/local/Cellar/capstone/4.0.1/lib 6 | KEYSTONE_INCLUDE_PATH = #/usr/local/Cellar/keystone/0.9.1/include 7 | KEYSTONE_LIB_PATH = #/usr/local/Cellar/keystone/0.9.1/lib 8 | RAPIDJSON_INCLUDE_PATH = #/usr/local/Cellar/rapidjson/1.1.0/include 9 | LIBPLIST_INCLUDE_PATH = #/usr/local/Cellar/libplist/2.0.0_1/include 10 | LIBPLIST_LIB_PATH = #/usr/local/Cellar/libplist/2.0.0_1/lib 11 | 12 | OUTPUT_DIR = ./bin/ 13 | COMMON_DIR = ./common/ 14 | PATCHER_DIR = ./navicat-patcher/ 15 | KEYGEN_DIR = ./navicat-keygen/ 16 | 17 | COMMON_HEADER = \ 18 | $(COMMON_DIR)Exception.hpp \ 19 | $(COMMON_DIR)ExceptionOpenssl.hpp \ 20 | $(COMMON_DIR)ExceptionSystem.hpp \ 21 | $(COMMON_DIR)ResourceOwned.hpp \ 22 | $(COMMON_DIR)ResourceTraitsOpenssl.hpp \ 23 | $(COMMON_DIR)RSACipher.hpp \ 24 | 25 | PATCHER_HEADER = \ 26 | $(PATCHER_DIR)ExceptionCapstone.hpp \ 27 | $(PATCHER_DIR)ExceptionKeystone.hpp \ 28 | $(PATCHER_DIR)ResourceTraitsCapstone.hpp \ 29 | $(PATCHER_DIR)ResourceTraitsKeystone.hpp \ 30 | $(PATCHER_DIR)ResourceTraitsUnix.hpp \ 31 | $(PATCHER_DIR)CapstoneDisassembler.hpp \ 32 | $(PATCHER_DIR)KeystoneAssembler.hpp \ 33 | $(PATCHER_DIR)X64ImageInterpreter.hpp \ 34 | $(PATCHER_DIR)PatchSolutions.hpp 35 | 36 | PATCHER_SOURCE = \ 37 | $(PATCHER_DIR)CapstoneDisassembler.cpp \ 38 | $(PATCHER_DIR)KeystoneAssembler.cpp \ 39 | $(PATCHER_DIR)X64ImageInterpreter.cpp \ 40 | $(PATCHER_DIR)HelperIsResolvedTo.cpp \ 41 | $(PATCHER_DIR)HelperPrintMemory.cpp \ 42 | $(PATCHER_DIR)PatchSolution0.cpp \ 43 | $(PATCHER_DIR)PatchSolution1.cpp \ 44 | $(PATCHER_DIR)PatchSolution2.cpp \ 45 | $(PATCHER_DIR)main.cpp 46 | 47 | PATCHER_BINARY = $(OUTPUT_DIR)navicat-patcher 48 | 49 | KEYGEN_HEADER = \ 50 | $(KEYGEN_DIR)DESCipher.hpp \ 51 | $(KEYGEN_DIR)NavicatKeygen.hpp 52 | 53 | KEYGEN_SOURCE = \ 54 | $(KEYGEN_DIR)Base64.cpp \ 55 | $(KEYGEN_DIR)main.cpp 56 | 57 | KEYGEN_BINARY = $(OUTPUT_DIR)navicat-keygen 58 | 59 | patcher: $(PATCHER_HEADER) $(PATCHER_SOURCE) 60 | @if [ ! -d $(OUTPUT_DIR) ]; then mkdir -p $(OUTPUT_DIR); fi 61 | $(CC) -std=c++17 -O2 \ 62 | -I$(OPENSSL_INCLUDE_PATH) -L$(OPENSSL_LIB_PATH) \ 63 | $(if $(CAPSTONE_INCLUDE_PATH),-I$(CAPSTONE_INCLUDE_PATH),) $(if $(CAPSTONE_LIB_PATH),-L$(CAPSTONE_LIB_PATH),) \ 64 | $(if $(KEYSTONE_INCLUDE_PATH),-I$(KEYSTONE_INCLUDE_PATH),) $(if $(KEYSTONE_LIB_PATH),-L$(KEYSTONE_LIB_PATH),) \ 65 | $(if $(LIBPLIST_INCLUDE_PATH),-I$(LIBPLIST_INCLUDE_PATH),) $(if $(LIBPLIST_LIB_PATH),-L$(LIBPLIST_LIB_PATH),) \ 66 | -lcrypto -lcapstone -lkeystone -lplist++ $(PATCHER_SOURCE) -o $(PATCHER_BINARY) 67 | @echo 68 | 69 | keygen: $(KEYGEM_HEADER) $(KEYGEN_SOURCE) 70 | @if [ ! -d $(OUTPUT_DIR) ]; then mkdir -p $(OUTPUT_DIR); fi 71 | $(CC) -std=c++17 -O2 \ 72 | -I$(OPENSSL_INCLUDE_PATH) -L$(OPENSSL_LIB_PATH) \ 73 | $(if $(RAPIDJSON_INCLUDE_PATH),-I$(RAPIDJSON_INCLUDE_PATH),) \ 74 | -lcrypto $(KEYGEN_SOURCE) -o $(KEYGEN_BINARY) 75 | 76 | all: patcher keygen 77 | @echo 'Done.' 78 | 79 | .PHONY: all 80 | 81 | clean: 82 | ifeq ($(wildcard $(PATCHER_BINARY)), $(PATCHER_BINARY)) 83 | rm $(PATCHER_BINARY) 84 | endif 85 | 86 | ifeq ($(wildcard $(KEYGEN_BINARY)), $(KEYGEN_BINARY)) 87 | rm $(KEYGEN_BINARY) 88 | endif 89 | 90 | -------------------------------------------------------------------------------- /navicat-patcher/PatchSolutions.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../common/Exception.hpp" 3 | #include "../common/ResourceOwned.hpp" 4 | #include "../common/RSACipher.hpp" 5 | #include "ResourceTraitsUnix.hpp" 6 | #include "X64ImageInterpreter.hpp" 7 | #include "CapstoneDisassembler.hpp" 8 | #include "KeystoneAssembler.hpp" 9 | 10 | namespace nkg { 11 | // 12 | // Print memory data in [from, to) at least 13 | // If `base` is not nullptr, print address as offset. Otherwise, as absolute address. 14 | // NOTICE: 15 | // `base` must >= `from` 16 | // 17 | void PrintMemory(const void *from, const void *to, const void *base = nullptr); 18 | bool IsResolvedTo(const X64ImageInterpreter& Image, const void* StubHelperProc, const char *Symbol); 19 | } 20 | 21 | class PatchSolution { 22 | public: 23 | 24 | [[nodiscard]] 25 | virtual bool FindPatchOffset() noexcept = 0; 26 | 27 | [[nodiscard]] 28 | virtual bool CheckKey(const RSACipher& RsaCipher) const noexcept = 0; 29 | 30 | virtual void MakePatch(const RSACipher& RsaCipher) const = 0; 31 | 32 | virtual ~PatchSolution() = default; 33 | }; 34 | 35 | class PatchSolution0 : public PatchSolution { 36 | private: 37 | 38 | static const char Keyword[452]; 39 | 40 | const X64ImageInterpreter& pvt_Image; 41 | uint32_t pvt_PatchOffset; 42 | 43 | public: 44 | 45 | explicit 46 | PatchSolution0(const X64ImageInterpreter& Image) noexcept; 47 | 48 | [[nodiscard]] 49 | // NOLINTNEXTLINE: mark "virtual" explicitly for more readability 50 | virtual bool FindPatchOffset() noexcept override; 51 | 52 | [[nodiscard]] 53 | // NOLINTNEXTLINE: mark "virtual" explicitly for more readability 54 | virtual bool CheckKey(const RSACipher& RsaCipher) const noexcept override; 55 | 56 | // NOLINTNEXTLINE: mark "virtual" explicitly for more readability 57 | virtual void MakePatch(const RSACipher& RsaCipher) const override; 58 | }; 59 | 60 | class PatchSolution1 : public PatchSolution { 61 | private: 62 | 63 | static const uint8_t Keyword[0x188]; 64 | 65 | const X64ImageInterpreter& pvt_Image; 66 | uint32_t pvt_PatchOffset; 67 | 68 | public: 69 | 70 | explicit 71 | PatchSolution1(const X64ImageInterpreter& Image) noexcept; 72 | 73 | [[nodiscard]] 74 | // NOLINTNEXTLINE: mark "virtual" explicitly for more readability 75 | virtual bool FindPatchOffset() noexcept override; 76 | 77 | [[nodiscard]] 78 | // NOLINTNEXTLINE: mark "virtual" explicitly for more readability 79 | virtual bool CheckKey(const RSACipher& RsaCipher) const noexcept override; 80 | 81 | // NOLINTNEXTLINE: mark "virtual" explicitly for more readability 82 | virtual void MakePatch(const RSACipher& RsaCipher) const override; 83 | }; 84 | 85 | class PatchSolution2 : public PatchSolution { 86 | private: 87 | 88 | static const char Keyword[1114]; 89 | static const uint8_t FunctionHeader[9]; 90 | 91 | const X64ImageInterpreter& pvt_Image; 92 | CapstoneDisassembler pvt_Disassembler; 93 | KeystoneAssembler pvt_Assembler; 94 | uint32_t pvt_FunctionOffset; 95 | uint32_t pvt_KeywordOffset; 96 | uint64_t pvt_StdStringAppendStubRva; 97 | 98 | public: 99 | 100 | explicit 101 | PatchSolution2(const X64ImageInterpreter& Image) noexcept; 102 | 103 | [[nodiscard]] 104 | // NOLINTNEXTLINE: mark "virtual" explicitly for more readability 105 | virtual bool FindPatchOffset() noexcept override; 106 | 107 | [[nodiscard]] 108 | // NOLINTNEXTLINE: mark "virtual" explicitly for more readability 109 | virtual bool CheckKey(const RSACipher& RsaCipher) const noexcept override; 110 | 111 | // NOLINTNEXTLINE: mark "virtual" explicitly for more readability 112 | virtual void MakePatch(const RSACipher& RsaCipher) const override; 113 | }; 114 | 115 | -------------------------------------------------------------------------------- /navicat-patcher/X64ImageInterpreter.cpp: -------------------------------------------------------------------------------- 1 | #include "X64ImageInterpreter.hpp" 2 | 3 | [[nodiscard]] 4 | X64ImageInterpreter X64ImageInterpreter::Parse(void* ImageBase) { 5 | X64ImageInterpreter NewImage; 6 | 7 | NewImage.pvt_MachHeader = reinterpret_cast(ImageBase); 8 | if (NewImage.pvt_MachHeader->magic != MH_MAGIC_64) { 9 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 10 | throw nkg::Exception(__FILE__, __LINE__, "Bad Image"); 11 | } 12 | 13 | auto cmd_p = reinterpret_cast(NewImage.pvt_MachHeader + 1); 14 | for (uint32_t i = 0; i < NewImage.pvt_MachHeader->ncmds; ++i) { 15 | switch (cmd_p->cmd) { 16 | case LC_SEGMENT_64: { 17 | auto segcmd_p = reinterpret_cast(cmd_p); 18 | auto section_p = reinterpret_cast(segcmd_p + 1); 19 | 20 | NewImage.pvt_SegmentCommands.emplace_back(segcmd_p); 21 | 22 | for (uint32_t j = 0; j < segcmd_p->nsects; ++j) { 23 | NewImage.pvt_Sections.ByIndex[NewImage.pvt_Sections.ByIndex.size()] = §ion_p[j]; 24 | NewImage.pvt_Sections.ByMapAddress[section_p[j].addr] = §ion_p[j]; 25 | NewImage.pvt_Sections.ByFileOffset[section_p[j].offset] = §ion_p[j]; 26 | } 27 | 28 | break; 29 | } 30 | case LC_DYSYMTAB: { 31 | NewImage.pvt_DynamicSymbol.SegmentCommand = reinterpret_cast(cmd_p); 32 | break; 33 | } 34 | case LC_SYMTAB: { 35 | NewImage.pvt_Symbol.SegmentCommand = reinterpret_cast(cmd_p); 36 | NewImage.pvt_Symbol.StringTable = NewImage.ImageOffset(NewImage.pvt_Symbol.SegmentCommand->stroff); 37 | NewImage.pvt_Symbol.SymbolTable = NewImage.ImageOffset(NewImage.pvt_Symbol.SegmentCommand->symoff); 38 | break; 39 | } 40 | case LC_DYLD_INFO_ONLY: { // NOLINT 41 | NewImage.pvt_DynamicLoaderInfoOnly.SegmentCommand = reinterpret_cast(cmd_p); 42 | } 43 | default: 44 | break; 45 | } 46 | 47 | cmd_p = reinterpret_cast( 48 | reinterpret_cast(cmd_p) + cmd_p->cmdsize 49 | ); 50 | } 51 | 52 | return NewImage; 53 | } 54 | 55 | [[nodiscard]] 56 | size_t X64ImageInterpreter::NumberOfSections() const noexcept { 57 | return pvt_Sections.ByIndex.size(); 58 | } 59 | 60 | [[nodiscard]] 61 | section_64* X64ImageInterpreter::ImageSection(size_t Index) const { 62 | auto it = pvt_Sections.ByIndex.find(Index); 63 | if (it != pvt_Sections.ByIndex.cend()) { 64 | return it->second; 65 | } else { 66 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 67 | throw nkg::Exception(__FILE__, __LINE__, "Section is not found."); 68 | } 69 | } 70 | 71 | [[nodiscard]] 72 | section_64* X64ImageInterpreter::ImageSection(const char* SegmentName, const char* SectionName) const { 73 | for (auto segcmd_p : pvt_SegmentCommands) { 74 | if (strncmp(SegmentName, segcmd_p->segname, sizeof(segcmd_p->segname)) == 0) { 75 | auto sec_p = reinterpret_cast(segcmd_p + 1); 76 | 77 | for (uint32_t i = 0; i < segcmd_p->nsects; ++i) { 78 | if (strncmp(SectionName, sec_p[i].sectname, sizeof(sec_p[i].sectname)) == 0) 79 | return &sec_p[i]; 80 | } 81 | 82 | break; 83 | } 84 | } 85 | 86 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 87 | throw nkg::Exception(__FILE__, __LINE__, "Section is not found."); 88 | } 89 | 90 | [[nodiscard]] 91 | section_64* X64ImageInterpreter::ImageSectionByOffset(uint32_t Offset) const { 92 | for (const auto& it : pvt_Sections.ByFileOffset) { 93 | if (it.first <= Offset && Offset < it.first + it.second->size) { 94 | return it.second; 95 | } 96 | } 97 | 98 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 99 | throw nkg::Exception(__FILE__, __LINE__, "Section is not found."); 100 | } 101 | 102 | [[nodiscard]] 103 | section_64* X64ImageInterpreter::ImageSectionByRva(uint64_t Rva) const { 104 | for (const auto& it : pvt_Sections.ByMapAddress) { 105 | if (it.first <= Rva && Rva < it.first + it.second->size) { 106 | return it.second; 107 | } 108 | } 109 | 110 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 111 | throw nkg::Exception(__FILE__, __LINE__, "Section is not found."); 112 | } 113 | 114 | [[nodiscard]] 115 | uint64_t X64ImageInterpreter::OffsetToRva(uint32_t Offset) const { 116 | auto section = ImageSectionByOffset(Offset); 117 | return section->addr + (Offset - section->offset); 118 | } 119 | 120 | [[nodiscard]] 121 | uint32_t X64ImageInterpreter::RvaToOffset(uint64_t Rva) const { 122 | auto section = ImageSectionByRva(Rva); 123 | return section->offset + (Rva - section->addr); 124 | } 125 | 126 | [[nodiscard]] 127 | nlist_64* X64ImageInterpreter::ImageSymbolTable() const noexcept { 128 | return pvt_Symbol.SymbolTable; 129 | } 130 | 131 | 132 | -------------------------------------------------------------------------------- /navicat-patcher/PatchSolution1.cpp: -------------------------------------------------------------------------------- 1 | #include "PatchSolutions.hpp" 2 | #include 3 | 4 | const uint8_t PatchSolution1::Keyword[0x188] = { 5 | 0xfe, 0xfd, 0xfc, 0xf4, 0xfe, 0xd2, 0xf8, 0xf4, 0xf1, 0xd3, 0xde, 0xc7, 0xdf, 0xd3, 0xd0, 0xfd, 6 | 0x8a, 0xc3, 0x85, 0xf4, 0xf6, 0xe9, 0xfc, 0xfc, 0xf2, 0xf5, 0xfa, 0xf5, 0xf6, 0xe9, 0x81, 0xfb, 7 | 0xfe, 0xfd, 0xfc, 0xf4, 0xf4, 0xdf, 0xf2, 0xf9, 0xf2, 0xe5, 0xf0, 0xf7, 0xc0, 0x89, 0xdd, 0xcb, 8 | 0xf5, 0x87, 0xe6, 0xdd, 0xf4, 0xd9, 0xf8, 0xfb, 0xde, 0xf9, 0xcf, 0xc5, 0x8f, 0x80, 0x80, 0xf3, 9 | 0xc2, 0xd0, 0xe2, 0x8f, 0xfa, 0x8a, 0xdd, 0xf3, 0xd7, 0xdc, 0x86, 0xdc, 0xf0, 0x81, 0xc0, 0xea, 10 | 0xd0, 0xd9, 0xf9, 0xd8, 0xda, 0xf2, 0xd0, 0xfd, 0xc3, 0xf6, 0xf3, 0x82, 0xf2, 0x81, 0xef, 0xf2, 11 | 0xe0, 0xf9, 0xf2, 0xd3, 0x8f, 0xd7, 0xe9, 0xfb, 0xca, 0x86, 0xde, 0xfc, 0xf3, 0xd5, 0xdd, 0xf4, 12 | 0xc7, 0x80, 0xf7, 0xd5, 0xf2, 0xc1, 0xde, 0xcc, 0xc0, 0xc7, 0xf0, 0xd0, 0xd0, 0xd1, 0xd7, 0xcc, 13 | 0xd2, 0x81, 0xc1, 0x83, 0xdd, 0xd5, 0x8a, 0x8f, 0x81, 0xe1, 0xf4, 0xd9, 0xf3, 0xd7, 0xca, 0xef, 14 | 0xf9, 0xdf, 0xe1, 0xee, 0xf0, 0xe9, 0xd1, 0xca, 0xf2, 0xe3, 0xf8, 0xf0, 0x83, 0xde, 0xfb, 0xd7, 15 | 0xf1, 0xc4, 0xfa, 0x85, 0xf2, 0xdd, 0xdd, 0xfd, 0x85, 0x86, 0xc7, 0xf9, 0xc4, 0xc9, 0xf4, 0xf8, 16 | 0xd4, 0xd9, 0xe6, 0xd2, 0xf6, 0xc1, 0xc1, 0xf9, 0xe0, 0xe4, 0xf7, 0xe4, 0xfd, 0xf1, 0xf6, 0xfc, 17 | 0xe1, 0x84, 0xe4, 0xd1, 0xed, 0xfe, 0xdb, 0xe8, 0xdd, 0xe1, 0x85, 0xd0, 0xc5, 0xd2, 0x8a, 0x8e, 18 | 0xd5, 0xdd, 0xe3, 0xdb, 0xd0, 0xe1, 0xd0, 0xf6, 0xc6, 0xee, 0xe6, 0xf7, 0xda, 0xf1, 0xdb, 0xc9, 19 | 0x8b, 0xee, 0xcd, 0xdf, 0xff, 0xe8, 0xdd, 0xca, 0x82, 0xdb, 0xf1, 0x82, 0xc3, 0xed, 0xc9, 0xcc, 20 | 0xc0, 0xf2, 0xd6, 0xdf, 0x83, 0xe9, 0xf3, 0xce, 0xea, 0xfa, 0xdf, 0xf8, 0xd9, 0xff, 0xec, 0x88, 21 | 0xe4, 0xe4, 0xfd, 0x80, 0xc5, 0xce, 0xfa, 0xd2, 0xf4, 0xd8, 0x84, 0xff, 0xe5, 0xf3, 0xcb, 0xc2, 22 | 0xfe, 0xc0, 0xc4, 0xfa, 0xde, 0xdd, 0xd5, 0xc9, 0xc5, 0xd5, 0xdf, 0xe3, 0xdd, 0xc1, 0xcb, 0xdd, 23 | 0xfc, 0xf7, 0x83, 0xf8, 0xda, 0xc1, 0xd4, 0xe3, 0xfe, 0xc2, 0xef, 0xf8, 0xf2, 0xea, 0x8a, 0xd2, 24 | 0xc7, 0xf2, 0xf0, 0xc2, 0xfb, 0x89, 0xdc, 0xeb, 0xd1, 0xf7, 0xcc, 0xe2, 0xd1, 0xfc, 0xd4, 0xce, 25 | 0xea, 0xcd, 0xe4, 0x87, 0xe0, 0xcc, 0x8d, 0xf5, 0xc7, 0x85, 0x87, 0xda, 0xcf, 0xde, 0x89, 0xcd, 26 | 0xe5, 0xfd, 0xe7, 0x83, 0xda, 0xdb, 0xfe, 0xf4, 0x84, 0xec, 0xf6, 0xee, 0xfd, 0xea, 0xf1, 0xf5, 27 | 0xf5, 0xfc, 0xe6, 0xd0, 0x86, 0xdf, 0xc3, 0xe2, 0xe4, 0xd5, 0xd7, 0xe4, 0xe4, 0xce, 0xd4, 0xce, 28 | 0x82, 0xda, 0xc7, 0xda, 0x80, 0xcb, 0xee, 0x8c, 0xd0, 0xde, 0xcd, 0xda, 0xdd, 0xcd, 0xcc, 0xeb, 29 | 0xd2, 0xc3, 0xfc, 0xf2, 0xf6, 0xe9, 0xf8, 0xf8 30 | }; 31 | 32 | PatchSolution1::PatchSolution1(const X64ImageInterpreter& Image) noexcept : 33 | pvt_Image(Image), 34 | pvt_PatchOffset(X64ImageInterpreter::InvalidOffset) {} 35 | 36 | bool PatchSolution1::FindPatchOffset() noexcept { 37 | uint8_t* pbPatch = nullptr; 38 | 39 | for (size_t i = 0; i < pvt_Image.NumberOfSections(); ++i) { 40 | auto pbSectionView = pvt_Image.SectionView(i); 41 | auto cbSectiveView = pvt_Image.ImageSection(i)->size; 42 | 43 | if (cbSectiveView < sizeof(Keyword)) { 44 | continue; 45 | } 46 | 47 | for (size_t j = 0; j < cbSectiveView - sizeof(Keyword); ++j) { 48 | if (memcmp(pbSectionView + j, Keyword, sizeof(Keyword)) == 0) { 49 | pbPatch = pbSectionView + j; 50 | i = pvt_Image.NumberOfSections(); 51 | break; 52 | } 53 | } 54 | } 55 | 56 | if (pbPatch == nullptr) { 57 | printf("[-] PatchSolution1 ...... Omitted.\n"); 58 | return false; 59 | } else { 60 | pvt_PatchOffset = pbPatch - pvt_Image.ImageBase(); 61 | 62 | printf("[+] PatchSolution1 ...... Ready to apply.\n"); 63 | printf(" Keyword offset = +0x%.8x\n", pvt_PatchOffset); 64 | return true; 65 | } 66 | } 67 | 68 | bool PatchSolution1::CheckKey(const RSACipher& RsaCipher) const noexcept { 69 | try { 70 | std::string PublicKeyPEM = RsaCipher.ExportKeyString(); 71 | 72 | PublicKeyPEM.erase(PublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"), 26); 73 | PublicKeyPEM.erase(PublicKeyPEM.find("-----END PUBLIC KEY-----"), 24); 74 | { 75 | std::string::size_type pos = 0; 76 | while ((pos = PublicKeyPEM.find('\n', pos)) != std::string::npos) { 77 | PublicKeyPEM.erase(pos, 1); 78 | } 79 | } 80 | 81 | return PublicKeyPEM.length() == sizeof(Keyword); 82 | } catch (...) { 83 | return false; 84 | } 85 | } 86 | 87 | void PatchSolution1::MakePatch(const RSACipher& RsaCipher) const { 88 | if (pvt_PatchOffset == X64ImageInterpreter::InvalidOffset) { 89 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 90 | throw nkg::Exception(__FILE__, __LINE__, "PatchSolution1 is not ready."); 91 | } 92 | 93 | auto pbPatch = pvt_Image.ImageOffset(pvt_PatchOffset); 94 | std::string PublicKeyPEM = RsaCipher.ExportKeyString(); 95 | 96 | PublicKeyPEM.erase(PublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"), 26); 97 | PublicKeyPEM.erase(PublicKeyPEM.find("-----END PUBLIC KEY-----"), 24); 98 | { 99 | std::string::size_type pos = 0; 100 | while ((pos = PublicKeyPEM.find('\n', pos)) != std::string::npos) { 101 | PublicKeyPEM.erase(pos, 1); 102 | } 103 | } 104 | 105 | uint8_t key = 8; 106 | for (auto& c : PublicKeyPEM) { 107 | if (key == 0) key = 8; 108 | reinterpret_cast(c) ^= 0xbbu - key; 109 | --key; 110 | } 111 | 112 | puts("**************************************************************"); 113 | puts("* PatchSolution1 *"); 114 | puts("**************************************************************"); 115 | printf("@+0x%.8x\n", pvt_PatchOffset); 116 | 117 | puts("Previous:"); 118 | nkg::PrintMemory(pbPatch, pbPatch + sizeof(Keyword), pbPatch); 119 | 120 | memcpy(pbPatch, PublicKeyPEM.data(), PublicKeyPEM.length()); 121 | 122 | puts("After:"); 123 | nkg::PrintMemory(pbPatch, pbPatch + sizeof(Keyword), pbPatch); 124 | 125 | puts(""); 126 | } 127 | 128 | -------------------------------------------------------------------------------- /navicat-patcher/X64ImageInterpreter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include // NOLINT 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "../common/Exception.hpp" 8 | 9 | class X64ImageInterpreter { 10 | public: 11 | static constexpr uint64_t InvalidAddress = static_cast(-1); 12 | static constexpr uint32_t InvalidOffset = static_cast(-1); 13 | private: 14 | 15 | mach_header_64* pvt_MachHeader; 16 | std::vector pvt_SegmentCommands; 17 | 18 | struct { 19 | std::map ByIndex; 20 | std::map ByMapAddress; 21 | std::map ByFileOffset; 22 | } pvt_Sections; 23 | 24 | struct { 25 | dysymtab_command* SegmentCommand; 26 | } pvt_DynamicSymbol; 27 | 28 | struct { 29 | symtab_command* SegmentCommand; 30 | char* StringTable; 31 | nlist_64* SymbolTable; 32 | } pvt_Symbol; 33 | 34 | struct { 35 | dyld_info_command* SegmentCommand; 36 | } pvt_DynamicLoaderInfoOnly; 37 | 38 | X64ImageInterpreter() : 39 | pvt_MachHeader(nullptr), 40 | pvt_DynamicSymbol{}, 41 | pvt_Symbol{}, 42 | pvt_DynamicLoaderInfoOnly{} {} 43 | 44 | public: 45 | 46 | [[nodiscard]] 47 | static X64ImageInterpreter Parse(void* ImageBase); 48 | 49 | template 50 | [[nodiscard]] 51 | __ReturnType ImageBase() const noexcept { 52 | static_assert(std::is_pointer_v<__ReturnType>); 53 | return reinterpret_cast<__ReturnType>(pvt_MachHeader); 54 | } 55 | 56 | template 57 | [[nodiscard]] 58 | __ReturnType ImageOffset(size_t Offset) const noexcept { 59 | static_assert(std::is_pointer_v<__ReturnType>); 60 | return reinterpret_cast<__ReturnType>( 61 | reinterpret_cast(pvt_MachHeader) + Offset 62 | ); 63 | } 64 | 65 | template 66 | [[nodiscard]] 67 | auto CommandOf() const noexcept { 68 | if constexpr (__CommandMacro == LC_DYSYMTAB) { 69 | return pvt_DynamicSymbol.SegmentCommand; 70 | } else if constexpr (__CommandMacro == LC_SYMTAB) { 71 | return pvt_Symbol.SegmentCommand; 72 | } else if constexpr (__CommandMacro == LC_DYLD_INFO_ONLY) { // NOLINT 73 | return pvt_DynamicLoaderInfoOnly.SegmentCommand; 74 | } else { 75 | return nullptr; 76 | } 77 | } 78 | 79 | [[nodiscard]] 80 | size_t NumberOfSections() const noexcept; 81 | 82 | [[nodiscard]] 83 | section_64* ImageSection(size_t Index) const; 84 | 85 | [[nodiscard]] 86 | section_64* ImageSection(const char* SegmentName, const char* SectionName) const; 87 | 88 | [[nodiscard]] 89 | section_64* ImageSectionByOffset(uint32_t Offset) const; 90 | 91 | [[nodiscard]] 92 | section_64* ImageSectionByRva(uint64_t Rva) const; 93 | 94 | template 95 | [[nodiscard]] 96 | __ReturnType SectionView(size_t Index) const { 97 | auto Section = ImageSection(Index); 98 | return ImageOffset<__ReturnType>(Section->offset); 99 | } 100 | 101 | template 102 | [[nodiscard]] 103 | __ReturnType SectionView(const char* SegmentName, const char* SectionName) const { 104 | auto Section = ImageSection(SegmentName, SectionName); 105 | return ImageOffset<__ReturnType>(Section->offset); 106 | } 107 | 108 | template 109 | [[nodiscard]] 110 | __ReturnType SectionView(section_64* Section) const { 111 | return ImageOffset<__ReturnType>(Section->offset); 112 | } 113 | 114 | template 115 | [[nodiscard]] 116 | __ReturnType SearchSection(size_t Index, __Hint&& Hint) const { 117 | return SearchSection<__ReturnType>(ImageSection(Index), std::forward<__Hint>(Hint)); 118 | } 119 | 120 | template 121 | [[nodiscard]] 122 | __ReturnType SearchSection(const char* SegmentName, const char* SectionName, __Hint&& Hint) const { 123 | return SearchSection<__ReturnType>(ImageSection(SegmentName, SectionName), std::forward<__Hint>(Hint)); 124 | } 125 | 126 | template 127 | [[nodiscard]] 128 | __ReturnType SearchSection(section_64* Section, __Hint&& Hint) const { 129 | static_assert(std::is_pointer_v<__ReturnType>); 130 | 131 | auto begin = SectionView(Section); 132 | auto end = begin + Section->size; 133 | 134 | for (; begin < end; ++begin) { 135 | if (Hint(begin) == true) { 136 | return reinterpret_cast<__ReturnType>(const_cast(begin)); 137 | } 138 | } 139 | 140 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 141 | throw nkg::Exception(__FILE__, __LINE__, "Data is not found."); 142 | } 143 | 144 | template 145 | [[nodiscard]] 146 | uint32_t SearchSectionOffset(size_t Index, __Hint&& Hint) const { 147 | return SearchSection(Index, std::forward<__Hint>(Hint)) - ImageBase(); 148 | } 149 | 150 | template 151 | [[nodiscard]] 152 | uint32_t SearchSectionOffset(const char* SegmentName, const char* SectionName, __Hint&& Hint) const { 153 | return SearchSection(SegmentName, SectionName, std::forward<__Hint>(Hint)) - ImageBase(); 154 | } 155 | 156 | template 157 | [[nodiscard]] 158 | uint32_t SearchSectionOffset(section_64* Section, __Hint&& Hint) const { 159 | return SearchSection(Section, std::forward<__Hint>(Hint)) - ImageBase(); 160 | } 161 | 162 | template 163 | [[nodiscard]] 164 | uint64_t SearchSectionRva(size_t Index, __Hint&& Hint) const { 165 | auto Section = ImageSection(Index); 166 | auto Offset = SearchSection(Section, std::forward<__Hint>(Hint)) - SectionView(Section); 167 | return Section->addr + Offset; 168 | } 169 | 170 | template 171 | [[nodiscard]] 172 | uint64_t SearchSectionRva(const char* SegmentName, const char* SectionName, __Hint&& Hint) const { 173 | auto Section = ImageSection(SegmentName, SectionName); 174 | auto Offset = SearchSection(Section, std::forward<__Hint>(Hint)) - SectionView(Section); 175 | return Section->addr + Offset; 176 | } 177 | 178 | template 179 | [[nodiscard]] 180 | uint64_t SearchSectionRva(section_64* Section, __Hint&& Hint) const { 181 | auto Offset = SearchSection(Section, std::forward<__Hint>(Hint)) - SectionView(Section); 182 | return Section->addr + Offset; 183 | } 184 | 185 | [[nodiscard]] 186 | uint64_t OffsetToRva(uint32_t Offset) const; 187 | 188 | [[nodiscard]] 189 | uint32_t RvaToOffset(uint64_t Address) const; 190 | 191 | [[nodiscard]] 192 | nlist_64* ImageSymbolTable() const noexcept; 193 | 194 | [[nodiscard]] 195 | char* LookupStringTable(size_t Offset) const noexcept { 196 | return pvt_Symbol.StringTable + Offset; 197 | } 198 | }; 199 | 200 | -------------------------------------------------------------------------------- /navicat-keygen/NavicatKeygen.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "DESCipher.hpp" 5 | 6 | class NavicatKeygen { 7 | public: 8 | enum class Language { 9 | English, 10 | SimplifiedChinese, 11 | TraditionalChinese, 12 | Japanese, 13 | Polish, 14 | Spanish, 15 | French, 16 | German, 17 | Korean, 18 | Russian, 19 | Portuguese 20 | }; 21 | 22 | enum class Product { 23 | DataModeler, 24 | Premium, 25 | MySQL, 26 | PostgreSQL, 27 | Oracle, 28 | SQLServer, 29 | SQLite, 30 | MariaDB, 31 | MongoDB, 32 | ReportViewer 33 | }; 34 | private: 35 | std::random_device rand_dev; 36 | std::default_random_engine rand_eng; 37 | std::uniform_int_distribution rand; 38 | uint8_t data[10]; 39 | public: 40 | 41 | NavicatKeygen() : rand_eng(rand_dev()), rand(0, UINT8_MAX), data{} { 42 | data[0] = 0x68; 43 | data[1] = 0x2A; 44 | } 45 | 46 | void SetLanguageSignature(Language _language) { 47 | switch (_language) { 48 | case Language::English: 49 | data[5] = 0xAC; // Must be 0xAC for English version. 50 | data[6] = 0x88; // Must be 0x88 for English version. 51 | break; 52 | case Language::SimplifiedChinese: 53 | data[5] = 0xCE; // Must be 0xCE for Simplified Chinese version. 54 | data[6] = 0x32; // Must be 0x32 for Simplified Chinese version. 55 | break; 56 | case Language::TraditionalChinese: 57 | data[5] = 0xAA; // Must be 0xAA for Traditional Chinese version. 58 | data[6] = 0x99; // Must be 0x99 for Traditional Chinese version. 59 | break; 60 | case Language::Japanese: 61 | data[5] = 0xAD; // Must be 0xAD for Japanese version. Discoverer: @dragonflylee 62 | data[6] = 0x82; // Must be 0x82 for Japanese version. Discoverer: @dragonflylee 63 | break; 64 | case Language::Polish: 65 | data[5] = 0xBB; // Must be 0xBB for Polish version. Discoverer: @dragonflylee 66 | data[6] = 0x55; // Must be 0x55 for Polish version. Discoverer: @dragonflylee 67 | break; 68 | case Language::Spanish: 69 | data[5] = 0xAE; // Must be 0xAE for Spanish version. Discoverer: @dragonflylee 70 | data[6] = 0x10; // Must be 0x10 for Spanish version. Discoverer: @dragonflylee 71 | break; 72 | case Language::French: 73 | data[5] = 0xFA; // Must be 0xFA for French version. Discoverer: @Deltafox79 74 | data[6] = 0x20; // Must be 0x20 for French version. Discoverer: @Deltafox79 75 | break; 76 | case Language::German: 77 | data[5] = 0xB1; // Must be 0xB1 for German version. Discoverer: @dragonflylee 78 | data[6] = 0x60; // Must be 0x60 for German version. Discoverer: @dragonflylee 79 | break; 80 | case Language::Korean: 81 | data[5] = 0xB5; // Must be 0xB5 for Korean version. Discoverer: @dragonflylee 82 | data[6] = 0x60; // Must be 0x60 for Korean version. Discoverer: @dragonflylee 83 | break; 84 | case Language::Russian: 85 | data[5] = 0xEE; // Must be 0xB5 for Russian version. Discoverer: @dragonflylee 86 | data[6] = 0x16; // Must be 0x60 for Russian version. Discoverer: @dragonflylee 87 | break; 88 | case Language::Portuguese: 89 | data[5] = 0xCD; // Must be 0xCD for Portuguese version. Discoverer: @dragonflylee 90 | data[6] = 0x49; // Must be 0x49 for Portuguese version. Discoverer: @dragonflylee 91 | break; 92 | default: 93 | break; 94 | } 95 | } 96 | 97 | void SetLanguageSignature(uint8_t value0, uint8_t value1) { 98 | data[5] = value0; 99 | data[6] = value1; 100 | } 101 | 102 | void SetProductSignature(Product _product) { 103 | switch (_product) { 104 | case Product::DataModeler: 105 | data[7] = 0x47; 106 | break; 107 | case Product::Premium: 108 | data[7] = 0x65; 109 | break; 110 | case Product::MySQL: 111 | data[7] = 0x68; 112 | break; 113 | case Product::PostgreSQL: 114 | data[7] = 0x6C; 115 | break; 116 | case Product::Oracle: 117 | data[7] = 0x70; 118 | break; 119 | case Product::SQLServer: 120 | data[7] = 0x74; 121 | break; 122 | case Product::SQLite: 123 | data[7] = 0x78; 124 | break; 125 | case Product::MariaDB: 126 | data[7] = 0x7C; 127 | break; 128 | case Product::MongoDB: 129 | data[7] = 0x80; 130 | break; 131 | case Product::ReportViewer: 132 | data[7] = 0xb; 133 | default: 134 | break; 135 | } 136 | } 137 | 138 | void SetProductSignature(uint8_t value) { 139 | data[7] = value; 140 | } 141 | 142 | void SetVersion(uint8_t version) { 143 | data[8] = version << 4; 144 | } 145 | 146 | void Generate() { 147 | static uint8_t DESKey[] = { 0x64, 0xAD, 0xF3, 0x2F, 0xAE, 0xF2, 0x1A, 0x27 }; 148 | DESCipher cipher; 149 | 150 | data[2] = static_cast(rand(rand_eng)); 151 | data[3] = static_cast(rand(rand_eng)); 152 | data[4] = static_cast(rand(rand_eng)); 153 | data[9] = 0x32; 154 | 155 | cipher.SetKey(&DESKey); 156 | cipher.EncryptBlock(data + 2); 157 | } 158 | 159 | std::string GetKey() const { 160 | static const char EncodeTable[] = "ABCDEFGH8JKLMN9PQRSTUVWXYZ234567"; 161 | std::string Key; 162 | 163 | Key.resize(16); 164 | 165 | Key[0] = EncodeTable[data[0] >> 3]; 166 | Key[1] = EncodeTable[(data[0] & 0x07) << 2 | data[1] >> 6]; 167 | Key[2] = EncodeTable[data[1] >> 1 & 0x1F]; 168 | Key[3] = EncodeTable[(data[1] & 0x1) << 4 | data[2] >> 4]; 169 | Key[4] = EncodeTable[(data[2] & 0xF) << 1 | data[3] >> 7]; 170 | Key[5] = EncodeTable[data[3] >> 2 & 0x1F]; 171 | Key[6] = EncodeTable[(data[3] << 3 & 0x1F) | data[4] >> 5]; 172 | Key[7] = EncodeTable[data[4] & 0x1F]; 173 | 174 | Key[8] = EncodeTable[data[5] >> 3]; 175 | Key[9] = EncodeTable[(data[5] & 0x07) << 2 | data[6] >> 6]; 176 | Key[10] = EncodeTable[data[6] >> 1 & 0x1F]; 177 | Key[11] = EncodeTable[(data[6] & 0x1) << 4 | data[7] >> 4]; 178 | Key[12] = EncodeTable[(data[7] & 0xF) << 1 | data[8] >> 7]; 179 | Key[13] = EncodeTable[data[8] >> 2 & 0x1F]; 180 | Key[14] = EncodeTable[(data[8] << 3 & 0x1F) | data[9] >> 5]; 181 | Key[15] = EncodeTable[data[9] & 0x1F]; 182 | 183 | return Key; 184 | } 185 | 186 | std::string GetFormatedKey() const { 187 | std::string Key = GetKey(); 188 | Key.insert(Key.begin() + 4, '-'); 189 | Key.insert(Key.begin() + 5 + 4, '-'); 190 | Key.insert(Key.begin() + 10 + 4, '-'); 191 | return Key; 192 | } 193 | 194 | }; 195 | 196 | -------------------------------------------------------------------------------- /HOW_DOES_IT_WORK.zh-CN.md: -------------------------------------------------------------------------------- 1 | # Navicat keygen - 注册机是怎么工作的? 2 | 3 | ## 1. 关键词解释. 4 | 5 | * __Navicat激活公钥__ 6 | 7 | 这是一个2048位的RSA公钥,Navicat使用这个公钥来完成相关激活信息的加密和解密。 8 | 9 | 这个公钥储存在 10 | 11 | ``` 12 | Navicat Premium.app/Contents/Resources/rpk 13 | ``` 14 | 15 | 中,你可以用任何一种文本编辑器打开并查看它。这个公钥的具体内容为: 16 | 17 | ``` 18 | -----BEGIN PUBLIC KEY----- 19 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I 20 | qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv 21 | a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF 22 | R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2 23 | WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt 24 | YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ 25 | awIDAQAB 26 | -----END PUBLIC KEY----- 27 | ``` 28 | 29 | 如果您有相应的私钥并乐意公开的话欢迎联系我,我将非常感谢您的慷慨。 30 | 31 | __注意:__ 32 | 33 | 从 __Navicat Premium for Mac 12.0.24__ 开始,公钥不再存储在 34 | 35 | ``` 36 | Navicat Premium.app/Contents/Resources/rpk 37 | ``` 38 | 39 | 中。事实上,公钥放在了Navicat的二进制执行文件 40 | 41 | ``` 42 | Navicat Premium.app/Contents/MacOS/Navicat Premium 43 | ``` 44 | 45 | 中,你可以通过搜索`"-----BEGIN PUBLIC KEY-----"`来找到它。 46 | 47 | __注意:__ 48 | 49 | 从 __Navicat Premium for Mac 12.1.14__ 开始,公钥仍然以明文的方式储存在二进制可执行文件中。 50 | 51 | 但是Navicat并不从这个明文中加载公钥。事实上,密钥是从一段0x188字节长的密文中加载的。密文为: 52 | 53 | ```c 54 | const uint8_t ciphertext[0x188] = { 55 | 0xfe, 0xfd, 0xfc, 0xf4, 0xfe, 0xd2, 0xf8, 0xf4, 0xf1, 0xd3, 0xde, 0xc7, 0xdf, 0xd3, 0xd0, 0xfd, 56 | 0x8a, 0xc3, 0x85, 0xf4, 0xf6, 0xe9, 0xfc, 0xfc, 0xf2, 0xf5, 0xfa, 0xf5, 0xf6, 0xe9, 0x81, 0xfb, 57 | 0xfe, 0xfd, 0xfc, 0xf4, 0xf4, 0xdf, 0xf2, 0xf9, 0xf2, 0xe5, 0xf0, 0xf7, 0xc0, 0x89, 0xdd, 0xcb, 58 | 0xf5, 0x87, 0xe6, 0xdd, 0xf4, 0xd9, 0xf8, 0xfb, 0xde, 0xf9, 0xcf, 0xc5, 0x8f, 0x80, 0x80, 0xf3, 59 | 0xc2, 0xd0, 0xe2, 0x8f, 0xfa, 0x8a, 0xdd, 0xf3, 0xd7, 0xdc, 0x86, 0xdc, 0xf0, 0x81, 0xc0, 0xea, 60 | 0xd0, 0xd9, 0xf9, 0xd8, 0xda, 0xf2, 0xd0, 0xfd, 0xc3, 0xf6, 0xf3, 0x82, 0xf2, 0x81, 0xef, 0xf2, 61 | 0xe0, 0xf9, 0xf2, 0xd3, 0x8f, 0xd7, 0xe9, 0xfb, 0xca, 0x86, 0xde, 0xfc, 0xf3, 0xd5, 0xdd, 0xf4, 62 | 0xc7, 0x80, 0xf7, 0xd5, 0xf2, 0xc1, 0xde, 0xcc, 0xc0, 0xc7, 0xf0, 0xd0, 0xd0, 0xd1, 0xd7, 0xcc, 63 | 0xd2, 0x81, 0xc1, 0x83, 0xdd, 0xd5, 0x8a, 0x8f, 0x81, 0xe1, 0xf4, 0xd9, 0xf3, 0xd7, 0xca, 0xef, 64 | 0xf9, 0xdf, 0xe1, 0xee, 0xf0, 0xe9, 0xd1, 0xca, 0xf2, 0xe3, 0xf8, 0xf0, 0x83, 0xde, 0xfb, 0xd7, 65 | 0xf1, 0xc4, 0xfa, 0x85, 0xf2, 0xdd, 0xdd, 0xfd, 0x85, 0x86, 0xc7, 0xf9, 0xc4, 0xc9, 0xf4, 0xf8, 66 | 0xd4, 0xd9, 0xe6, 0xd2, 0xf6, 0xc1, 0xc1, 0xf9, 0xe0, 0xe4, 0xf7, 0xe4, 0xfd, 0xf1, 0xf6, 0xfc, 67 | 0xe1, 0x84, 0xe4, 0xd1, 0xed, 0xfe, 0xdb, 0xe8, 0xdd, 0xe1, 0x85, 0xd0, 0xc5, 0xd2, 0x8a, 0x8e, 68 | 0xd5, 0xdd, 0xe3, 0xdb, 0xd0, 0xe1, 0xd0, 0xf6, 0xc6, 0xee, 0xe6, 0xf7, 0xda, 0xf1, 0xdb, 0xc9, 69 | 0x8b, 0xee, 0xcd, 0xdf, 0xff, 0xe8, 0xdd, 0xca, 0x82, 0xdb, 0xf1, 0x82, 0xc3, 0xed, 0xc9, 0xcc, 70 | 0xc0, 0xf2, 0xd6, 0xdf, 0x83, 0xe9, 0xf3, 0xce, 0xea, 0xfa, 0xdf, 0xf8, 0xd9, 0xff, 0xec, 0x88, 71 | 0xe4, 0xe4, 0xfd, 0x80, 0xc5, 0xce, 0xfa, 0xd2, 0xf4, 0xd8, 0x84, 0xff, 0xe5, 0xf3, 0xcb, 0xc2, 72 | 0xfe, 0xc0, 0xc4, 0xfa, 0xde, 0xdd, 0xd5, 0xc9, 0xc5, 0xd5, 0xdf, 0xe3, 0xdd, 0xc1, 0xcb, 0xdd, 73 | 0xfc, 0xf7, 0x83, 0xf8, 0xda, 0xc1, 0xd4, 0xe3, 0xfe, 0xc2, 0xef, 0xf8, 0xf2, 0xea, 0x8a, 0xd2, 74 | 0xc7, 0xf2, 0xf0, 0xc2, 0xfb, 0x89, 0xdc, 0xeb, 0xd1, 0xf7, 0xcc, 0xe2, 0xd1, 0xfc, 0xd4, 0xce, 75 | 0xea, 0xcd, 0xe4, 0x87, 0xe0, 0xcc, 0x8d, 0xf5, 0xc7, 0x85, 0x87, 0xda, 0xcf, 0xde, 0x89, 0xcd, 76 | 0xe5, 0xfd, 0xe7, 0x83, 0xda, 0xdb, 0xfe, 0xf4, 0x84, 0xec, 0xf6, 0xee, 0xfd, 0xea, 0xf1, 0xf5, 77 | 0xf5, 0xfc, 0xe6, 0xd0, 0x86, 0xdf, 0xc3, 0xe2, 0xe4, 0xd5, 0xd7, 0xe4, 0xe4, 0xce, 0xd4, 0xce, 78 | 0x82, 0xda, 0xc7, 0xda, 0x80, 0xcb, 0xee, 0x8c, 0xd0, 0xde, 0xcd, 0xda, 0xdd, 0xcd, 0xcc, 0xeb, 79 | 0xd2, 0xc3, 0xfc, 0xf2, 0xf6, 0xe9, 0xf8, 0xf8 80 | }; 81 | ``` 82 | 83 | 这个密文是采用XOR加密得来,XOR密钥为 84 | 85 | ``` 86 | \xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba 87 | ``` 88 | 89 | * __请求码__ 90 | 91 | 这是一个Base64编码的字符串,代表的是长度为256字节的数据。这256字节的数据是 __离线激活信息__ 用 __Navicat激活公钥__ 加密的密文。 92 | 93 | * __离线激活请求信息__ 94 | 95 | 这是一个JSON风格的UTF-8字符串。它包含了3个Key:`"K"`、`"DI"`和`"P"`,分别代表 __序列号__、__设备识别码__(与你的电脑硬件信息相关)和 __平台__ (其实就是操作系统类型)。 96 | 97 | 例如: 98 | 99 | ``` 100 | { "K": "xxxxxxxxxxxxxxxx", "P": "Mac 10.13", "DI": "xxxxxxxxxxxxxxxxxxxx" } 101 | ``` 102 | 103 | * __激活码__ 104 | 105 | 这是一个Base64编码的字符串,代表的是长度为256字节的数据。这256字节的数据是 __离线激活回复信息__ 用 __Navicat激活私钥__ 加密的密文。目前我们不知道官方的 __Navicat激活私钥__,所以我们得替换掉软件里的公钥。 106 | 107 | * __离线激活回复信息__ 108 | 109 | 和 __离线激活请求信息__ 一样,它也是一个JSON风格的UTF-8字符串。但是它包含5个Key,分别为`"K"`、`"N"`、`"O"`、`"T"` 和 `"DI"`. 110 | 111 | `"K"` 和 `"DI"` 的意义与 __离线激活请求信息__ 中的相同,且Value必须与 __离线激活请求信息__ 中的相同。 112 | 113 | `"N"`、`"O"`、`"T"` 分别代表 __注册名__、__组织__、__授权时间__。 114 | 115 | __注册名__ 和 __组织__ 的值类型为UTF-8编码的字符串。__授权时间__ 的值类型可以为字符串或整数(感谢@Wizr在issue #10中的报告)。 116 | 117 | 和Windows版本Navicat不同的是,`"T"` 项不可以省略,并且和当前时间的差距必须在-1 ~ +4天之内。 118 | 119 | 例如: 120 | 121 | ``` 122 | { 123 | "DI" : "xxxxxxxxxxxxxxxxxxxx", 124 | "T" : "1515770827.925012", 125 | "K" : "xxxxxxxxxxxxxxxx", 126 | "N" : "DoubleLabyrinth", 127 | "O" : "Shadow" 128 | } 129 | ``` 130 | 131 | * __序列号__ 132 | 133 | 这是一个被分为了4个部分的字符串,其中每个部分都是4个字符长。 134 | 135 | __序列号__ 是通过10个字节的数据来生成的。为了表达方便,我用 __uint8_t data[10]__ 来表示这10个字节。 136 | 137 | 1. __data[0]__ 和 __data[1]__ 必须分别为 `0x68` 和 `0x2A`。 138 | 139 | 这两个字节为Navicat的标志数。 140 | 141 | 2. __data[2]__、__data[3]__ 和 __data[4]__ 可以是任意字节,你想设成什么都行。 142 | 143 | 3. __data[5]__ 和 __data[6]__ 是Navicat的语言标志,值如下: 144 | 145 | | 语言类型 | data[5] | data[6] | 发现者 | 146 | |------------|:---------:|:---------:|-----------------| 147 | | English | 0xAC | 0x88 | | 148 | | 简体中文 | 0xCE | 0x32 | | 149 | | 繁體中文 | 0xAA | 0x99 | | 150 | | 日本語 | 0xAD | 0x82 | @dragonflylee | 151 | | Polski | 0xBB | 0x55 | @dragonflylee | 152 | | Español | 0xAE | 0x10 | @dragonflylee | 153 | | Français | 0xFA | 0x20 | @Deltafox79 | 154 | | Deutsch | 0xB1 | 0x60 | @dragonflylee | 155 | | 한국어 | 0xB5 | 0x60 | @dragonflylee | 156 | | Русский | 0xEE | 0x16 | @dragonflylee | 157 | | Português | 0xCD | 0x49 | @dragonflylee | 158 | 159 | 4. __data[7]__ 是Navicat产品ID。(感谢 @dragonflylee 和 @Deltafox79提供的数据) 160 | 161 | |产品名 |Enterprise|Standard|Educational|Essentials| 162 | |---------------------|:--------:|:------:|:---------:|:--------:| 163 | |Navicat Report Viewer|0x0B | | | | 164 | |Navicat Data Modeler | |0x47 |0x4A | | 165 | |Navicat Premium |0x65 | |0x66 |0x67 | 166 | |Navicat MySQL |0x68 |0x69 |0x6A |0x6B | 167 | |Navicat PostgreSQL |0x6C |0x6D |0x6E |0x6F | 168 | |Navicat Oracle |0x70 |0x71 |0x72 |0x73 | 169 | |Navicat SQL Server |0x74 |0x75 |0x76 |0x77 | 170 | |Navicat SQLite |0x78 |0x79 |0x7A |0x7B | 171 | |Navicat MariaDB |0x7C |0x7D |0x7E |0x7F | 172 | |Navicat MongoDB |0x80 |0x81 |0x82 | | 173 | 174 | 5. __data[8]__ 的高4位代表 __版本号__。低4位未知,但可以用来延长激活期限,可取的值有`0000`和`0001`。 175 | 176 | 例如: 177 | 178 | 对于 __Navicat 12__: 高4位必须是`1100`,为`12`的二进制形式。 179 | 对于 __Navicat 11__: 高4位必须是`1011`,为`11`的二进制形式。 180 | 181 | 6. __data[9]__ 目前暂未知,但如果你想要 __not-for-resale license__ 的话可以设成`0xFD`、`0xFC`或`0xFB`。 182 | 183 | 根据 __Navicat 12 for Mac x64__ 版本残留的符号信息可知: 184 | 185 | * `0xFB`是 __Not-For-Resale-30-days__ license. 186 | * `0xFC`是 __Not-For-Resale-90-days__ license. 187 | * `0xFD`是 __Not-For-Resale-365-days__ license. 188 | * `0xFE`是 __Not-For-Resale__ license. 189 | * `0xFF`是 __Site__ license. 190 | 191 | 之后Navicat使用 __ECB__ 模式的 __DES__ 算法来加密 __data[10]__ 的后8字节,也就是 __data[2]__ 到 __data[9]__ 的部分。 192 | 193 | 相应的DES密钥为: 194 | 195 | ```cpp 196 | const uint8_t DESKey = { 0x64, 0xAD, 0xF3, 0x2F, 0xAE, 0xF2, 0x1A, 0x27 }; 197 | ``` 198 | 199 | 之后使用Base32编码 __data[10]__,其中编码表改为: 200 | 201 | ```cpp 202 | // Thanks for discoveries from @Wizr, issue #10 203 | char EncodeTable[] = "ABCDEFGH8JKLMN9PQRSTUVWXYZ234567"; 204 | ``` 205 | 206 | 编码之后你应该会得到一个16字节长的字符串,并且以"NAV"打头。 207 | 208 | 将16字节的字符串分成4个4字节的小块,然后用`"-"`连接就可以得到 __序列号__。 209 | 210 | ## 2. 激活过程 211 | 212 | 1. 检查用户输入的 __序列号__ 是否合法。 213 | 214 | 2. 在用户点击了`激活`按钮之后,Navicat会先尝试在线激活。如果失败,用户可以选择离线激活。 215 | 216 | 3. Navicat会使用用户输入的 __序列号__ 以及从用户电脑收集来的信息生成 __离线激活请求信息__,然后用 __Navicat激活公钥__ 加密,并将密文用Base64编码,最后得到 __请求码__。 217 | 218 | 4. 正常流程下,__请求码__ 应该通过可联网的电脑发送给Navicat的官方激活服务器。之后Navicat的官方激活服务器会返回一个合法的 __激活码__。 219 | 220 | 但现在我们使用注册机来扮演官方激活服务器的角色,只是Navicat软件里的激活公钥得换成自己的公钥: 221 | 222 | 1. 根据 __请求码__, 获得`"DI"`值和`"K"`值。 223 | 224 | 2. 用`"K"`值、用户名、组织名和`"DI"`值填写 __离线激活回复信息__。 225 | 226 | 3. 用自己的2048位RSA私钥加密 __离线激活回复信息__,你将会得到256字节的密文。 227 | 228 | 4. 用Base64编码这256字节的密文,就可以得到 __激活码__。 229 | 230 | 5. 在Navicat软件中填入 __激活码__ 即可完成离线激活。 231 | -------------------------------------------------------------------------------- /common/ResourceOwned.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | template 6 | class ResourceOwned { 7 | private: 8 | 9 | using __HandleType = typename __ResourceTraits::HandleType; 10 | static_assert(std::is_pod_v<__HandleType>); 11 | 12 | __HandleType pvt_Handle; 13 | __LambdaReleasor pvt_Releasor; 14 | 15 | public: 16 | 17 | // 18 | // Construct from custom releasor. 19 | // ``pvt_Handle` will be set to an invalid value. 20 | // 21 | ResourceOwned(__ResourceTraits, __LambdaReleasor&& Releasor) noexcept : 22 | pvt_Handle(__ResourceTraits::InvalidValue), 23 | pvt_Releasor(std::forward<__LambdaReleasor>(Releasor)) {} 24 | 25 | // 26 | // Construct from handle given and custom releasor. 27 | // 28 | ResourceOwned(__ResourceTraits, const __HandleType& Handle, __LambdaReleasor&& Releasor) noexcept : 29 | pvt_Handle(Handle), 30 | pvt_Releasor(std::forward<__LambdaReleasor>(Releasor)) {} 31 | 32 | // 33 | // ResourceOwned doesn't allow to copy. 34 | // Because it takes `pvt_Handle` exclusively. 35 | // 36 | ResourceOwned(const ResourceOwned<__ResourceTraits, __LambdaReleasor>& Other) = delete; 37 | 38 | // 39 | // ResourceOwned allows to move. 40 | // 41 | ResourceOwned(ResourceOwned<__ResourceTraits, __LambdaReleasor>&& Other) noexcept : 42 | pvt_Handle(Other.pvt_Handle), 43 | pvt_Releasor(std::move(Other.pvt_Releasor)) 44 | { 45 | Other.pvt_Handle = __ResourceTraits::InvalidValue; 46 | } 47 | 48 | // 49 | // ResourceOwned doesn't allow to copy. 50 | // Because it takes `pvt_Handle` exclusively. 51 | // 52 | ResourceOwned<__ResourceTraits, __LambdaReleasor>& 53 | operator=(const ResourceOwned<__ResourceTraits, __LambdaReleasor>& Other) = delete; 54 | 55 | // 56 | // ResourceOwned allows to move. 57 | // 58 | ResourceOwned<__ResourceTraits, __LambdaReleasor>& 59 | operator=(ResourceOwned<__ResourceTraits, __LambdaReleasor>&& Other) noexcept { 60 | this->pvt_Handle = Other.pvt_Handle; 61 | this->pvt_Releasor = std::move(Other.pvt_Releasor); 62 | Other.pvt_Handle = __ResourceTraits::InvalidValue; 63 | return *this; 64 | } 65 | 66 | [[nodiscard]] 67 | operator const __HandleType&() const noexcept { // NOLINT: Allow implicit conversion. 68 | return this->pvt_Handle; 69 | } 70 | 71 | template, typename = std::enable_if_t<__IsPointer>> 72 | [[nodiscard]] 73 | __AsType As() const noexcept { 74 | return reinterpret_cast<__AsType>(this->pvt_Handle); 75 | } 76 | 77 | template, typename = typename std::enable_if_t<__IsPointer>> 78 | [[nodiscard]] 79 | __HandleType operator->() const noexcept { 80 | return this->pvt_Handle; 81 | } 82 | 83 | [[nodiscard]] 84 | bool IsValid() const noexcept { 85 | return __ResourceTraits::IsValid(this->pvt_Handle); 86 | } 87 | 88 | [[nodiscard]] 89 | const __HandleType& Get() const noexcept { 90 | return this->pvt_Handle; 91 | } 92 | 93 | template 94 | [[nodiscard]] 95 | __ReturnType GetAddress() noexcept { 96 | return reinterpret_cast<__ReturnType>(&this->pvt_Handle); 97 | } 98 | 99 | void TakeOver(const __HandleType& Handle) { 100 | if (__ResourceTraits::IsValid(this->pvt_Handle) == true) { 101 | this->pvt_Releasor(this->pvt_Handle); 102 | } 103 | this->pvt_Handle = Handle; 104 | } 105 | 106 | void Discard() noexcept { 107 | this->pvt_Handle = __ResourceTraits::InvalidValue; 108 | } 109 | 110 | [[nodiscard]] 111 | __HandleType Transfer() noexcept { 112 | __HandleType t = this->pvt_Handle; 113 | this->pvt_Handle = __ResourceTraits::InvalidValue; 114 | return t; 115 | } 116 | 117 | void Release() { 118 | if (__ResourceTraits::IsValid(this->pvt_Handle)) { 119 | this->pvt_Releasor(this->pvt_Handle); 120 | this->pvt_Handle = __ResourceTraits::InvalidValue; 121 | } 122 | } 123 | 124 | ~ResourceOwned() { 125 | if (__ResourceTraits::IsValid(this->pvt_Handle)) { 126 | this->pvt_Releasor(this->pvt_Handle); 127 | this->pvt_Handle = __ResourceTraits::InvalidValue; 128 | } 129 | } 130 | }; 131 | 132 | template 133 | class ResourceOwned<__ResourceTraits, void> { 134 | private: 135 | 136 | using __HandleType = typename __ResourceTraits::HandleType; 137 | static_assert(std::is_pod_v<__HandleType>); 138 | 139 | __HandleType pvt_Handle; 140 | 141 | public: 142 | 143 | // 144 | // Construct from custom releasor. 145 | // ``pvt_Handle` will be set to an invalid value. 146 | // 147 | explicit 148 | ResourceOwned(__ResourceTraits) noexcept : 149 | pvt_Handle(__ResourceTraits::InvalidValue) {} 150 | 151 | // 152 | // Construct from handle given and custom releasor. 153 | // 154 | ResourceOwned(__ResourceTraits, const __HandleType& Handle) noexcept : 155 | pvt_Handle(Handle) {} 156 | 157 | // 158 | // ResourceOwned doesn't allow to copy. 159 | // Because it takes `pvt_Handle` exclusively. 160 | // 161 | ResourceOwned(const ResourceOwned<__ResourceTraits, void>& Other) = delete; 162 | 163 | // 164 | // ResourceOwned allows to move. 165 | // 166 | ResourceOwned(ResourceOwned<__ResourceTraits, void>&& Other) noexcept : 167 | pvt_Handle(Other.pvt_Handle) 168 | { 169 | Other.pvt_Handle = __ResourceTraits::InvalidValue; 170 | } 171 | 172 | // 173 | // ResourceOwned doesn't allow to copy. 174 | // Because it takes `pvt_Handle` exclusively. 175 | // 176 | ResourceOwned<__ResourceTraits, void>& 177 | operator=(const ResourceOwned<__ResourceTraits, void>& Other) = delete; 178 | 179 | // 180 | // ResourceOwned allows to move. 181 | // 182 | ResourceOwned<__ResourceTraits, void>& 183 | operator=(ResourceOwned<__ResourceTraits, void>&& Other) noexcept { 184 | this->pvt_Handle = Other.pvt_Handle; 185 | Other.pvt_Handle = __ResourceTraits::InvalidValue; 186 | return *this; 187 | } 188 | 189 | [[nodiscard]] 190 | operator const __HandleType&() const noexcept { // NOLINT: Allow implicit conversion. 191 | return this->pvt_Handle; 192 | } 193 | 194 | template, typename = typename std::enable_if_t<__IsPointer>> 195 | [[nodiscard]] 196 | __AsType As() const noexcept { 197 | return reinterpret_cast<__AsType>(this->pvt_Handle); 198 | } 199 | 200 | template, typename = typename std::enable_if_t<__IsPointer>> 201 | [[nodiscard]] 202 | __HandleType operator->() const noexcept { 203 | return this->pvt_Handle; 204 | } 205 | 206 | [[nodiscard]] 207 | bool IsValid() const noexcept { 208 | return __ResourceTraits::IsValid(this->pvt_Handle); 209 | } 210 | 211 | [[nodiscard]] 212 | const __HandleType& Get() const noexcept { 213 | return this->pvt_Handle; 214 | } 215 | 216 | template 217 | [[nodiscard]] 218 | __ReturnType GetAddress() noexcept { 219 | return reinterpret_cast<__ReturnType>(&this->pvt_Handle); 220 | } 221 | 222 | void TakeOver(const __HandleType& Handle) { 223 | if (__ResourceTraits::IsValid(this->pvt_Handle) == true) { 224 | __ResourceTraits::Releasor(this->pvt_Handle); 225 | } 226 | this->pvt_Handle = Handle; 227 | } 228 | 229 | void Discard() noexcept { 230 | this->pvt_Handle = __ResourceTraits::InvalidValue; 231 | } 232 | 233 | [[nodiscard]] 234 | __HandleType Transfer() noexcept { 235 | __HandleType t = this->pvt_Handle; 236 | this->pvt_Handle = __ResourceTraits::InvalidValue; 237 | return t; 238 | } 239 | 240 | void Release() { 241 | if (__ResourceTraits::IsValid(this->pvt_Handle)) { 242 | __ResourceTraits::Releasor(this->pvt_Handle); 243 | this->pvt_Handle = __ResourceTraits::InvalidValue; 244 | } 245 | } 246 | 247 | ~ResourceOwned() { 248 | if (__ResourceTraits::IsValid(this->pvt_Handle)) { 249 | __ResourceTraits::Releasor(this->pvt_Handle); 250 | this->pvt_Handle = __ResourceTraits::InvalidValue; 251 | } 252 | } 253 | }; 254 | 255 | template 256 | ResourceOwned(__ResourceTraits) -> 257 | ResourceOwned<__ResourceTraits, void>; 258 | 259 | template 260 | ResourceOwned(__ResourceTraits, __ArgType&&) -> 261 | ResourceOwned< 262 | __ResourceTraits, 263 | std::conditional_t< 264 | std::is_same_v>, typename __ResourceTraits::HandleType> == false, 265 | std::remove_reference_t<__ArgType>, 266 | void 267 | > 268 | >; 269 | 270 | template 271 | ResourceOwned(__ResourceTraits, const typename __ResourceTraits::HandleType&, __LambdaReleasor&&) -> 272 | ResourceOwned<__ResourceTraits, std::remove_reference_t<__LambdaReleasor>>; 273 | 274 | template 275 | struct CppObjectTraits { 276 | using HandleType = __ClassType*; 277 | 278 | static inline const HandleType InvalidValue = nullptr; 279 | 280 | [[nodiscard]] 281 | static bool IsValid(const HandleType& Handle) noexcept { 282 | return Handle != InvalidValue; 283 | } 284 | 285 | static void Releasor(const HandleType& Handle) { 286 | delete Handle; 287 | } 288 | }; 289 | 290 | template 291 | struct CppDynamicArrayTraits { 292 | using HandleType = __ClassType*; 293 | 294 | static inline const HandleType InvalidValue = nullptr; 295 | 296 | [[nodiscard]] 297 | static bool IsValid(const HandleType& Handle) noexcept { 298 | return Handle != InvalidValue; 299 | } 300 | 301 | static void Releasor(const HandleType& Handle) { 302 | delete[] Handle; 303 | } 304 | }; 305 | -------------------------------------------------------------------------------- /navicat-keygen/main.cpp: -------------------------------------------------------------------------------- 1 | #include // NOLINT 2 | #include // NOLINT 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../common/RSACipher.hpp" 8 | #include "NavicatKeygen.hpp" 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | template 15 | bool read_int(int& num, const char* prompt, const char* err_msg) { 16 | int temp; 17 | std::string input; 18 | 19 | while (true) { 20 | std::cout << prompt; 21 | if (!std::getline(std::cin, input)) 22 | return false; 23 | 24 | try { 25 | temp = std::stoi(input, nullptr, 0); 26 | if (min_num <= temp && temp <= max_num) { 27 | num = temp; 28 | return true; 29 | } else { 30 | throw std::invalid_argument(err_msg); 31 | } 32 | } catch (...) { 33 | std::cout << err_msg << std::endl; 34 | } 35 | } 36 | } 37 | 38 | std::string base64_encode(const std::vector& bindata); 39 | std::vector base64_decode(const std::string& ascdata); 40 | 41 | void Help() { 42 | std::cout << "***************************************************" << std::endl; 43 | std::cout << "* Navicat Keygen by @DoubleLabyrinth *" << std::endl; 44 | std::cout << "* Version: 4.0 *" << std::endl; 45 | std::cout << "***************************************************" << std::endl; 46 | std::cout << std::endl; 47 | std::cout << "Usage:" << std::endl; 48 | std::cout << " navicat-keygen " << std::endl; 49 | std::cout << std::endl; 50 | std::cout << " Path to a PEM-format RSA-2048 private key file." << std::endl; 51 | std::cout << " This parameter must be specified." << std::endl; 52 | std::cout << std::endl; 53 | } 54 | 55 | int main(int argc, char* argv[]) { 56 | if (argc != 2) { 57 | Help(); 58 | return -1; 59 | } else { 60 | try { 61 | std::cout << "***************************************************" << std::endl; 62 | std::cout << "* Navicat Keygen by @DoubleLabyrinth *" << std::endl; 63 | std::cout << "* Version: 4.0 *" << std::endl; 64 | std::cout << "***************************************************" << std::endl; 65 | std::cout << std::endl; 66 | 67 | RSACipher RsaCipher; 68 | 69 | int select_num; 70 | 71 | NavicatKeygen keygen; 72 | std::string username; 73 | std::string organization; 74 | 75 | std::string RequestCode_b64; 76 | std::vector RequestCode; 77 | char RequestInfo[256] = {}; 78 | char ResponseInfo[256] = {}; 79 | std::vector ResponseCode; 80 | std::string ResponseCode_b64; 81 | 82 | rapidjson::Document json; 83 | rapidjson::Value N_Key; 84 | rapidjson::Value N_Value; 85 | rapidjson::Value O_Key; 86 | rapidjson::Value O_Value; 87 | rapidjson::Value T_Key; 88 | rapidjson::Value T_Value; 89 | rapidjson::StringBuffer buffer; 90 | rapidjson::Writer writer(buffer); 91 | 92 | RsaCipher.ImportKeyFromFile(argv[1]); 93 | if (RsaCipher.Bits() != 2048) { 94 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 95 | throw nkg::Exception(__FILE__, __LINE__, "Not RSA-2048 private key file."); 96 | } 97 | 98 | keygen.SetProductSignature(NavicatKeygen::Product::Premium); 99 | 100 | std::cout 101 | << "Which is your Navicat Premium language?" << std::endl 102 | << "0. English" << std::endl 103 | << "1. Simplified Chinese" << std::endl 104 | << "2. Traditional Chinese" << std::endl 105 | << "3. Japanese" << std::endl 106 | << "4. Polish" << std::endl 107 | << "5. Spanish" << std::endl 108 | << "6. French" << std::endl 109 | << "7. German" << std::endl 110 | << "8. Korean" << std::endl 111 | << "9. Russian" << std::endl 112 | << "10. Portuguese" << std::endl 113 | << std::endl; 114 | if (read_int<0, 10>(select_num, "(Input index)> ", "Invalid index.")) { 115 | keygen.SetLanguageSignature(static_cast(select_num)); 116 | } else { 117 | return -1; 118 | } 119 | 120 | std::cout << std::endl; 121 | 122 | if (read_int<0, 15>(select_num, "(Input major version number, range: 0 ~ 15, default: 12)> ", "Invalid number.")) { 123 | keygen.SetVersion(static_cast(select_num)); 124 | } else { 125 | return -1; 126 | } 127 | 128 | // 129 | // Generate snKey 130 | // 131 | keygen.Generate(); 132 | std::cout << std::endl; 133 | std::cout << "Serial number:" << std::endl; 134 | std::cout << keygen.GetFormatedKey() << std::endl; 135 | std::cout << std::endl; 136 | 137 | // 138 | // Get user name 139 | // 140 | std::cout << "Your name: "; 141 | if (!std::getline(std::cin, username)) { 142 | return -1; 143 | } 144 | 145 | // 146 | // Get organization name 147 | // 148 | std::cout << "Your organization: "; 149 | if (!std::getline(std::cin, organization)) { 150 | return -1; 151 | } 152 | 153 | std::cout << std::endl; 154 | 155 | // 156 | // Get request code in base64 157 | // 158 | std::cout << "Input request code (in Base64), input empty line to end:" << std::endl; 159 | while (true) { 160 | std::string temp; 161 | if (!std::getline(std::cin, temp)) 162 | return 0; 163 | 164 | if (temp.empty()) 165 | break; 166 | 167 | RequestCode_b64 += temp; 168 | } 169 | 170 | // 171 | // Get request code in raw bytes 172 | // 173 | RequestCode = base64_decode(RequestCode_b64); 174 | if (RequestCode.size() > ((RsaCipher.Bits() + 7) / 8)) { 175 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 176 | throw nkg::Exception(__FILE__, __LINE__, "Request code is too long."); 177 | } 178 | 179 | // 180 | // Decrypt to get request info 181 | // 182 | RsaCipher.Decrypt(RequestCode.data(), static_cast(RequestCode.size()), RequestInfo, RSA_PKCS1_PADDING); 183 | 184 | // 185 | // print out request info 186 | // 187 | std::cout << "Request Info:" << std::endl; 188 | std::cout << RequestInfo << std::endl; 189 | std::cout << std::endl; 190 | 191 | // 192 | // Generate response info 193 | // 194 | json.Parse(RequestInfo); 195 | json.RemoveMember("P"); // remove Platform info 196 | 197 | N_Key.SetString("N", 1); 198 | N_Value.SetString(username.c_str(), static_cast(username.length())); 199 | O_Key.SetString("O", 1); 200 | O_Value.SetString(organization.c_str(), static_cast(organization.length())); 201 | T_Key.SetString("T", 1); 202 | T_Value.SetUint(static_cast(std::time(nullptr))); 203 | 204 | json.AddMember(N_Key, N_Value, json.GetAllocator()); 205 | json.AddMember(O_Key, O_Value, json.GetAllocator()); 206 | json.AddMember(T_Key, T_Value, json.GetAllocator()); 207 | 208 | json.Accept(writer); 209 | 210 | if (buffer.GetSize() > 240) { 211 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 212 | throw nkg::Exception(__FILE__, __LINE__, "Response info is too long."); 213 | } 214 | 215 | // 216 | // print out response info 217 | // 218 | memcpy(ResponseInfo, buffer.GetString(), buffer.GetSize()); 219 | std::cout << "Response Info:" << std::endl; 220 | std::cout << ResponseInfo << std::endl; 221 | std::cout << std::endl; 222 | 223 | // 224 | // encrypt response info 225 | // 226 | ResponseCode.resize((RsaCipher.Bits() + 7) / 8); 227 | RsaCipher.Encrypt(ResponseInfo, static_cast(strlen(ResponseInfo)), ResponseCode.data(), RSA_PKCS1_PADDING); 228 | 229 | // 230 | // encode encrypted response info in base64 format 231 | // 232 | ResponseCode_b64 = base64_encode(ResponseCode); 233 | 234 | // 235 | // print out activation code 236 | // 237 | std::cout << "License:" << std::endl; 238 | std::cout << ResponseCode_b64 << std::endl; 239 | 240 | return 0; 241 | } catch (nkg::Exception& e) { 242 | std::cout << "[-] " << e.File() << ":" << e.Line() << " ->" << std::endl; 243 | std::cout << " " << e.Message() << std::endl; 244 | if (e.HasErrorCode()) { 245 | std::cout << " " << e.ErrorString() << std::endl; 246 | } 247 | return -1; 248 | } 249 | } 250 | } 251 | 252 | -------------------------------------------------------------------------------- /README.zh-CN.md: -------------------------------------------------------------------------------- 1 | # Navicat Keygen 2 | 3 | 这份repo将会告诉你Navicat是怎么完成离线激活的。 4 | 5 | [注册机是怎么工作的?](HOW_DOES_IT_WORK.zh-CN.md) 6 | 7 | ## 1. 如何编译 8 | 9 | * 在编译之前,你应该确保你有如下几个库: 10 | 11 | ``` 12 | openssl 13 | capstone 14 | keystone 15 | rapidjson 16 | libplist 17 | ``` 18 | 19 | 如果你有`brew`的话,你可以通过 20 | 21 | ``` 22 | $ brew install openssl 23 | $ brew install capstone 24 | $ brew install keystone 25 | $ brew install rapidjson 26 | $ brew install libplist 27 | ``` 28 | 29 | 来完成它们的安装。 30 | 31 | * Clone `mac` 分支,并编译keygen和patcher 32 | 33 | ```bash 34 | $ git clone -b mac --single-branch https://github.com/DoubleLabyrinth/navicat-keygen.git 35 | $ cd navicat-keygen 36 | $ make all 37 | ``` 38 | 39 | 编译完成后你会在 `bin/` 文件夹下看到两个可执行文件: 40 | 41 | ```bash 42 | $ ls bin/ 43 | navicat-keygen navicat-patcher 44 | ``` 45 | 46 | ## 2. 如何使用这个Keygen 47 | 48 | 1. 编译好keygen和patcher。 49 | 50 | 2. 备份好Navicat中所有已保存的数据库连接(包括密码)。 51 | 52 | 3. 移除所有Navicat在 `Keychain Access.app` (即钥匙链)中保存的连接,如果有的话。 53 | 54 | 你可以通过在 `Keychain Access.app` 中搜索关键词 `navicat` 来找到它们。 55 | 56 | 4. 使用`navicat-patcher`替换掉公钥: 57 | 58 | ``` 59 | Usage: 60 | navicat-patcher [RSA-2048 Private Key File] 61 | 62 | Path to `Navicat Premium.app`. 63 | Example: 64 | /Applications/Navicat\ Premium.app/ 65 | This parameter must be specified. 66 | 67 | [RSA-2048 Private Key File] Path to a PEM-format RSA-2048 private key file. 68 | This parameter is optional. 69 | ``` 70 | 71 | * ``: `Navicat Premium.app` 的路径。 72 | 73 | __这个参数必须指定。__ 74 | 75 | * `[RSA-2048 PrivateKey(PEM file)]`: PEM格式的RSA-2048私钥文件路径。 76 | 77 | __这个参数是可选的。__ 78 | 79 | 如果没有指定,`navicat-patcher`将会在当前目录下生成一个新的RSA-2048私钥文件`RegPrivateKey.pem`。 80 | 81 | __例如:__ 82 | 83 | ```console 84 | $ ./navicat-patcher /Applications/Navicat\ Premium.app/ 85 | ``` 86 | 87 | __Navicat Premium For Mac 12.1.24 简体中文版__ 已通过测试。下面将是一份样例输出: 88 | 89 | ```console 90 | $ ./navicat-patcher /Applications/Navicat\ Premium.app/ 91 | *************************************************** 92 | * Navicat Patcher by @DoubleLabyrinth * 93 | * Version: 4.0 * 94 | *************************************************** 95 | 96 | Press Enter to continue or Ctrl + C to abort. 97 | 98 | [*] Your Navicat version: 12.1.24 99 | 100 | [+] PatchSolution0 ...... Ready to apply. 101 | Keyword offset = +0x024a7db8 102 | [-] PatchSolution1 ...... Omitted. 103 | [+] PatchSolution2 ...... Ready to apply. 104 | Function offset = +0x00ec9868 105 | Keyword offset = +0x0263fd60 106 | std::string::append(const char*) RVA = 0x000000010214b726 107 | 108 | [*] Generating new RSA private key, it may take a long time... 109 | [+] New RSA private key has been saved to RegPrivateKey.pem. 110 | 111 | [*] Your RSA public key: 112 | -----BEGIN PUBLIC KEY----- 113 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA030QIDXRx372bGrne9kp 114 | uuAqpxaxJX0x6LVaOf8+Uan2SQnFH8frnuMDRg5PjdBnFWEJGqZmRD1fNkLOhhCE 115 | iFZWxrDgJcuEBrv5VlduQ4hlYIulcf6qilBZUaaX9Kb3R7+H8ClMb00HwLc/Iht5 116 | bd9krhU3CT3g2ZG00GxVhEF4a/zZMDjeuQvTUeeubIeriT/2YC+w/tKfGbqWvjC6 117 | wkbjXGbVICSiKzhzztS4BHbtQMl8v6doMhFVd/PEDNFQrbkEr3kbk/oD8AccL8iz 118 | aV17UHt4VW2fR8tMyTvcuhTaUtWmt/tL6Z1RzCqH+KvTv8GpH8qFcty89YXja7dL 119 | kQIDAQAB 120 | -----END PUBLIC KEY----- 121 | 122 | ************************************************************** 123 | * PatchSolution0 * 124 | ************************************************************** 125 | @+0x024a7db8 126 | Previous: 127 | -0x0000000000000008 2d 2d 2d 2d 2d 42 45 47 -----BEG 128 | +0x0000000000000008 49 4e 20 50 55 42 4c 49 43 20 4b 45 59 2d 2d 2d IN PUBLIC KEY--- 129 | +0x0000000000000018 2d 2d 00 4d 49 49 42 49 6a 41 4e 42 67 6b 71 68 --.MIIBIjANBgkqh 130 | +0x0000000000000028 6b 69 47 39 77 30 42 41 51 45 46 41 41 4f 43 41 kiG9w0BAQEFAAOCA 131 | ... 132 | ... 133 | ... 134 | After: 135 | -0x0000000000000008 2d 2d 2d 2d 2d 42 45 47 -----BEG 136 | +0x0000000000000008 49 4e 20 50 55 42 4c 49 43 20 4b 45 59 2d 2d 2d IN PUBLIC KEY--- 137 | +0x0000000000000018 2d 2d 00 4d 49 49 42 49 6a 41 4e 42 67 6b 71 68 --.MIIBIjANBgkqh 138 | +0x0000000000000028 6b 69 47 39 77 30 42 41 51 45 46 41 41 4f 43 41 kiG9w0BAQEFAAOCA 139 | ... 140 | ... 141 | ... 142 | 143 | ************************************************************** 144 | * PatchSolution2 * 145 | ************************************************************** 146 | @+0x0263fd60 147 | Previous: 148 | +0x0000000000000000 42 49 6a 57 79 6f 65 52 52 30 4e 42 67 6b 71 6e BIjWyoeRR0NBgkqn 149 | +0x0000000000000010 44 5a 57 78 43 67 4b 43 45 41 77 31 64 71 46 33 DZWxCgKCEAw1dqF3 150 | +0x0000000000000020 44 54 76 4f 42 39 31 5a 48 77 65 63 4a 59 46 72 DTvOB91ZHwecJYFr 151 | +0x0000000000000030 64 4d 31 4b 45 68 31 79 56 65 52 6f 47 71 53 64 dM1KEh1yVeRoGqSd 152 | +0x0000000000000040 4c 4c 47 5a 47 55 6c 6e 67 69 67 33 4f 44 35 6d LLGZGUlngig3OD5m 153 | ... 154 | ... 155 | ... 156 | 157 | @+0x00ec9868 158 | Previous: 159 | -0x0000000000000008 55 48 89 e5 41 57 41 56 UH..AWAV 160 | +0x0000000000000008 53 48 83 ec 38 49 89 fe c6 45 e5 01 31 c0 88 45 SH..8I...E..1..E 161 | +0x0000000000000018 e6 88 45 e7 48 8d 35 c5 70 61 01 48 8d 5d b0 48 ..E.H.5.pa.H.].H 162 | +0x0000000000000028 89 df e8 49 d9 ff ff 48 8d 35 ef ed 60 01 48 89 ...I...H.5..`.H. 163 | +0x0000000000000038 df e8 80 1e 28 01 e8 3f f4 00 00 88 45 e5 e8 b7 ....(..?....E... 164 | +0x0000000000000048 f4 00 00 88 45 e6 e8 2f f5 00 00 88 45 e7 f6 45 ....E../....E..E 165 | After: 166 | -0x0000000000000008 55 48 89 e5 41 57 41 56 UH..AWAV 167 | +0x0000000000000008 53 48 83 ec 48 48 89 fb 48 31 c0 48 89 04 24 48 SH..HH..H1.H..$H 168 | +0x0000000000000018 89 44 24 08 48 89 44 24 10 48 8d 3c 24 48 8d 35 .D$.H.D$.H.<$H.5 169 | +0x0000000000000028 cc 64 77 01 e8 8d 1e 28 01 48 8b 04 24 48 89 03 .dw....(.H..$H.. 170 | +0x0000000000000038 48 8b 44 24 08 48 89 43 08 48 8b 44 24 10 48 89 H.D$.H.C.H.D$.H. 171 | +0x0000000000000048 43 10 48 89 d8 48 83 c4 48 5b 41 5e 41 5f 5d c3 C.H..H..H[A^A_]. 172 | 173 | [+] PatchSolution0 has been applied. 174 | [+] PatchSolution2 has been applied. 175 | 176 | ************************************************************** 177 | * Patch has been done successfully. Have fun and enjoy~~ * 178 | * DO NOT FORGET TO SIGN NAVICAT BY YOUR CERTIFICATE!!! * 179 | ************************************************************** 180 | ``` 181 | 182 | * __仅对 Navicat Premium 版本 < 12.0.24 的说明:__ 183 | 184 | 如果你的Navicat版本小于12.0.24,那么`navicat-patcher`将会终止并且不会修改目标文件。 185 | 186 | 你必须使用openssl生成`RegPrivateKey.pem`和`rpk`文件: 187 | 188 | ```console 189 | $ openssl genrsa -out RegPrivateKey.pem 2048 190 | $ openssl rsa -in RegPrivateKey.pem -pubout -out rpk 191 | ``` 192 | 193 | 接着用刚生成的`rpk`文件替换 194 | 195 | ``` 196 | /Applications/Navicat Premium.app/Contents/Resources/rpk 197 | ``` 198 | 199 | 5. __生成一份自签名的代码证书,并总是信任该证书。这一步非常重要。__ 200 | 201 | __然后用`codesign`对`Navicat Premium.app`重签名。__ 202 | 203 | ```console 204 | $ codesign -f -s "Your self-signed code-sign certificate name" 205 | ``` 206 | 207 | __注意:__ 208 | 209 | "Your self-signed code-sign certificate name"是你证书的名字,不是路径。 210 | 211 | __例如:__ 212 | 213 | ```console 214 | $ codesign -f -s "foobar" /Applications/Navicat\ Premium.app/ 215 | ``` 216 | 217 | 6. 接下来使用`navicat-keygen`来生成 __序列号__ 和 __激活码__。 218 | 219 | ``` 220 | Usage: 221 | navicat-keygen 222 | 223 | Path to a PEM-format RSA-2048 private key file. 224 | This parameter must be specified. 225 | ``` 226 | 227 | * ``: PEM格式的RSA-2048私钥文件路径。 228 | 229 | __这个参数必须指定。__ 230 | 231 | __例如:__ 232 | 233 | ```console 234 | $ ./navicat-keygen ./RegPrivateKey.pem 235 | ``` 236 | 237 | 你会被要求选择Navicat的语言以及输入主版本号。之后会随机生成一个 __序列号__。 238 | 239 | ```console 240 | $ ./navicat-keygen ./RegPrivateKey.pem 241 | *************************************************** 242 | * Navicat Keygen by @DoubleLabyrinth * 243 | * Version: 4.0 * 244 | *************************************************** 245 | 246 | Which is your Navicat Premium language? 247 | 0. English 248 | 1. Simplified Chinese 249 | 2. Traditional Chinese 250 | 3. Japanese 251 | 4. Polish 252 | 5. Spanish 253 | 6. French 254 | 7. German 255 | 8. Korean 256 | 9. Russian 257 | 10. Portuguese 258 | 259 | (Input index)> 1 260 | 261 | (Input major version number, range: 0 ~ 15, default: 12)> 12 262 | 263 | Serial number: 264 | NAVG-Z5H9-NK2L-MAZJ 265 | 266 | Your name: 267 | ``` 268 | 269 | 你可以使用这个 __序列号__ 暂时激活Navicat。 270 | 271 | 接下来你会被要求输入`用户名`和`组织名`;请随便填写,但不要太长。 272 | 273 | ```console 274 | Your name: DoubleLabyrinth 275 | Your organization: DoubleLabyrinth 276 | Input request code (in Base64), input empty line to end: 277 | ``` 278 | 279 | 之后你会被要求填入请求码。注意 __不要关闭注册机__。 280 | 281 | 7. __断开网络__ 并打开Navicat。 282 | 283 | 找到`注册`窗口,填入注册机给你的序列号。然后点击`激活`按钮。 284 | 285 | 8. 一般来说在线激活肯定会失败,这时候Navicat会询问你是否`手动激活`,直接选吧。 286 | 287 | 9. 在`手动激活`窗口你会得到一个请求码,复制它并把它粘贴到keygen里。最后别忘了连按至少两下回车结束输入。 288 | 289 | ```console 290 | Your name: DoubleLabyrinth 291 | Your organization: DoubleLabyrinth 292 | 293 | Input request code (in Base64), input empty line to end: 294 | Nuk6pouXNhuGnqb2rBbxpDOiCFxhdJF4/gteYA/UZFUwqmhhphn3PAErvlxCtbUCf9Lzw02gfIFog3gmTB1C5JzPdeE5uuD6SAvhlQ7ZVOmdA66dvt6mDDpuf78cGio1Rpkd0D/6dLzgHnFJJPOfPtlIT5ZOLDiWkiSJm8d83+ckMBoMtcvpXCiwDIGb1KfVZwsgLojyrrO5OzakIzd2xQ8r3mEmbVbMl/zD0S5fO4agxEOp2WvpmM1cqom9Egll7kgcQG8A0z1Abqo1PrVBjjOsb/v8wy5V/469G6/uDT4AkZQSz8m/TX9ZQlZE3QBlzrJ+sTEkpMVhw3h3u6l4JQ== 295 | 296 | Request Info: 297 | {"K":"NAVGZ5H9NK2LMAZJ", "DI":"NGVlZjdjOTViMzYyMWI0", "P":"MAC"} 298 | 299 | Response Info: 300 | {"K":"NAVGZ5H9NK2LMAZJ","DI":"NGVlZjdjOTViMzYyMWI0","N":"DoubleLabyrinth","O":"DoubleLabyrinth","T":1564415636} 301 | 302 | License: 303 | Vd4QUzEw6DPNpJLYVKV6ZNDny0gsZWCXbKyrf2nF27iTM35YUBouXEcAB/Vy355V2z++7iXe/coKmV4kNZbywlBchI5ts7gOHnhXWzBYQ3yKsBYKob/7sxaiw7CXCmhM4mPLMzrp5okewCWjBjb51keZ4SA3F6j8HGIVYiZW3CAZtkjxs9uUoXvVIJr+Gt83TgU+sqiC4oSplokopAql2zWPieA9KuhPoCKiGLMvuQwv0wWWPc2HorY0AHAetsyZ8MN4utZ2ylQ9z/ZojwX1KViyh3xxnjWF7xXJljIdBA4tCi4QDqDLvTuICfUV7VeKzOUY+ZKCO0xGxkTe1HVwog== 304 | ``` 305 | 306 | 10. 如果不出意外,你会得到一个看似用Base64编码的激活码。 307 | 308 | 直接复制它,并把它粘贴到Navicat的`手动激活`窗口,最后点`激活`按钮。 309 | 310 | 如果没什么意外的话应该能成功激活。 311 | -------------------------------------------------------------------------------- /common/RSACipher.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "ExceptionOpenssl.hpp" 11 | #include "ResourceOwned.hpp" 12 | #include "ResourceTraitsOpenssl.hpp" 13 | 14 | enum class RSAKeyType { 15 | PrivateKey, 16 | PublicKey 17 | }; 18 | 19 | enum class RSAKeyFormat { 20 | PEM, 21 | PKCS1 22 | }; 23 | 24 | class RSACipher { 25 | private: 26 | ResourceOwned pvt_RsaObj; 27 | 28 | template 29 | static void pvt_WriteRSAToBIO(RSA* lpRSA, BIO* lpBIO) { 30 | if constexpr (__Type == RSAKeyType::PrivateKey) { 31 | if (PEM_write_bio_RSAPrivateKey(lpBIO, lpRSA, nullptr, nullptr, 0, nullptr, nullptr) == 0) { 32 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 33 | throw nkg::Exception(__FILE__, __LINE__, "PEM_write_bio_RSAPrivateKey failed."); 34 | } 35 | } else { 36 | if constexpr (__Format == RSAKeyFormat::PEM) { 37 | if (PEM_write_bio_RSA_PUBKEY(lpBIO, lpRSA) == 0) { 38 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 39 | throw nkg::Exception(__FILE__, __LINE__, "PEM_write_bio_RSA_PUBKEY failed."); 40 | } 41 | } else if constexpr (__Format == RSAKeyFormat::PKCS1) { 42 | if (PEM_write_bio_RSAPublicKey(lpBIO, lpRSA) == 0) { 43 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 44 | throw nkg::Exception(__FILE__, __LINE__, "PEM_write_bio_RSAPublicKey failed."); 45 | } 46 | } else { 47 | static_assert(__Format == RSAKeyFormat::PEM || __Format == RSAKeyFormat::PKCS1); 48 | __builtin_unreachable(); 49 | } 50 | } 51 | } 52 | 53 | template 54 | [[nodiscard]] 55 | static RSA* pvt_ReadRSAFromBIO(BIO* lpBIO) { 56 | RSA* lpRSA; 57 | 58 | if constexpr (_Type == RSAKeyType::PrivateKey) { 59 | lpRSA = PEM_read_bio_RSAPrivateKey(lpBIO, nullptr, nullptr, nullptr); 60 | if (lpRSA == nullptr) { 61 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 62 | throw nkg::Exception(__FILE__, __LINE__, "PEM_read_bio_RSAPrivateKey failed."); 63 | } 64 | } else { 65 | if constexpr (_Format == RSAKeyFormat::PEM) { 66 | lpRSA = PEM_read_bio_RSA_PUBKEY(lpBIO, nullptr, nullptr, nullptr); 67 | if (lpRSA == nullptr) { 68 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 69 | throw nkg::Exception(__FILE__, __LINE__, " -> PEM_read_bio_RSA_PUBKEY failed."); 70 | } 71 | } else if constexpr (_Format == RSAKeyFormat::PKCS1) { 72 | lpRSA = PEM_read_bio_RSAPublicKey(lpBIO, nullptr, nullptr, nullptr); 73 | if (lpRSA == nullptr) { 74 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 75 | throw nkg::Exception(__FILE__, __LINE__, "PEM_read_bio_RSAPublicKey failed."); 76 | } 77 | } else { 78 | static_assert(_Format == RSAKeyFormat::PEM || _Format == RSAKeyFormat::PKCS1); 79 | __builtin_unreachable(); 80 | } 81 | } 82 | 83 | return lpRSA; 84 | } 85 | 86 | public: 87 | 88 | RSACipher() : pvt_RsaObj(OpensslRSATraits{}, RSA_new()) { 89 | if (pvt_RsaObj.IsValid() == false) { 90 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 91 | throw nkg::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_new failed."); 92 | } 93 | } 94 | 95 | [[nodiscard]] 96 | size_t Bits() const { 97 | #if (OPENSSL_VERSION_NUMBER & 0xffff0000) == 0x10000000 // openssl 1.0.x 98 | if (pvt_RsaObj->n == nullptr) { 99 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 100 | throw nkg::Exception(__FILE__, __LINE__, "RSA modulus has not been set."); 101 | } else { 102 | return BN_num_bits(pvt_RsaObj->n); 103 | } 104 | #elif (OPENSSL_VERSION_NUMBER & 0xffff0000) == 0x10100000 // openssl 1.1.x 105 | return RSA_bits(pvt_RsaObj); 106 | #else 107 | #error "Unexpected openssl version!" 108 | #endif 109 | } 110 | 111 | void GenerateKey(int bits, unsigned int e = RSA_F4) { 112 | ResourceOwned bn_e(OpensslBNTraits{}, BN_new()); 113 | 114 | if (bn_e.IsValid() == false) { 115 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 116 | throw nkg::OpensslError(__FILE__, __LINE__, ERR_get_error(), "BN_new failed."); 117 | } 118 | 119 | if (!BN_set_word(bn_e, e)) { 120 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 121 | throw nkg::Exception(__FILE__, __LINE__, "BN_set_word failed."); 122 | } 123 | 124 | if (!RSA_generate_key_ex(pvt_RsaObj, bits, bn_e, nullptr)) { 125 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 126 | throw nkg::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_generate_key_ex failed."); 127 | } 128 | } 129 | 130 | template 131 | void ExportKeyToFile(const std::string& FileName) const { 132 | ResourceOwned BioFile(OpensslBIOTraits{}, BIO_new_file(FileName.c_str(), "w")); 133 | 134 | if (BioFile.IsValid() == false) { 135 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 136 | throw nkg::Exception(__FILE__, __LINE__, "BIO_new_file failed."); 137 | } 138 | 139 | pvt_WriteRSAToBIO<__Type, __Format>(pvt_RsaObj, BioFile); 140 | } 141 | 142 | template 143 | [[nodiscard]] 144 | std::string ExportKeyString() const { 145 | ResourceOwned BioMemory(OpensslBIOTraits{}, BIO_new(BIO_s_mem())); 146 | long StringLength; 147 | const char* StringChars = nullptr; 148 | 149 | if (BioMemory.IsValid() == false) { 150 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 151 | throw nkg::Exception(__FILE__, __LINE__, "BIO_new failed."); 152 | } 153 | 154 | pvt_WriteRSAToBIO<__Type, __Format>(pvt_RsaObj, BioMemory); 155 | 156 | StringLength = BIO_get_mem_data(BioMemory, &StringChars); 157 | 158 | return std::string(StringChars, StringLength); 159 | } 160 | 161 | template 162 | void ImportKeyFromFile(const std::string& FileName) { 163 | ResourceOwned BioFile(OpensslBIOTraits{}, BIO_new_file(FileName.c_str(), "r")); 164 | 165 | if (BioFile.IsValid() == false) { 166 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 167 | throw nkg::Exception(__FILE__, __LINE__, "BIO_new_file failed."); 168 | } 169 | 170 | pvt_RsaObj.TakeOver(pvt_ReadRSAFromBIO<__Type, __Format>(BioFile)); 171 | } 172 | 173 | template 174 | void ImportKeyString(const std::string& KeyString) { 175 | ResourceOwned BioMemory(OpensslBIOTraits{}, BIO_new(BIO_s_mem())); 176 | RSA* NewRsaObj; 177 | 178 | if (BioMemory.IsValid() == false) { 179 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 180 | throw nkg::Exception(__FILE__, __LINE__, "BIO_new failed."); 181 | } 182 | 183 | if (BIO_puts(BioMemory, KeyString.c_str()) <= 0) { 184 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 185 | throw nkg::Exception(__FILE__, __LINE__, "BIO_puts failed."); 186 | } 187 | 188 | pvt_RsaObj.TakeOver(pvt_ReadRSAFromBIO<__Type, __Format>(BioMemory)); 189 | } 190 | 191 | template 192 | size_t Encrypt(const void* lpFrom, size_t cbFrom, void* lpTo, int Padding) const { 193 | int BytesWritten; 194 | 195 | if (cbFrom > INT_MAX) { 196 | // NOLINTNEXTLINE: allow exceptions that is not derived lpFrom std::exception 197 | throw nkg::Exception(__FILE__, __LINE__, "Length overflowed."); 198 | } 199 | 200 | if constexpr (__Type == RSAKeyType::PrivateKey) { 201 | BytesWritten = RSA_private_encrypt( 202 | static_cast(cbFrom), 203 | reinterpret_cast(lpFrom), 204 | reinterpret_cast(lpTo), 205 | pvt_RsaObj, 206 | Padding 207 | ); 208 | 209 | if (BytesWritten == -1) { 210 | // NOLINTNEXTLINE: allow exceptions that is not derived lpFrom std::exception 211 | throw nkg::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_private_encrypt failed."); 212 | } 213 | } else { 214 | BytesWritten = RSA_public_encrypt( 215 | static_cast(cbFrom), 216 | reinterpret_cast(lpFrom), 217 | reinterpret_cast(lpTo), 218 | pvt_RsaObj, 219 | Padding 220 | ); 221 | 222 | if (BytesWritten == -1) { 223 | // NOLINTNEXTLINE: allow exceptions that is not derived lpFrom std::exception 224 | throw nkg::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_public_encrypt failed."); 225 | } 226 | } 227 | 228 | return BytesWritten; 229 | } 230 | 231 | template 232 | size_t Decrypt(const void* lpFrom, int cbFrom, void* lpTo, int Padding) const { 233 | int BytesWritten; 234 | 235 | if (cbFrom > INT_MAX) { 236 | // NOLINTNEXTLINE: allow exceptions that is not derived lpFrom std::exception 237 | throw nkg::Exception(__FILE__, __LINE__, "Length overflowed."); 238 | } 239 | 240 | if constexpr (__Type == RSAKeyType::PrivateKey) { 241 | BytesWritten = RSA_private_decrypt( 242 | cbFrom, 243 | reinterpret_cast(lpFrom), 244 | reinterpret_cast(lpTo), 245 | pvt_RsaObj, 246 | Padding 247 | ); 248 | 249 | if (BytesWritten == -1) { 250 | // NOLINTNEXTLINE: allow exceptions that is not derived lpFrom std::exception 251 | throw nkg::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_private_decrypt failed."); 252 | } 253 | } else { 254 | BytesWritten = RSA_public_decrypt( 255 | cbFrom, 256 | reinterpret_cast(lpFrom), 257 | reinterpret_cast(lpTo), 258 | pvt_RsaObj, 259 | Padding 260 | ); 261 | 262 | if (BytesWritten == -1) { 263 | // NOLINTNEXTLINE: allow exceptions that is not derived lpFrom std::exception 264 | throw nkg::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_public_decrypt failed."); 265 | } 266 | } 267 | 268 | return BytesWritten; 269 | } 270 | }; -------------------------------------------------------------------------------- /HOW_DOES_IT_WORK.md: -------------------------------------------------------------------------------- 1 | # Navicat Keygen - How does it work? 2 | 3 | [中文版 How does it work?](HOW_DOES_IT_WORK.zh-CN.md) 4 | 5 | ## 1. Keyword Explanation. 6 | 7 | * __Navicat Activation Public Key__ 8 | 9 | It is an __RSA-2048__ public key that Navicat used to encrypt or decrypt offline activation information. 10 | 11 | It is stored in 12 | 13 | ``` 14 | Navicat Premium.app/Contents/Resources/rpk 15 | ``` 16 | 17 | You can see it by any kind of text editor. The content is: 18 | 19 | ``` 20 | -----BEGIN PUBLIC KEY----- 21 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I 22 | qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv 23 | a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF 24 | R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2 25 | WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt 26 | YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ 27 | awIDAQAB 28 | -----END PUBLIC KEY----- 29 | ``` 30 | 31 | If you have the corresponding private key, please tell me. I would be very appreciated for your generous. 32 | 33 | __NOTICE:__ 34 | 35 | Start from __Navicat Premuim 12.0.24 for Mac__, the public key is no longer stored in 36 | 37 | ``` 38 | Navicat Premium.app/Contents/Resources/rpk 39 | ``` 40 | 41 | Instead, the public key is stored in Navicat executable file 42 | 43 | ``` 44 | Navicat Premium.app/Contents/MacOS/Navicat Premium 45 | ``` 46 | 47 | in plaintext. You can see it by searching string `"-----BEGIN PUBLIC KEY----- "`. 48 | 49 | __NOTICE:__ 50 | 51 | Start from __Navicat Premium 12.1.14 for Mac__, the public key is still stored in the executable file in plaintext. 52 | 53 | However, it does not load the key from the plaintext. Instead, it loads the key from a piece of ciphertext which is 0x188 bytes long. The ciphertext is 54 | 55 | ```c 56 | const uint8_t ciphertext[0x188] = { 57 | 0xfe, 0xfd, 0xfc, 0xf4, 0xfe, 0xd2, 0xf8, 0xf4, 0xf1, 0xd3, 0xde, 0xc7, 0xdf, 0xd3, 0xd0, 0xfd, 58 | 0x8a, 0xc3, 0x85, 0xf4, 0xf6, 0xe9, 0xfc, 0xfc, 0xf2, 0xf5, 0xfa, 0xf5, 0xf6, 0xe9, 0x81, 0xfb, 59 | 0xfe, 0xfd, 0xfc, 0xf4, 0xf4, 0xdf, 0xf2, 0xf9, 0xf2, 0xe5, 0xf0, 0xf7, 0xc0, 0x89, 0xdd, 0xcb, 60 | 0xf5, 0x87, 0xe6, 0xdd, 0xf4, 0xd9, 0xf8, 0xfb, 0xde, 0xf9, 0xcf, 0xc5, 0x8f, 0x80, 0x80, 0xf3, 61 | 0xc2, 0xd0, 0xe2, 0x8f, 0xfa, 0x8a, 0xdd, 0xf3, 0xd7, 0xdc, 0x86, 0xdc, 0xf0, 0x81, 0xc0, 0xea, 62 | 0xd0, 0xd9, 0xf9, 0xd8, 0xda, 0xf2, 0xd0, 0xfd, 0xc3, 0xf6, 0xf3, 0x82, 0xf2, 0x81, 0xef, 0xf2, 63 | 0xe0, 0xf9, 0xf2, 0xd3, 0x8f, 0xd7, 0xe9, 0xfb, 0xca, 0x86, 0xde, 0xfc, 0xf3, 0xd5, 0xdd, 0xf4, 64 | 0xc7, 0x80, 0xf7, 0xd5, 0xf2, 0xc1, 0xde, 0xcc, 0xc0, 0xc7, 0xf0, 0xd0, 0xd0, 0xd1, 0xd7, 0xcc, 65 | 0xd2, 0x81, 0xc1, 0x83, 0xdd, 0xd5, 0x8a, 0x8f, 0x81, 0xe1, 0xf4, 0xd9, 0xf3, 0xd7, 0xca, 0xef, 66 | 0xf9, 0xdf, 0xe1, 0xee, 0xf0, 0xe9, 0xd1, 0xca, 0xf2, 0xe3, 0xf8, 0xf0, 0x83, 0xde, 0xfb, 0xd7, 67 | 0xf1, 0xc4, 0xfa, 0x85, 0xf2, 0xdd, 0xdd, 0xfd, 0x85, 0x86, 0xc7, 0xf9, 0xc4, 0xc9, 0xf4, 0xf8, 68 | 0xd4, 0xd9, 0xe6, 0xd2, 0xf6, 0xc1, 0xc1, 0xf9, 0xe0, 0xe4, 0xf7, 0xe4, 0xfd, 0xf1, 0xf6, 0xfc, 69 | 0xe1, 0x84, 0xe4, 0xd1, 0xed, 0xfe, 0xdb, 0xe8, 0xdd, 0xe1, 0x85, 0xd0, 0xc5, 0xd2, 0x8a, 0x8e, 70 | 0xd5, 0xdd, 0xe3, 0xdb, 0xd0, 0xe1, 0xd0, 0xf6, 0xc6, 0xee, 0xe6, 0xf7, 0xda, 0xf1, 0xdb, 0xc9, 71 | 0x8b, 0xee, 0xcd, 0xdf, 0xff, 0xe8, 0xdd, 0xca, 0x82, 0xdb, 0xf1, 0x82, 0xc3, 0xed, 0xc9, 0xcc, 72 | 0xc0, 0xf2, 0xd6, 0xdf, 0x83, 0xe9, 0xf3, 0xce, 0xea, 0xfa, 0xdf, 0xf8, 0xd9, 0xff, 0xec, 0x88, 73 | 0xe4, 0xe4, 0xfd, 0x80, 0xc5, 0xce, 0xfa, 0xd2, 0xf4, 0xd8, 0x84, 0xff, 0xe5, 0xf3, 0xcb, 0xc2, 74 | 0xfe, 0xc0, 0xc4, 0xfa, 0xde, 0xdd, 0xd5, 0xc9, 0xc5, 0xd5, 0xdf, 0xe3, 0xdd, 0xc1, 0xcb, 0xdd, 75 | 0xfc, 0xf7, 0x83, 0xf8, 0xda, 0xc1, 0xd4, 0xe3, 0xfe, 0xc2, 0xef, 0xf8, 0xf2, 0xea, 0x8a, 0xd2, 76 | 0xc7, 0xf2, 0xf0, 0xc2, 0xfb, 0x89, 0xdc, 0xeb, 0xd1, 0xf7, 0xcc, 0xe2, 0xd1, 0xfc, 0xd4, 0xce, 77 | 0xea, 0xcd, 0xe4, 0x87, 0xe0, 0xcc, 0x8d, 0xf5, 0xc7, 0x85, 0x87, 0xda, 0xcf, 0xde, 0x89, 0xcd, 78 | 0xe5, 0xfd, 0xe7, 0x83, 0xda, 0xdb, 0xfe, 0xf4, 0x84, 0xec, 0xf6, 0xee, 0xfd, 0xea, 0xf1, 0xf5, 79 | 0xf5, 0xfc, 0xe6, 0xd0, 0x86, 0xdf, 0xc3, 0xe2, 0xe4, 0xd5, 0xd7, 0xe4, 0xe4, 0xce, 0xd4, 0xce, 80 | 0x82, 0xda, 0xc7, 0xda, 0x80, 0xcb, 0xee, 0x8c, 0xd0, 0xde, 0xcd, 0xda, 0xdd, 0xcd, 0xcc, 0xeb, 81 | 0xd2, 0xc3, 0xfc, 0xf2, 0xf6, 0xe9, 0xf8, 0xf8 82 | }; 83 | ``` 84 | 85 | The ciphertext is encrypted by XOR encryption where XOR key is 86 | 87 | ``` 88 | \xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba 89 | ``` 90 | 91 | * __Request Code__ 92 | 93 | It is a Base64 string that represents 256-bytes-long data, while the 256-bytes-long data is the ciphertext of __Offline Activation Request Information__ encrypted by __Navicat Activation Public Key__. 94 | 95 | * __Offline Activation Request Information__ 96 | 97 | It is just a JSON-style UTF-8 string which contains 3 items. Respectively they are `"K"`, `"DI"` and `"P"`, which represent __snKey__, __DeviceIdentifier__, __Platform__. 98 | 99 | Example: 100 | 101 | ``` 102 | { "K": "xxxxxxxxxxxxxxxx", "P": "Mac 10.13", "DI": "xxxxxxxxxxxxxxxxxxxx" } 103 | ``` 104 | 105 | * __Activation Code__ 106 | 107 | It is a Base64 string that represents 256-bytes-long data, while the 256-bytes-long data is the ciphertext of __Offline Activation Response Information__ encrypted by __Navicat Activation Private Key__ which, so far, we don't know. 108 | 109 | * __Offline Activation Response Information__ 110 | 111 | Just like __Offline Activation Request Information__, it is also a JSON-style UTF-8 string. But it contains 5 items. Respectively they are `"K"`, `"N"`, `"O"`, `"T"`, '`DI`'. 112 | 113 | `"K"` and `"DI"` has the same meaning mentioned in __Offline Activation Request Information__ and must be same with the corresponding items in __Offline Activation Request Information__. 114 | 115 | `"N"`, `"O"`, `"T"` represent __Name__, __Organization__, __Time__ respectively. __Name__ and __Organization__ are string and the type of __Time__ can be string or integer (Thanks for discoveries from @Wizr, issue #10). 116 | 117 | Differ from Navicat Windows version, `"T"` is mandatory and must have -1 ~ +4 days difference from current time. 118 | 119 | Example: 120 | 121 | ``` 122 | { 123 | "DI" : "xxxxxxxxxxxxxxxxxxxx", 124 | "T" : "1515770827.925012", 125 | "K" : "xxxxxxxxxxxxxxxx", 126 | "N" : "DoubleLabyrinth", 127 | "O" : "Shadow" 128 | } 129 | ``` 130 | 131 | * __snKey__ 132 | 133 | It is a 4-block-long string, while every block is 4-chars-long. 134 | 135 | __snKey__ is generated by 10-bytes-long data. In order to explain it easily, I use __uint8_t data[10]__ to represent the 10-bytes-long data. 136 | 137 | 1. __data[0]__ and __data[1]__ must be `0x68` and `0x2A` respectively. 138 | 139 | These two bytes are Naivcat signature number. 140 | 141 | 2. __data[2]__, __data[3]__ and __data[4]__ can be any byte. Just set them whatever you want. 142 | 143 | 3. __data[5]__ and __data[6]__ are related with your Navicat product language. 144 | 145 | | Language | data[5] | data[6] | Discoverer | 146 | |------------|-----------|-----------|-----------------| 147 | | English | 0xAC | 0x88 | | 148 | | 简体中文 | 0xCE | 0x32 | | 149 | | 繁體中文 | 0xAA | 0x99 | | 150 | | 日本語 | 0xAD | 0x82 | @dragonflylee | 151 | | Polski | 0xBB | 0x55 | @dragonflylee | 152 | | Español | 0xAE | 0x10 | @dragonflylee | 153 | | Français | 0xFA | 0x20 | @Deltafox79 | 154 | | Deutsch | 0xB1 | 0x60 | @dragonflylee | 155 | | 한국어 | 0xB5 | 0x60 | @dragonflylee | 156 | | Русский | 0xEE | 0x16 | @dragonflylee | 157 | | Português | 0xCD | 0x49 | @dragonflylee | 158 | 159 | 4. __data[7]__ is Navicat product ID. (Thanks @dragonflylee and @Deltafox79) 160 | 161 | |Product Name |Enterprise|Standard|Educational|Essentials| 162 | |---------------------|:--------:|:------:|:---------:|:--------:| 163 | |Navicat Report Viewer|0x0B | | | | 164 | |Navicat Data Modeler | |0x47 |0x4A | | 165 | |Navicat Premium |0x65 | |0x66 |0x67 | 166 | |Navicat MySQL |0x68 |0x69 |0x6A |0x6B | 167 | |Navicat PostgreSQL |0x6C |0x6D |0x6E |0x6F | 168 | |Navicat Oracle |0x70 |0x71 |0x72 |0x73 | 169 | |Navicat SQL Server |0x74 |0x75 |0x76 |0x77 | 170 | |Navicat SQLite |0x78 |0x79 |0x7A |0x7B | 171 | |Navicat MariaDB |0x7C |0x7D |0x7E |0x7F | 172 | |Navicat MongoDB |0x80 |0x81 |0x82 | | 173 | 174 | 5. High 4 bits of __data[8]__ represents __major version number__. 175 | 176 | Low 4 bits is unknown, but we can use it to delay activation deadline. Possible values are `0000` or `0001`. 177 | 178 | __Example:__ 179 | 180 | For __Navicat 12 x64__: High 4 bits must be `1100`, which is the binary of number `12`. 181 | For __Navicat 11 x64__: High 4 bits must be `1011`, which is the binary of number `11`. 182 | 183 | 6. __data[9]__ is unknown, but you can set it by `0xFD`, `0xFC` or `0xFB` if you want to use __not-for-resale license__. 184 | 185 | According to symbol information in __Navicat 12 for Mac x64__ version: 186 | 187 | * `0xFB` is __Not-For-Resale-30-days__ license. 188 | * `0xFC` is __Not-For-Resale-90-days__ license. 189 | * `0xFD` is __Not-For-Resale-365-days__ license. 190 | * `0xFE` is __Not-For-Resale__ license. 191 | * `0xFF` is __Site__ license. 192 | 193 | After that. Navicat use __DES__ with __ECB mode__ to encrypt the last 8 bytes which are from __data[2]__ to __data[9]__. 194 | 195 | The DES key is: 196 | 197 | ```cpp 198 | const uint8_t DESKey = { 0x64, 0xAD, 0xF3, 0x2F, 0xAE, 0xF2, 0x1A, 0x27 }; 199 | ``` 200 | 201 | Then use Base32 to encode `uint8_t data[10]` whose encode table is 202 | 203 | ```cpp 204 | // Thanks for discoveries from @Wizr, issue #10 205 | char EncodeTable[] = "ABCDEFGH8JKLMN9PQRSTUVWXYZ234567"; 206 | ``` 207 | 208 | After encoding, you will get a 16-char-long string starting with `"NAV"`. 209 | 210 | Finally, divide the 16-char-long string to four 4-chars-long blocks and join them with `"-"` then you will get __snKey__. 211 | 212 | ## 2. Activation Process 213 | 214 | 1. Check whether __snKey__ that user inputs is valid. 215 | 216 | 2. After user clicks `Activate`, Navicat will start online activation first. If fails, user can choose offline activation. 217 | 218 | 3. Navicat will use the __snKey__ that user inputs and some information collected from user's machine to generate __Offline Activation Request Information__. Then Navicat will encrypt it by __Navicat Activation Public Key__ and return a Base64-encoded string as __Request Code__. 219 | 220 | 4. In legal way, the __Request Code__ should be sent to Navicat official activation server by a Internet-accessible computer. And Navicat official activation server will return a legal __Activation Code__. 221 | 222 | But now, we use keygen to play the official activation server's role. 223 | 224 | 1. According to the __Request Code__, get `"DI"` value and `"K"` value. 225 | 226 | 2. Fill __Offline Activation Response Information__ with `"K"` value, name, organization name, `"DI"` value and `"T"` value. 227 | 228 | 3. Encrypt __Offline Activation Response Information__ by __Navicat Activation Private Key__ and you will get 256-byte-long data. 229 | 230 | 4. Encode the 256-byte-long data by Base64. The result is __Activation Code__. 231 | 232 | 5. After user input __Activation Code__, offline activation is done successfully. 233 | 234 | -------------------------------------------------------------------------------- /navicat-patcher/PatchSolution2.cpp: -------------------------------------------------------------------------------- 1 | #include "PatchSolutions.hpp" 2 | #include 3 | 4 | const char PatchSolution2::Keyword[1114] = 5 | "BIjWyoeRR0NBgkqnDZWxCgKCEAw1dqF3DTvOB91ZHwecJYFrdM1KEh" 6 | "1yVeRoGqSdLLGZGUlngig3OD5mMzs889IqWqqfHSeHMvzyg1p6UPCY" 7 | "nesxa9M2dDUrXHomRHOFHSfsbSXRFwt5GivtnJG9lLJHZ7XWeIQABi" 8 | "dKionYD3O6c9tvUAoDosUJAdQ1RaSXTzyETbHTRtnTPeLpO3EedGMs" 9 | "v3jG9yPcmmdYkddSeJRwn2raPJmnvdHScHUACw0sUNuosAqPaQbTQN" 10 | "PATDzcrnd1Sf8RIbUp4MQJFVJugPLVZbP53Gjtyyniqe5q75kva8Qm" 11 | "Hr1uOuXkVppe3cwECaGamupG43L1XfcpRjCMrxRep3s2VlbL01xmfz" 12 | "5cIhrj34iVmgZSAmIb8ZxiHPdp1oDMFkbNetZyWegqjAHQQ9eoSOTD" 13 | "bERbKEwZ5FLeLsbNAxfqsapB1XBvCavFHualx6bxVxuRQceh4z8kaZ" 14 | "iv2pOKbZQSJ2Dx5HEq0bYZ6y6b7sN9IaeDFNQwjzQn1K7k3XlYAPWC" 15 | "IvDe8Ln0FUe4yMNmuUhu5RTjxE05hUqtz1HjJvYQ9Es1VA6LflKQ87" 16 | "TwIXBNvfrcHaZ72QM4dQtDUyEMrLgMDkJBDM9wqIDps65gSlAz6eHD" 17 | "8tYWUttrWose0cH0yykVnqFzPtdRiZyZRfio6lGyK48mIC9z7T6MN3" 18 | "a7OaLZHZSwzcpQLcGi7M9q1wXLq4Ms1UvlwntB9FLHc63tHPpG8rhn" 19 | "XhZIk4QrSm4GYuEKQVHwku6ulw6wfggVL8FZPhoPCGsrb2rQGurBUL" 20 | "3lkVJ6RO9VGHcczDYomXqAJqlt4y9pkQIj9kgwTrxTzEZgMGdYZqsV" 21 | "4Bd5JjtrL7u3LA0N2Hq9Xvmmis2jDVhSQoUoGukNIoqng3SBsf0E7b" 22 | "4W0S1aZSSOJ90nQHQkQShE9YIMDBbNwIg2ncthwADYqibYUgIvJcK9" 23 | "89XHnYmZsdMWtt53lICsXE1vztR5WrQjSw4WXDiB31LXTrvudCB6vw" 24 | "kCQa4leutETpKLJ2bYaOYBdoiBFOwvf36YaSuRoY4SP2x1pWOwGFTg" 25 | "d90J2uYyCqUa3Q3iX52iigT4EKL2vJKdJ"; 26 | 27 | const uint8_t PatchSolution2::FunctionHeader[9] = { 28 | 0x55, // push rbp 29 | 0x48, 0x89, 0xe5, // mov rbp, rsp 30 | 0x41, 0x57, // push r15 31 | 0x41, 0x56, // push r14 32 | 0x53, // push rbx 33 | }; 34 | 35 | PatchSolution2::PatchSolution2(const X64ImageInterpreter& Image) noexcept : 36 | pvt_Image(Image), 37 | pvt_Disassembler(CapstoneDisassembler::Create(CS_ARCH_X86, CS_MODE_64)), 38 | pvt_Assembler(KeystoneAssembler::Create(KS_ARCH_X86, KS_MODE_64)), 39 | pvt_FunctionOffset(X64ImageInterpreter::InvalidOffset), 40 | pvt_KeywordOffset(X64ImageInterpreter::InvalidOffset), 41 | pvt_StdStringAppendStubRva(X64ImageInterpreter::InvalidAddress) 42 | { 43 | pvt_Disassembler.Option(CS_OPT_DETAIL, CS_OPT_ON); 44 | } 45 | 46 | bool PatchSolution2::FindPatchOffset() noexcept { 47 | auto FunctionOffset = X64ImageInterpreter::InvalidOffset; 48 | auto KeywordOffset = X64ImageInterpreter::InvalidOffset; 49 | auto StdStringAppendStubRva = X64ImageInterpreter::InvalidAddress; 50 | 51 | try { 52 | auto Sec__text = pvt_Image.ImageSection("__TEXT", "__text"); 53 | auto Sec__const = pvt_Image.ImageSection("__TEXT", "__const"); 54 | auto Sec__stubs = pvt_Image.ImageSection("__TEXT", "__stubs"); 55 | auto SecView__text = pvt_Image.SectionView(Sec__text); 56 | auto SecView__const = pvt_Image.SectionView(Sec__const); 57 | auto SecView__stubs = pvt_Image.SectionView(Sec__stubs); 58 | 59 | KeywordOffset = pvt_Image.SearchSectionOffset("__TEXT", "__const", [](const uint8_t* p) { 60 | return memcmp(p, Keyword, sizeof(Keyword)) == 0; 61 | }); 62 | 63 | auto KeywordRva = pvt_Image.OffsetToRva(KeywordOffset); 64 | 65 | auto Hint = pvt_Image.SearchSectionOffset("__TEXT", "__text", [Sec__text, SecView__text, KeywordRva](const uint8_t* p) { 66 | auto rip = (p - SecView__text) + Sec__text->addr + 4; 67 | auto off = *reinterpret_cast(p); 68 | return rip + off == KeywordRva; 69 | }) - 0xc0; 70 | 71 | for (uint32_t i = 0; i < 0xc0; ++i) { 72 | if (memcmp(pvt_Image.ImageOffset(Hint + i), FunctionHeader, sizeof(FunctionHeader)) == 0) { 73 | FunctionOffset = Hint + i; 74 | break; 75 | } 76 | } 77 | 78 | if (FunctionOffset == X64ImageInterpreter::InvalidOffset) { 79 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 80 | throw nkg::Exception(__FILE__, __LINE__, "Not found."); 81 | } 82 | 83 | pvt_Disassembler.SetContext(SecView__stubs, Sec__stubs->size, Sec__stubs->addr); 84 | while (pvt_Disassembler.Next()) { 85 | auto insn = pvt_Disassembler.GetInstruction(); 86 | 87 | // 88 | // As far as I know, all stub functions have a pattern looking like: 89 | // jmp qword ptr [RIP + xxxx] 90 | // 91 | if (strcasecmp(insn->mnemonic, "jmp") == 0 && insn->detail->x86.operands[0].type == X86_OP_MEM && insn->detail->x86.operands[0].mem.base == X86_REG_RIP) { 92 | uint64_t la_symbol_ptr_rva = pvt_Disassembler.GetContext().Address + insn->detail->x86.operands[0].mem.disp; 93 | uint32_t la_symbol_ptr_offset = pvt_Image.RvaToOffset(la_symbol_ptr_rva); 94 | if (la_symbol_ptr_offset == X64ImageInterpreter::InvalidOffset) 95 | continue; 96 | 97 | uint64_t stub_helper_rva = *pvt_Image.ImageOffset(la_symbol_ptr_offset); 98 | uint32_t stub_helper_offset = pvt_Image.RvaToOffset(stub_helper_rva); 99 | if (stub_helper_offset == X64ImageInterpreter::InvalidOffset) 100 | continue; 101 | 102 | // 103 | // __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc 104 | // is the mangled name of "std::__1::basic_string, std::__1::allocator >::append(char const*)", 105 | // which is, as known as, "std::string::append(const char*)" 106 | // You can demangle it by c++flit 107 | // e.g. 108 | // c++filt -_ '__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc' 109 | // 110 | if (nkg::IsResolvedTo(pvt_Image, pvt_Image.ImageOffset(stub_helper_offset), "__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc")) { 111 | auto StdStringAppendStubOffset = pvt_Disassembler.GetInstructionContext().pbOpcode - pvt_Image.ImageBase(); 112 | StdStringAppendStubRva = pvt_Image.OffsetToRva(StdStringAppendStubOffset); 113 | break; 114 | } 115 | } 116 | } 117 | 118 | if (StdStringAppendStubRva == X64ImageInterpreter::InvalidAddress) { 119 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 120 | throw nkg::Exception(__FILE__, __LINE__, "Not found."); 121 | } 122 | 123 | pvt_FunctionOffset = FunctionOffset; 124 | pvt_KeywordOffset = KeywordOffset; 125 | pvt_StdStringAppendStubRva = StdStringAppendStubRva; 126 | 127 | printf("[+] PatchSolution2 ...... Ready to apply.\n"); 128 | printf(" Function offset = +0x%.8x\n", pvt_FunctionOffset); 129 | printf(" Keyword offset = +0x%.8x\n", pvt_KeywordOffset); 130 | printf(" std::string::append(const char*) RVA = 0x%.16llx\n", pvt_StdStringAppendStubRva); 131 | return true; 132 | } catch (...) { 133 | printf("[-] PatchSolution2 ...... Omitted.\n"); 134 | return false; 135 | } 136 | } 137 | 138 | [[nodiscard]] 139 | bool PatchSolution2::CheckKey(const RSACipher& RsaCipher) const noexcept { 140 | std::string PublicKeyPEM = RsaCipher.ExportKeyString(); 141 | 142 | PublicKeyPEM.erase(PublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"), 26); 143 | PublicKeyPEM.erase(PublicKeyPEM.find("-----END PUBLIC KEY-----"), 24); 144 | { 145 | std::string::size_type pos = 0; 146 | while ((pos = PublicKeyPEM.find('\n', pos)) != std::string::npos) { 147 | PublicKeyPEM.erase(pos, 1); 148 | } 149 | } 150 | 151 | return PublicKeyPEM.length() == 0x188; 152 | } 153 | 154 | void PatchSolution2::MakePatch(const RSACipher& RsaCipher) const { 155 | if (pvt_FunctionOffset == X64ImageInterpreter::InvalidOffset || 156 | pvt_KeywordOffset == X64ImageInterpreter::InvalidOffset || 157 | pvt_StdStringAppendStubRva == X64ImageInterpreter::InvalidAddress) 158 | { 159 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 160 | throw nkg::Exception(__FILE__, __LINE__, "PatchSolution2 is not ready."); 161 | } 162 | 163 | // 164 | // Prepare public key string 165 | // 166 | std::string PublicKeyPEM = RsaCipher.ExportKeyString(); 167 | 168 | PublicKeyPEM.erase(PublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"), 26); 169 | PublicKeyPEM.erase(PublicKeyPEM.find("-----END PUBLIC KEY-----"), 24); 170 | { 171 | std::string::size_type pos = 0; 172 | while ((pos = PublicKeyPEM.find('\n', pos)) != std::string::npos) { 173 | PublicKeyPEM.erase(pos, 1); 174 | } 175 | } 176 | 177 | // 178 | // Prepare new function opcodes 179 | // 180 | uint64_t FunctionRVA = pvt_Image.OffsetToRva(pvt_FunctionOffset); 181 | uint64_t KeywordRVA = pvt_Image.OffsetToRva(pvt_KeywordOffset); 182 | 183 | char AssemblyCode[512] = {}; 184 | sprintf(AssemblyCode, 185 | "push rbp;" 186 | "mov rbp, rsp;" 187 | "push r15;" 188 | "push r14;" 189 | "push rbx;" 190 | "sub rsp, 0x48;" 191 | 192 | "mov rbx, rdi;" 193 | 194 | "xor rax, rax;" // initialize std::string with null 195 | "mov qword ptr[rsp], rax;" 196 | "mov qword ptr[rsp + 0x8], rax;" 197 | "mov qword ptr[rsp + 0x10], rax;" 198 | 199 | "lea rdi, qword ptr[rsp];" 200 | "lea rsi, qword ptr[0x%.16llx];" // filled with address to Keyword 201 | "call 0x%.16llx;" // filled with address to std::string::append(const char*) 202 | 203 | "mov rax, qword ptr[rsp];" 204 | "mov qword ptr[rbx], rax;" 205 | "mov rax, qword ptr[rsp + 0x8];" 206 | "mov qword ptr[rbx + 0x8], rax;" 207 | "mov rax, qword ptr[rsp + 0x10];" 208 | "mov qword ptr[rbx + 0x10], rax;" 209 | 210 | "mov rax, rbx;" 211 | "add rsp, 0x48;" 212 | "pop rbx;" 213 | "pop r14;" 214 | "pop r15;" 215 | "pop rbp;" 216 | "ret;", 217 | KeywordRVA, 218 | pvt_StdStringAppendStubRva 219 | ); 220 | 221 | auto NewFunctionOpcode = pvt_Assembler.GenerateOpcode(AssemblyCode, FunctionRVA); 222 | 223 | auto pbFunctionPatch = pvt_Image.ImageOffset(pvt_FunctionOffset); 224 | auto pbKeywordPatch = pvt_Image.ImageOffset(pvt_KeywordOffset); 225 | 226 | puts("**************************************************************"); 227 | puts("* PatchSolution2 *"); 228 | puts("**************************************************************"); 229 | printf("@+0x%.8x\n", pvt_KeywordOffset); 230 | 231 | puts("Previous:"); 232 | nkg::PrintMemory(pbKeywordPatch, pbKeywordPatch + sizeof(Keyword) - 1, pbKeywordPatch); 233 | 234 | memcpy(pbKeywordPatch, PublicKeyPEM.c_str(), PublicKeyPEM.length() + 1); // with a null-terminator 235 | 236 | puts("After:"); 237 | nkg::PrintMemory(pbKeywordPatch, pbKeywordPatch + sizeof(Keyword) - 1, pbKeywordPatch); 238 | 239 | 240 | 241 | 242 | puts(""); 243 | printf("@+0x%.8x\n", pvt_FunctionOffset); 244 | 245 | puts("Previous:"); 246 | nkg::PrintMemory(pbFunctionPatch, pbFunctionPatch + NewFunctionOpcode.size(), pbFunctionPatch); 247 | 248 | memcpy(pbFunctionPatch, NewFunctionOpcode.data(), NewFunctionOpcode.size()); 249 | 250 | puts("After:"); 251 | nkg::PrintMemory(pbFunctionPatch, pbFunctionPatch + NewFunctionOpcode.size(), pbFunctionPatch); 252 | 253 | puts(""); 254 | } 255 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Navicat Keygen 2 | 3 | [中文版README](README.zh-CN.md) 4 | 5 | This repository will tell you how Navicat offline activation works. 6 | 7 | [How does it work?](HOW_DOES_IT_WORK.md) 8 | 9 | __NOTICE: This keygen only supports Navicat Premium.__ 10 | 11 | ## 1. How to build 12 | 13 | * Before you build keygen, you should make sure you have following libs: 14 | 15 | ``` 16 | openssl 17 | capstone 18 | keystone 19 | rapidjson 20 | libplist 21 | ``` 22 | 23 | You can install them by 24 | 25 | ```shell 26 | $ brew install openssl 27 | $ brew install capstone 28 | $ brew install keystone 29 | $ brew install rapidjson 30 | $ brew install libplist 31 | ``` 32 | 33 | * Clone `mac` branch and build keygen and patcher: 34 | 35 | ```shell 36 | $ git clone -b mac --single-branch https://github.com/DoubleLabyrinth/navicat-keygen.git 37 | $ cd navicat-keygen 38 | $ make all 39 | ``` 40 | 41 | You will see two executable files in `bin/` directory: 42 | 43 | ```shell 44 | $ ls bin/ 45 | navicat-keygen navicat-patcher 46 | ``` 47 | 48 | ## 2. How to Use 49 | 50 | 1. Build keygen and patcher. 51 | 52 | 2. Backup all of your saved database connection configurations (with password). 53 | 54 | 3. Remove all connections, if have, that Navicat saved in `Keychain Access.app`. 55 | 56 | You can find them by search with keyword `navicat` in `Keychain Access.app`. 57 | 58 | 4. Use `navicat-patcher` to replace __Navicat Activation Public Key__. 59 | 60 | ``` 61 | Usage: 62 | navicat-patcher [RSA-2048 Private Key File] 63 | 64 | Path to `Navicat Premium.app`. 65 | Example: 66 | /Applications/Navicat\ Premium.app/ 67 | This parameter must be specified. 68 | 69 | [RSA-2048 Private Key File] Path to a PEM-format RSA-2048 private key file. 70 | This parameter is optional. 71 | ``` 72 | 73 | * ``: The path to `Navicat Premium.app`. 74 | 75 | __This parameter must be specified.__ 76 | 77 | * `[RSA-2048 PrivateKey(PEM file)]`: The path to an RSA-2048 private key file. 78 | 79 | __This parameter is optional.__ 80 | 81 | If not specified, `navicat-patcher` will generate a new RSA-2048 private key file `RegPrivateKey.pem` at current directory. 82 | 83 | __Example:__ 84 | 85 | ```console 86 | $ ./navicat-patcher /Applications/Navicat\ Premium.app/ 87 | ``` 88 | 89 | It has been tested on __Navicat Premium 12.1.24 For Mac Simplified Chinese__ version. 90 | 91 | The following is an example of output: 92 | 93 | ```console 94 | $ ./navicat-patcher /Applications/Navicat\ Premium.app/ 95 | *************************************************** 96 | * Navicat Patcher by @DoubleLabyrinth * 97 | * Version: 4.0 * 98 | *************************************************** 99 | 100 | Press Enter to continue or Ctrl + C to abort. 101 | 102 | [*] Your Navicat version: 12.1.24 103 | 104 | [+] PatchSolution0 ...... Ready to apply. 105 | Keyword offset = +0x024a7db8 106 | [-] PatchSolution1 ...... Omitted. 107 | [+] PatchSolution2 ...... Ready to apply. 108 | Function offset = +0x00ec9868 109 | Keyword offset = +0x0263fd60 110 | std::string::append(const char*) RVA = 0x000000010214b726 111 | 112 | [*] Generating new RSA private key, it may take a long time... 113 | [+] New RSA private key has been saved to RegPrivateKey.pem. 114 | 115 | [*] Your RSA public key: 116 | -----BEGIN PUBLIC KEY----- 117 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA030QIDXRx372bGrne9kp 118 | uuAqpxaxJX0x6LVaOf8+Uan2SQnFH8frnuMDRg5PjdBnFWEJGqZmRD1fNkLOhhCE 119 | iFZWxrDgJcuEBrv5VlduQ4hlYIulcf6qilBZUaaX9Kb3R7+H8ClMb00HwLc/Iht5 120 | bd9krhU3CT3g2ZG00GxVhEF4a/zZMDjeuQvTUeeubIeriT/2YC+w/tKfGbqWvjC6 121 | wkbjXGbVICSiKzhzztS4BHbtQMl8v6doMhFVd/PEDNFQrbkEr3kbk/oD8AccL8iz 122 | aV17UHt4VW2fR8tMyTvcuhTaUtWmt/tL6Z1RzCqH+KvTv8GpH8qFcty89YXja7dL 123 | kQIDAQAB 124 | -----END PUBLIC KEY----- 125 | 126 | ************************************************************** 127 | * PatchSolution0 * 128 | ************************************************************** 129 | @+0x024a7db8 130 | Previous: 131 | -0x0000000000000008 2d 2d 2d 2d 2d 42 45 47 -----BEG 132 | +0x0000000000000008 49 4e 20 50 55 42 4c 49 43 20 4b 45 59 2d 2d 2d IN PUBLIC KEY--- 133 | +0x0000000000000018 2d 2d 00 4d 49 49 42 49 6a 41 4e 42 67 6b 71 68 --.MIIBIjANBgkqh 134 | +0x0000000000000028 6b 69 47 39 77 30 42 41 51 45 46 41 41 4f 43 41 kiG9w0BAQEFAAOCA 135 | ... 136 | ... 137 | ... 138 | After: 139 | -0x0000000000000008 2d 2d 2d 2d 2d 42 45 47 -----BEG 140 | +0x0000000000000008 49 4e 20 50 55 42 4c 49 43 20 4b 45 59 2d 2d 2d IN PUBLIC KEY--- 141 | +0x0000000000000018 2d 2d 00 4d 49 49 42 49 6a 41 4e 42 67 6b 71 68 --.MIIBIjANBgkqh 142 | +0x0000000000000028 6b 69 47 39 77 30 42 41 51 45 46 41 41 4f 43 41 kiG9w0BAQEFAAOCA 143 | ... 144 | ... 145 | ... 146 | 147 | ************************************************************** 148 | * PatchSolution2 * 149 | ************************************************************** 150 | @+0x0263fd60 151 | Previous: 152 | +0x0000000000000000 42 49 6a 57 79 6f 65 52 52 30 4e 42 67 6b 71 6e BIjWyoeRR0NBgkqn 153 | +0x0000000000000010 44 5a 57 78 43 67 4b 43 45 41 77 31 64 71 46 33 DZWxCgKCEAw1dqF3 154 | +0x0000000000000020 44 54 76 4f 42 39 31 5a 48 77 65 63 4a 59 46 72 DTvOB91ZHwecJYFr 155 | +0x0000000000000030 64 4d 31 4b 45 68 31 79 56 65 52 6f 47 71 53 64 dM1KEh1yVeRoGqSd 156 | +0x0000000000000040 4c 4c 47 5a 47 55 6c 6e 67 69 67 33 4f 44 35 6d LLGZGUlngig3OD5m 157 | ... 158 | ... 159 | ... 160 | 161 | @+0x00ec9868 162 | Previous: 163 | -0x0000000000000008 55 48 89 e5 41 57 41 56 UH..AWAV 164 | +0x0000000000000008 53 48 83 ec 38 49 89 fe c6 45 e5 01 31 c0 88 45 SH..8I...E..1..E 165 | +0x0000000000000018 e6 88 45 e7 48 8d 35 c5 70 61 01 48 8d 5d b0 48 ..E.H.5.pa.H.].H 166 | +0x0000000000000028 89 df e8 49 d9 ff ff 48 8d 35 ef ed 60 01 48 89 ...I...H.5..`.H. 167 | +0x0000000000000038 df e8 80 1e 28 01 e8 3f f4 00 00 88 45 e5 e8 b7 ....(..?....E... 168 | +0x0000000000000048 f4 00 00 88 45 e6 e8 2f f5 00 00 88 45 e7 f6 45 ....E../....E..E 169 | After: 170 | -0x0000000000000008 55 48 89 e5 41 57 41 56 UH..AWAV 171 | +0x0000000000000008 53 48 83 ec 48 48 89 fb 48 31 c0 48 89 04 24 48 SH..HH..H1.H..$H 172 | +0x0000000000000018 89 44 24 08 48 89 44 24 10 48 8d 3c 24 48 8d 35 .D$.H.D$.H.<$H.5 173 | +0x0000000000000028 cc 64 77 01 e8 8d 1e 28 01 48 8b 04 24 48 89 03 .dw....(.H..$H.. 174 | +0x0000000000000038 48 8b 44 24 08 48 89 43 08 48 8b 44 24 10 48 89 H.D$.H.C.H.D$.H. 175 | +0x0000000000000048 43 10 48 89 d8 48 83 c4 48 5b 41 5e 41 5f 5d c3 C.H..H..H[A^A_]. 176 | 177 | [+] PatchSolution0 has been applied. 178 | [+] PatchSolution2 has been applied. 179 | 180 | ************************************************************** 181 | * Patch has been done successfully. Have fun and enjoy~~ * 182 | * DO NOT FORGET TO SIGN NAVICAT BY YOUR CERTIFICATE!!! * 183 | ************************************************************** 184 | ``` 185 | 186 | * __FOR Navicat Premium version < 12.0.24 ONLY:__ 187 | 188 | `navicat-patcher` will abort and won't apply any patch. 189 | 190 | You should use openssl to generate `RegPrivateKey.pem` and `rpk` file. 191 | 192 | ```console 193 | $ openssl genrsa -out RegPrivateKey.pem 2048 194 | $ openssl rsa -in RegPrivateKey.pem -pubout -out rpk 195 | ``` 196 | 197 | Then replace 198 | 199 | ``` 200 | /Applications/Navicat Premium.app/Contents/Resources/rpk 201 | ``` 202 | 203 | by `rpk` you just generated. 204 | 205 | 5. __Generate a self-signed code-sign certificate and always trust it.__ 206 | 207 | __Then use `codesign` to re-sign `Navicat Premium.app`.__ 208 | 209 | ```console 210 | $ codesign -f -s "Your self-signed code-sign certificate name" 211 | ``` 212 | 213 | __NOTICE:__ 214 | 215 | "Your self-signed code-sign certificate name" is the name of your certificate in `Keychain Access.app`, not path. 216 | 217 | __Example:__ 218 | 219 | ```console 220 | $ codesign -f -s "foobar" /Applications/Navicat\ Premium.app/ 221 | ``` 222 | 223 | 6. Then use `navicat-keygen` to generate __snKey__ and __Activation Code__. 224 | 225 | ``` 226 | Usage: 227 | navicat-keygen 228 | 229 | Path to a PEM-format RSA-2048 private key file. 230 | This parameter must be specified. 231 | ``` 232 | 233 | * ``: Path to a PEM-format RSA-2048 private key file. 234 | 235 | __This parameter must be specified.__ 236 | 237 | __Example:__ 238 | 239 | ```console 240 | $ ./navicat-keygen ./RegPrivateKey.pem 241 | ``` 242 | 243 | You will be asked to select Navicat language and give major version number. After that an randomly generated __snKey__ will be given. 244 | 245 | ```console 246 | $ ./navicat-keygen ./RegPrivateKey.pem 247 | *************************************************** 248 | * Navicat Keygen by @DoubleLabyrinth * 249 | * Version: 4.0 * 250 | *************************************************** 251 | 252 | Which is your Navicat Premium language? 253 | 0. English 254 | 1. Simplified Chinese 255 | 2. Traditional Chinese 256 | 3. Japanese 257 | 4. Polish 258 | 5. Spanish 259 | 6. French 260 | 7. German 261 | 8. Korean 262 | 9. Russian 263 | 10. Portuguese 264 | 265 | (Input index)> 1 266 | 267 | (Input major version number, range: 0 ~ 15, default: 12)> 12 268 | 269 | Serial number: 270 | NAVG-Z5H9-NK2L-MAZJ 271 | 272 | Your name: 273 | ``` 274 | 275 | You can use this __snKey__ to activate your Navicat preliminarily. 276 | 277 | Then you will be asked to input `Your name` and `Your organization`. Just set them whatever you want, but not too long. 278 | 279 | ```console 280 | Your name: DoubleLabyrinth 281 | Your organization: DoubleLabyrinth 282 | Input request code (in Base64), input empty line to end: 283 | ``` 284 | 285 | After that, you will be asked to input request code. Now __DO NOT CLOSE KEYGEN__. 286 | 287 | 7. __Disconnect your network__ and open Navicat Premium. 288 | 289 | Find and click `Registration`. 290 | 291 | Fill license key by __Serial number__ that the keygen gave and click `Activate`. 292 | 293 | 8. Generally online activation will fail and Navicat will ask you do `Manual Activation`, just choose it. 294 | 295 | 9. Copy your request code and paste it in the keygen. Input empty line to tell the keygen that your input ends. 296 | 297 | ```console 298 | Your name: DoubleLabyrinth 299 | Your organization: DoubleLabyrinth 300 | 301 | Input request code (in Base64), input empty line to end: 302 | Nuk6pouXNhuGnqb2rBbxpDOiCFxhdJF4/gteYA/UZFUwqmhhphn3PAErvlxCtbUCf9Lzw02gfIFog3gmTB1C5JzPdeE5uuD6SAvhlQ7ZVOmdA66dvt6mDDpuf78cGio1Rpkd0D/6dLzgHnFJJPOfPtlIT5ZOLDiWkiSJm8d83+ckMBoMtcvpXCiwDIGb1KfVZwsgLojyrrO5OzakIzd2xQ8r3mEmbVbMl/zD0S5fO4agxEOp2WvpmM1cqom9Egll7kgcQG8A0z1Abqo1PrVBjjOsb/v8wy5V/469G6/uDT4AkZQSz8m/TX9ZQlZE3QBlzrJ+sTEkpMVhw3h3u6l4JQ== 303 | 304 | Request Info: 305 | {"K":"NAVGZ5H9NK2LMAZJ", "DI":"NGVlZjdjOTViMzYyMWI0", "P":"MAC"} 306 | 307 | Response Info: 308 | {"K":"NAVGZ5H9NK2LMAZJ","DI":"NGVlZjdjOTViMzYyMWI0","N":"DoubleLabyrinth","O":"DoubleLabyrinth","T":1564415636} 309 | 310 | License: 311 | Vd4QUzEw6DPNpJLYVKV6ZNDny0gsZWCXbKyrf2nF27iTM35YUBouXEcAB/Vy355V2z++7iXe/coKmV4kNZbywlBchI5ts7gOHnhXWzBYQ3yKsBYKob/7sxaiw7CXCmhM4mPLMzrp5okewCWjBjb51keZ4SA3F6j8HGIVYiZW3CAZtkjxs9uUoXvVIJr+Gt83TgU+sqiC4oSplokopAql2zWPieA9KuhPoCKiGLMvuQwv0wWWPc2HorY0AHAetsyZ8MN4utZ2ylQ9z/ZojwX1KViyh3xxnjWF7xXJljIdBA4tCi4QDqDLvTuICfUV7VeKzOUY+ZKCO0xGxkTe1HVwog== 312 | ``` 313 | 314 | 10. Finally, you will get __Activation Code__ which looks like a Base64 string. 315 | 316 | Just copy it and paste it in Navicat `Manual Activation` window, then click `Activate`. 317 | 318 | If nothing wrong, activation should be done successfully. 319 | 320 | -------------------------------------------------------------------------------- /navicat-patcher/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "../common/ExceptionSystem.hpp" 8 | #include "../common/ResourceOwned.hpp" 9 | #include "ResourceTraitsUnix.hpp" 10 | #include "PatchSolutions.hpp" 11 | 12 | static void Welcome() { 13 | puts("***************************************************"); 14 | puts("* Navicat Patcher by @DoubleLabyrinth *"); 15 | puts("* Version: 4.0 *"); 16 | puts("***************************************************"); 17 | puts(""); 18 | puts("Press Enter to continue or Ctrl + C to abort."); 19 | getchar(); 20 | } 21 | 22 | static void Help() { 23 | puts("***************************************************"); 24 | puts("* Navicat Patcher by @DoubleLabyrinth *"); 25 | puts("* Version: 4.0 *"); 26 | puts("***************************************************"); 27 | puts(""); 28 | puts("Usage:"); 29 | puts(" navicat-patcher [RSA-2048 Private Key File]"); 30 | puts(""); 31 | puts(" Path to `Navicat Premium.app`."); 32 | puts(" Example:"); 33 | puts(" /Applications/Navicat\\ Premium.app/"); 34 | puts(" This parameter must be specified."); 35 | puts(""); 36 | puts(" [RSA-2048 Private Key File] Path to a PEM-format RSA-2048 private key file."); 37 | puts(" This parameter is optional."); 38 | puts(""); 39 | } 40 | 41 | static std::string GetNavicatVersion(const char* AppPath) { 42 | ResourceOwned hInfoPlist(FileHandleTraits{}); 43 | ResourceOwned InfoPlist(CppObjectTraits{}); 44 | 45 | hInfoPlist.TakeOver(open((std::string(AppPath) + "/Contents/Info.plist").c_str(), O_RDONLY)); 46 | if (hInfoPlist.IsValid() == false) { 47 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 48 | throw nkg::SystemError(__FILE__, __LINE__, errno, "Failed to open Contents/Info.plist."); 49 | } 50 | 51 | struct stat statInfoPlist = {}; 52 | if (fstat(hInfoPlist, &statInfoPlist) != 0) { 53 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 54 | throw nkg::SystemError(__FILE__, __LINE__, errno, "Failed to get file size of Contents/Info.plist."); 55 | } 56 | 57 | std::string contentInfoPlist(statInfoPlist.st_size, '\x00'); 58 | if (read(hInfoPlist, contentInfoPlist.data(), contentInfoPlist.size()) != contentInfoPlist.size()) { 59 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 60 | throw nkg::SystemError(__FILE__, __LINE__, errno, "Failed to read Contents/Info.plist."); 61 | } 62 | 63 | InfoPlist.TakeOver(dynamic_cast(PList::Structure::FromXml(contentInfoPlist))); 64 | if (InfoPlist.IsValid() == false) { 65 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 66 | throw nkg::Exception(__FILE__, __LINE__, "Failed to parse Contents/Info.plist."); 67 | } 68 | 69 | auto kv = InfoPlist->Find("CFBundleShortVersionString"); 70 | if (kv == InfoPlist->End()) { 71 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 72 | throw nkg::Exception(__FILE__, __LINE__, "Cannot find CFBundleShortVersionString in Contents/Info.plist."); 73 | } 74 | 75 | if (kv->second->GetType() == PLIST_STRING) { 76 | return dynamic_cast(kv->second)->GetValue(); 77 | } else { 78 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 79 | throw nkg::Exception(__FILE__, __LINE__, "Failed to get Navicat version."); 80 | } 81 | } 82 | 83 | static void LoadKey(RSACipher& RsaCipher, const char* RsaKeyFileName, 84 | PatchSolution* lpSolution0, 85 | PatchSolution* lpSolution1, 86 | PatchSolution* lpSolution2) { 87 | if (RsaKeyFileName) { 88 | RsaCipher.ImportKeyFromFile(RsaKeyFileName); 89 | 90 | if ((lpSolution0 && !lpSolution0->CheckKey(RsaCipher)) || 91 | (lpSolution1 && !lpSolution1->CheckKey(RsaCipher)) || 92 | (lpSolution2 && !lpSolution2->CheckKey(RsaCipher))) 93 | { 94 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 95 | throw nkg::Exception(__FILE__, __LINE__, "The RSA private key you provide cannot be used."); 96 | } 97 | } else { 98 | puts(""); 99 | puts("[*] Generating new RSA private key, it may take a long time..."); 100 | 101 | do { 102 | RsaCipher.GenerateKey(2048); 103 | } while ((lpSolution0 && !lpSolution0->CheckKey(RsaCipher)) || 104 | (lpSolution1 && !lpSolution1->CheckKey(RsaCipher)) || 105 | (lpSolution2 && !lpSolution2->CheckKey(RsaCipher))); // re-generate RSA key if CheckKey return false 106 | 107 | RsaCipher.ExportKeyToFile("RegPrivateKey.pem"); 108 | 109 | puts("[+] New RSA private key has been saved to RegPrivateKey.pem."); 110 | } 111 | 112 | std::string PublicKeyPEM = RsaCipher.ExportKeyString(); 113 | puts(""); 114 | puts("[*] Your RSA public key:"); 115 | puts(PublicKeyPEM.c_str()); 116 | } 117 | 118 | int main(int argc, char* argv[]) { 119 | if (argc != 2 && argc != 3) { 120 | Help(); 121 | return -1; 122 | } else { 123 | Welcome(); 124 | 125 | try { 126 | RSACipher RsaCipher; 127 | ResourceOwned Solution0(CppObjectTraits{}); 128 | ResourceOwned Solution1(CppObjectTraits{}); 129 | ResourceOwned Solution2(CppObjectTraits{}); 130 | 131 | ResourceOwned hMainApp(FileHandleTraits{}, open((std::string(argv[1]) + "/Contents/MacOS/Navicat Premium").c_str(), O_RDWR)); 132 | if (hMainApp.IsValid() == false) { 133 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 134 | throw nkg::SystemError(__FILE__, __LINE__, errno, "open failed."); 135 | } 136 | 137 | struct stat statMainApp = {}; 138 | if (fstat(hMainApp, &statMainApp) != 0) { 139 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 140 | throw nkg::SystemError(__FILE__, __LINE__, errno, "fstat failed."); 141 | } 142 | 143 | ResourceOwned lpMainApp(MapViewTraits{}, 144 | mmap(nullptr, static_cast(statMainApp.st_size), PROT_READ | PROT_WRITE, MAP_SHARED, hMainApp, 0), 145 | [&statMainApp](void* p) { munmap(p, statMainApp.st_size); } 146 | ); 147 | if (lpMainApp.IsValid() == false) { 148 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 149 | throw nkg::SystemError(__FILE__, __LINE__, errno, "mmap failed."); 150 | } 151 | 152 | int Ver0, Ver1, Ver2; 153 | if (sscanf(GetNavicatVersion(argv[1]).c_str(), "%d.%d.%d", &Ver0, &Ver1, &Ver2) != 3) { // NOLINT 154 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 155 | throw nkg::SystemError(__FILE__, __LINE__, errno, "Failed to get version of Navicat."); 156 | } 157 | 158 | X64ImageInterpreter MainApp = X64ImageInterpreter::Parse(lpMainApp); 159 | 160 | printf("[*] Your Navicat version: %d.%d.%d\n", Ver0, Ver1, Ver2); 161 | printf("\n"); 162 | 163 | Solution0.TakeOver(new PatchSolution0(MainApp)); 164 | Solution1.TakeOver(new PatchSolution1(MainApp)); 165 | Solution2.TakeOver(new PatchSolution2(MainApp)); 166 | 167 | if (Solution0->FindPatchOffset() == false) { 168 | Solution0.Release(); 169 | } 170 | if (Solution1->FindPatchOffset() == false) { 171 | Solution1.Release(); 172 | } 173 | if (Solution2->FindPatchOffset() == false) { 174 | Solution2.Release(); 175 | } 176 | 177 | // 178 | // Begin strategies by different Navicat versions 179 | // 180 | if (Ver0 < 12) { // ver < 12.0.0 181 | // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception 182 | throw nkg::SystemError(__FILE__, __LINE__, errno, "Unsupported version of Navicat."); 183 | } else if (Ver0 == 12 && Ver1 == 0 && Ver2 < 24) { // ver < 12.0.24 184 | std::string path(argv[1]); 185 | while(path.back() == '/') { 186 | path.pop_back(); 187 | } 188 | 189 | printf("[*] Your Navicat version is < 12.0.24. So there would be nothing patched.\n"); 190 | printf(" Just use `openssl` to generate `RegPrivateKey.pem` and `rpk` file:\n"); 191 | printf(" openssl genrsa -out RegPrivateKey.pem 2048\n"); 192 | printf(" openssl rsa -in RegPrivateKey.pem -pubout -out rpk\n"); 193 | printf(" and replace `%s/Contents/Resources/rpk` with the `rpk` file you just generated.\n", path.c_str()); 194 | printf("\n"); 195 | 196 | return 0; 197 | } else if (Ver0 == 12 && (Ver1 == 0 || (Ver1 == 1 && Ver2 < 14))) { // 12.0.24 <= ver && ver < 12.1.14 198 | // In this case, Solution0 must be applied 199 | if (Solution0.IsValid() == false) { 200 | puts("[-] Patch abort. None of PatchSolutions will be applied."); 201 | puts(" Are you sure your Navicat has not been patched before?"); 202 | return -1; 203 | } 204 | } else if (Ver0 == 12 && Ver1 == 1 && Ver2 == 14) { // ver == 12.1.14 205 | // In this case, Solution0 and Solution1 must be applied 206 | if ((Solution0.IsValid() && Solution1.IsValid()) == false) { 207 | puts("[-] Patch abort. None of PatchSolutions will be applied."); 208 | puts(" Are you sure your Navicat has not been patched before?"); 209 | return -1; 210 | } 211 | } else { // ver > 12.1.14 212 | // In this case, Solution0 and Solution2 must be applied 213 | if ((Solution0.IsValid() && Solution2.IsValid()) == false) { 214 | puts("[-] Patch abort. None of PatchSolutions will be applied."); 215 | puts(" Are you sure your Navicat has not been patched before?"); 216 | return -1; 217 | } 218 | } 219 | // 220 | // End strategies by different Navicat versions 221 | // 222 | 223 | LoadKey(RsaCipher, argc == 3 ? argv[2] : nullptr, Solution0, Solution1, Solution2); 224 | 225 | if (Solution0.IsValid()) { 226 | Solution0->MakePatch(RsaCipher); 227 | } 228 | if (Solution1.IsValid()) { 229 | Solution1->MakePatch(RsaCipher); 230 | } 231 | if (Solution2.IsValid()) { 232 | Solution2->MakePatch(RsaCipher); 233 | } 234 | 235 | if (Solution0.IsValid()) 236 | puts("[+] PatchSolution0 has been applied."); 237 | if (Solution1.IsValid()) 238 | puts("[+] PatchSolution1 has been applied."); 239 | if (Solution2.IsValid()) 240 | puts("[+] PatchSolution2 has been applied."); 241 | 242 | puts(""); 243 | puts("**************************************************************"); 244 | puts("* Patch has been done successfully. Have fun and enjoy~~ *"); 245 | puts("* DO NOT FORGET TO SIGN NAVICAT BY YOUR CERTIFICATE!!! *"); 246 | puts("**************************************************************"); 247 | 248 | return 0; 249 | } catch (nkg::Exception& e) { 250 | printf("[-] %s:%zu -> \n", e.File(), e.Line()); 251 | printf(" %s\n", e.Message()); 252 | if (e.HasErrorCode()) { 253 | printf(" %s\n", e.ErrorString()); 254 | } 255 | return -1; 256 | } 257 | } 258 | } 259 | 260 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) 2017 {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | navicat-keygen Copyright (C) 2017 Double Helix 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . --------------------------------------------------------------------------------