diff --git a/.gitignore b/.gitignore index db9d25b..d3bfed3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.DS_Store +.vscode/* bin/* \ No newline at end of file diff --git a/Makefile b/Makefile index f344fdd..0d8ae10 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,13 @@ CC = g++ -OPENSSL_INCLUDE_PATH = /usr/local/opt/openssl/include -OPENSSL_LIB_PATH = /usr/local/opt/openssl/lib -CAPSTONE_INCLUDE_PATH = #/usr/local/Cellar/capstone/4.0.1/include -CAPSTONE_LIB_PATH = #/usr/local/Cellar/capstone/4.0.1/lib -KEYSTONE_INCLUDE_PATH = #/usr/local/Cellar/keystone/0.9.1/include -KEYSTONE_LIB_PATH = #/usr/local/Cellar/keystone/0.9.1/lib -RAPIDJSON_INCLUDE_PATH = #/usr/local/Cellar/rapidjson/1.1.0/include -LIBPLIST_INCLUDE_PATH = #/usr/local/Cellar/libplist/2.0.0_1/include -LIBPLIST_LIB_PATH = #/usr/local/Cellar/libplist/2.0.0_1/lib +OPENSSL_INCLUDE_PATH = /usr/local/opt/openssl@1.1/include +OPENSSL_LIB_PATH = /usr/local/opt/openssl@1.1/lib +CAPSTONE_INCLUDE_PATH = +CAPSTONE_LIB_PATH = +KEYSTONE_INCLUDE_PATH = +KEYSTONE_LIB_PATH = +RAPIDJSON_INCLUDE_PATH = +LIBPLIST_INCLUDE_PATH = +LIBPLIST_LIB_PATH = OUTPUT_DIR = ./bin/ COMMON_DIR = ./common/ @@ -16,10 +16,11 @@ KEYGEN_DIR = ./navicat-keygen/ COMMON_HEADER = \ $(COMMON_DIR)Exception.hpp \ +$(COMMON_DIR)ExceptionGeneric.hpp \ $(COMMON_DIR)ExceptionOpenssl.hpp \ $(COMMON_DIR)ExceptionSystem.hpp \ -$(COMMON_DIR)ResourceOwned.hpp \ $(COMMON_DIR)ResourceTraitsOpenssl.hpp \ +$(COMMON_DIR)ResourceWrapper.hpp \ $(COMMON_DIR)RSACipher.hpp \ PATCHER_HEADER = \ @@ -30,15 +31,16 @@ $(PATCHER_DIR)ResourceTraitsKeystone.hpp \ $(PATCHER_DIR)ResourceTraitsUnix.hpp \ $(PATCHER_DIR)CapstoneDisassembler.hpp \ $(PATCHER_DIR)KeystoneAssembler.hpp \ +$(PATCHER_DIR)MemoryAccess.hpp \ +$(PATCHER_DIR)Misc.hpp \ $(PATCHER_DIR)X64ImageInterpreter.hpp \ $(PATCHER_DIR)PatchSolutions.hpp PATCHER_SOURCE = \ $(PATCHER_DIR)CapstoneDisassembler.cpp \ $(PATCHER_DIR)KeystoneAssembler.cpp \ +$(PATCHER_DIR)Misc.cpp \ $(PATCHER_DIR)X64ImageInterpreter.cpp \ -$(PATCHER_DIR)HelperIsResolvedTo.cpp \ -$(PATCHER_DIR)HelperPrintMemory.cpp \ $(PATCHER_DIR)PatchSolution0.cpp \ $(PATCHER_DIR)PatchSolution1.cpp \ $(PATCHER_DIR)PatchSolution2.cpp \ @@ -47,31 +49,36 @@ $(PATCHER_DIR)main.cpp PATCHER_BINARY = $(OUTPUT_DIR)navicat-patcher KEYGEN_HEADER = \ -$(KEYGEN_DIR)DESCipher.hpp \ -$(KEYGEN_DIR)NavicatKeygen.hpp +$(KEYGEN_DIR)Base32.hpp \ +$(KEYGEN_DIR)Base64.hpp \ +$(KEYGEN_DIR)SerialNumberGenerator.hpp KEYGEN_SOURCE = \ -$(KEYGEN_DIR)Base64.cpp \ -$(KEYGEN_DIR)main.cpp +$(KEYGEN_DIR)CollectInformation.cpp \ +$(KEYGEN_DIR)GenerateLicense.cpp \ +$(KEYGEN_DIR)main.cpp \ +$(KEYGEN_DIR)SerialNumberGenerator.cpp KEYGEN_BINARY = $(OUTPUT_DIR)navicat-keygen patcher: $(PATCHER_HEADER) $(PATCHER_SOURCE) @if [ ! -d $(OUTPUT_DIR) ]; then mkdir -p $(OUTPUT_DIR); fi $(CC) -std=c++17 -O2 \ +-I$(COMMON_DIR) \ -I$(OPENSSL_INCLUDE_PATH) -L$(OPENSSL_LIB_PATH) \ $(if $(CAPSTONE_INCLUDE_PATH),-I$(CAPSTONE_INCLUDE_PATH),) $(if $(CAPSTONE_LIB_PATH),-L$(CAPSTONE_LIB_PATH),) \ $(if $(KEYSTONE_INCLUDE_PATH),-I$(KEYSTONE_INCLUDE_PATH),) $(if $(KEYSTONE_LIB_PATH),-L$(KEYSTONE_LIB_PATH),) \ $(if $(LIBPLIST_INCLUDE_PATH),-I$(LIBPLIST_INCLUDE_PATH),) $(if $(LIBPLIST_LIB_PATH),-L$(LIBPLIST_LIB_PATH),) \ --lcrypto -lcapstone -lkeystone -lplist++ $(PATCHER_SOURCE) -o $(PATCHER_BINARY) +$(PATCHER_SOURCE) -o $(PATCHER_BINARY) -lcrypto -lcapstone -lkeystone -lplist++ @echo keygen: $(KEYGEM_HEADER) $(KEYGEN_SOURCE) @if [ ! -d $(OUTPUT_DIR) ]; then mkdir -p $(OUTPUT_DIR); fi $(CC) -std=c++17 -O2 \ +-I$(COMMON_DIR) \ -I$(OPENSSL_INCLUDE_PATH) -L$(OPENSSL_LIB_PATH) \ $(if $(RAPIDJSON_INCLUDE_PATH),-I$(RAPIDJSON_INCLUDE_PATH),) \ --lcrypto $(KEYGEN_SOURCE) -o $(KEYGEN_BINARY) +$(KEYGEN_SOURCE) -o $(KEYGEN_BINARY) -lcrypto all: patcher keygen @echo 'Done.' diff --git a/common/Exception.hpp b/common/Exception.hpp index 86dc1d4..01db81c 100644 --- a/common/Exception.hpp +++ b/common/Exception.hpp @@ -1,36 +1,101 @@ #pragma once -#include // NOLINT -#include // NOLINT +#include +#include +#include +#include +#include +#include -namespace nkg { +namespace ARL { - class Exception { + class Exception : public std::exception { private: - const char* pvt_File; - const char* pvt_Message; - size_t pvt_Line; + const char* m_SourceFile; + const size_t m_SourceLine; + std::string m_Message; + std::vector m_Hints; public: - Exception(const char* File, size_t Line, const char* Message) noexcept : - pvt_File(File), - pvt_Message(Message), - pvt_Line(Line) {} + template + Exception(const char* SourceFile, size_t SourceLine, const char* Format, __ArgTypes&&... Args) noexcept : + m_SourceFile(SourceFile), + m_SourceLine(SourceLine) + { + if constexpr (sizeof...(Args) == 0) { + m_Message.assign(Format); + } else { + int l; + + l = snprintf(nullptr, 0, Format, std::forward<__ArgTypes>(Args)...); + if (l < 0) { + std::terminate(); + } - [[nodiscard]] - const char* File() const noexcept { - return pvt_File; + m_Message.resize(l + 1); + + l = snprintf(m_Message.data(), m_Message.length(), Format, std::forward<__ArgTypes>(Args)...); + if (l < 0) { + std::terminate(); + } + + while (m_Message.back() == '\x00') { + m_Message.pop_back(); + } + } } [[nodiscard]] - size_t Line() const noexcept { - return pvt_Line; + auto ExceptionFile() const noexcept { + return m_SourceFile; + } + + [[nodiscard]] + auto ExceptionLine() const noexcept { + return m_SourceLine; } [[nodiscard]] - const char* Message() const noexcept { - return pvt_Message; + auto ExceptionMessage() const noexcept { + return m_Message.c_str(); + } + + template + auto& PushHint(__HintType&& Hint) noexcept { // if an exception is thrown, just suppress and terminate. + m_Hints.emplace_back(std::forward<__HintType>(Hint)); + return *this; + } + + template + auto& PushFormatHint(const char* Format, __ArgTypes&&... Args) noexcept { // if an exception is thrown, just suppress and terminate. + int l; + std::string s; + + l = snprintf(nullptr, 0, Format, std::forward<__ArgTypes>(Args)...); + if (l < 0) { + std::terminate(); + } + + s.resize(l + 1); + + l = snprintf(s.data(), s.length(), Format, std::forward<__ArgTypes>(Args)...); + if (l < 0) { + std::terminate(); + } + + while (s.back() == '\x00') { + s.pop_back(); + } + + m_Hints.emplace_back(std::move(s)); + + return *this; + } + + [[nodiscard]] + const auto& Hints() const noexcept { + return m_Hints; } [[nodiscard]] @@ -47,6 +112,12 @@ namespace nkg { virtual const char* ErrorString() const noexcept { return nullptr; } + + [[nodiscard]] + // NOLINTNEXTLINE: mark "virtual" explicitly for more readability + virtual const char* what() const noexcept override { + return ExceptionMessage(); + } }; } diff --git a/common/ExceptionGeneric.hpp b/common/ExceptionGeneric.hpp new file mode 100644 index 0000000..7163293 --- /dev/null +++ b/common/ExceptionGeneric.hpp @@ -0,0 +1,28 @@ +#pragma once +#include "Exception.hpp" + +namespace ARL { + +#pragma push_macro("DECLARE_NEW_EXCEPTION") +#undef DECLARE_NEW_EXCEPTION + +#define DECLARE_NEW_EXCEPTION(name) \ + class name final : public Exception { \ + public: \ + template \ + name(const char* SourceFile, size_t SourceLine, const char* Format, __ArgTypes&&... Args) noexcept : \ + Exception(SourceFile, SourceLine, Format, std::forward<__ArgTypes>(Args)...) {} \ + } + + DECLARE_NEW_EXCEPTION(AssertionError); + DECLARE_NEW_EXCEPTION(EOFError); + DECLARE_NEW_EXCEPTION(IndexError); + DECLARE_NEW_EXCEPTION(KeyError); + DECLARE_NEW_EXCEPTION(NotImplementedError); + DECLARE_NEW_EXCEPTION(OverflowError); + DECLARE_NEW_EXCEPTION(ValueError); + +#undef DECLARE_NEW_EXCEPTION +#pragma pop_macro("DECLARE_NEW_EXCEPTION") + +} diff --git a/common/ExceptionOpenssl.hpp b/common/ExceptionOpenssl.hpp index 791f7dd..6ef1db8 100644 --- a/common/ExceptionOpenssl.hpp +++ b/common/ExceptionOpenssl.hpp @@ -1,19 +1,20 @@ #pragma once -#include #include "Exception.hpp" +#include -namespace nkg { +namespace ARL { class OpensslError final : public Exception { private: - unsigned long pvt_ErrorCode; + unsigned long m_ErrorCode; public: - OpensslError(const char* File, unsigned Line, unsigned long ErrorCode, const char* Message) noexcept : - Exception(File, Line, Message), - pvt_ErrorCode(ErrorCode) {} + template + OpensslError(const char* SourceFile, size_t SourceLine, unsigned long ErrorCode, const char* Format, __ArgTypes&&... Args) noexcept : + Exception(SourceFile, SourceLine, Format, std::forward<__ArgTypes>(Args)...), + m_ErrorCode(ErrorCode) {} [[nodiscard]] // NOLINTNEXTLINE: mark "virtual" explicitly for more readability @@ -24,14 +25,18 @@ namespace nkg { [[nodiscard]] // NOLINTNEXTLINE: mark "virtual" explicitly for more readability virtual intptr_t ErrorCode() const noexcept override { - return pvt_ErrorCode; + return m_ErrorCode; } [[nodiscard]] // NOLINTNEXTLINE: mark "virtual" explicitly for more readability virtual const char* ErrorString() const noexcept override { - ERR_load_crypto_strings(); - return ERR_reason_error_string(pvt_ErrorCode); + static bool loaded = false; + if (loaded == false) { + ERR_load_crypto_strings(); + loaded = true; + } + return ERR_reason_error_string(m_ErrorCode); } }; diff --git a/common/ExceptionSystem.hpp b/common/ExceptionSystem.hpp index 6b73c9b..0d74790 100644 --- a/common/ExceptionSystem.hpp +++ b/common/ExceptionSystem.hpp @@ -1,19 +1,20 @@ #pragma once #include "Exception.hpp" -#include // NOLINT +#include -namespace nkg { +namespace ARL { class SystemError final : public Exception { private: - int pvt_ErrorCode; + int m_ErrorCode; public: - SystemError(const char* File, unsigned Line, int ErrorCode, const char* Message) noexcept : - Exception(File, Line, Message), - pvt_ErrorCode(ErrorCode) {} + template + SystemError(const char* SourceFile, size_t SourceLine, int ErrorCode, const char* Format, __ArgTypes&&... Args) noexcept : + Exception(SourceFile, SourceLine, Format, std::forward<__ArgTypes>(Args)...), + m_ErrorCode(ErrorCode) {} [[nodiscard]] // NOLINTNEXTLINE: mark "virtual" explicitly for more readability @@ -24,14 +25,15 @@ namespace nkg { [[nodiscard]] // NOLINTNEXTLINE: mark "virtual" explicitly for more readability virtual intptr_t ErrorCode() const noexcept override { - return pvt_ErrorCode; + return m_ErrorCode; } [[nodiscard]] // NOLINTNEXTLINE: mark "virtual" explicitly for more readability virtual const char* ErrorString() const noexcept override { - return strerror(pvt_ErrorCode); + return strerror(m_ErrorCode); } }; -} \ No newline at end of file +} + diff --git a/common/RSACipher.hpp b/common/RSACipher.hpp index 1fe170d..f1b3641 100644 --- a/common/RSACipher.hpp +++ b/common/RSACipher.hpp @@ -4,267 +4,271 @@ #include #include #include - #include - +#include "Exception.hpp" #include "ExceptionOpenssl.hpp" -#include "ResourceOwned.hpp" +#include "ResourceWrapper.hpp" #include "ResourceTraitsOpenssl.hpp" -enum class RSAKeyType { - PrivateKey, - PublicKey -}; +namespace nkg { -enum class RSAKeyFormat { - PEM, - PKCS1 -}; + enum class RSAKeyType { + PrivateKey, + PublicKey + }; -class RSACipher { -private: - ResourceOwned pvt_RsaObj; + enum class RSAKeyFormat { + PEM, + PKCS1 + }; - template - static void pvt_WriteRSAToBIO(RSA* lpRSA, BIO* lpBIO) { - if constexpr (__Type == RSAKeyType::PrivateKey) { - if (PEM_write_bio_RSAPrivateKey(lpBIO, lpRSA, nullptr, nullptr, 0, nullptr, nullptr) == 0) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "PEM_write_bio_RSAPrivateKey failed."); + class RSACipher final : private ARL::ResourceWrapper { + private: + + template + static void _WriteRSAToBIO(RSA* lpRSA, BIO* lpBIO) { + if constexpr (__Type == RSAKeyType::PrivateKey) { + if (PEM_write_bio_RSAPrivateKey(lpBIO, lpRSA, nullptr, nullptr, 0, nullptr, nullptr) == 0) { + throw ARL::Exception(__FILE__, __LINE__, "PEM_write_bio_RSAPrivateKey failed."); + } } - } else { - if constexpr (__Format == RSAKeyFormat::PEM) { - if (PEM_write_bio_RSA_PUBKEY(lpBIO, lpRSA) == 0) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "PEM_write_bio_RSA_PUBKEY failed."); + + if constexpr (__Type == RSAKeyType::PublicKey) { + if constexpr (__Format == RSAKeyFormat::PEM) { + if (PEM_write_bio_RSA_PUBKEY(lpBIO, lpRSA) == 0) { + throw ARL::Exception(__FILE__, __LINE__, "PEM_write_bio_RSA_PUBKEY failed."); + } } - } else if constexpr (__Format == RSAKeyFormat::PKCS1) { - if (PEM_write_bio_RSAPublicKey(lpBIO, lpRSA) == 0) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "PEM_write_bio_RSAPublicKey failed."); + + if constexpr (__Format == RSAKeyFormat::PKCS1) { + if (PEM_write_bio_RSAPublicKey(lpBIO, lpRSA) == 0) { + throw ARL::Exception(__FILE__, __LINE__, "PEM_write_bio_RSAPublicKey failed."); + } } - } else { + static_assert(__Format == RSAKeyFormat::PEM || __Format == RSAKeyFormat::PKCS1); - __builtin_unreachable(); + } + + static_assert(__Type == RSAKeyType::PrivateKey || __Type == RSAKeyType::PublicKey); + } + + template + [[nodiscard]] + static RSA* _ReadRSAFromBIO(BIO* lpBIO) { + RSA* lpRSA; + + if constexpr (__Type == RSAKeyType::PrivateKey) { + lpRSA = PEM_read_bio_RSAPrivateKey(lpBIO, nullptr, nullptr, nullptr); + if (lpRSA == nullptr) { + throw ARL::Exception(__FILE__, __LINE__, "PEM_read_bio_RSAPrivateKey failed.") + .PushHint("Are you sure that you DO provide a valid RSA private key file?"); + } + } + + if constexpr (__Type == RSAKeyType::PublicKey) { + if constexpr (__Format == RSAKeyFormat::PEM) { + lpRSA = PEM_read_bio_RSA_PUBKEY(lpBIO, nullptr, nullptr, nullptr); + if (lpRSA == nullptr) { + throw ARL::Exception(__FILE__, __LINE__, "PEM_read_bio_RSA_PUBKEY failed.") + .PushHint("Are you sure that you DO provide a valid RSA public key file with PEM format?"); + } + } + + if constexpr (__Format == RSAKeyFormat::PKCS1) { + lpRSA = PEM_read_bio_RSAPublicKey(lpBIO, nullptr, nullptr, nullptr); + if (lpRSA == nullptr) { + throw ARL::Exception(__FILE__, __LINE__, "PEM_read_bio_RSAPublicKey failed.") + .PushHint("Are you sure that you DO provide a valid RSA public key file with PKCS1 format?"); + } + } + + static_assert(__Format == RSAKeyFormat::PEM || __Format == RSAKeyFormat::PKCS1); + } + + static_assert(__Type == RSAKeyType::PrivateKey || __Type == RSAKeyType::PublicKey); + + return lpRSA; + } + + public: + + RSACipher() : ARL::ResourceWrapper(RSA_new()) { + if (IsValid() == false) { + throw ARL::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_new failed."); } } - } - template - [[nodiscard]] - static RSA* pvt_ReadRSAFromBIO(BIO* lpBIO) { - RSA* lpRSA; - - if constexpr (_Type == RSAKeyType::PrivateKey) { - lpRSA = PEM_read_bio_RSAPrivateKey(lpBIO, nullptr, nullptr, nullptr); - if (lpRSA == nullptr) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "PEM_read_bio_RSAPrivateKey failed."); + [[nodiscard]] + size_t Bits() const { +#if (OPENSSL_VERSION_NUMBER & 0xffff0000) == 0x10000000 // openssl 1.0.x + if (Get()->n == nullptr) { + throw ARL::Exception(__FILE__, __LINE__, "RSA modulus has not been set."); + } else { + return BN_num_bits(Get()->n); } - } else { - if constexpr (_Format == RSAKeyFormat::PEM) { - lpRSA = PEM_read_bio_RSA_PUBKEY(lpBIO, nullptr, nullptr, nullptr); - if (lpRSA == nullptr) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, " -> PEM_read_bio_RSA_PUBKEY failed."); - } - } else if constexpr (_Format == RSAKeyFormat::PKCS1) { - lpRSA = PEM_read_bio_RSAPublicKey(lpBIO, nullptr, nullptr, nullptr); - if (lpRSA == nullptr) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "PEM_read_bio_RSAPublicKey failed."); +#elif (OPENSSL_VERSION_NUMBER & 0xffff0000) == 0x10100000 // openssl 1.1.x + return RSA_bits(Get()); +#else +#error "RSACipher.hpp: unexpected OpenSSL version." +#endif + } + + void GenerateKey(int bits, unsigned int e = RSA_F4) { + ARL::ResourceWrapper bn_e{ ARL::ResourceTraits::OpensslBIGNUM{} }; + + bn_e.TakeOver(BN_new()); + if (bn_e.IsValid() == false) { + throw ARL::OpensslError(__FILE__, __LINE__, ERR_get_error(), "BN_new failed."); + } + + if (!BN_set_word(bn_e, e)) { + throw ARL::Exception(__FILE__, __LINE__, "BN_set_word failed."); + } + + if (!RSA_generate_key_ex(Get(), bits, bn_e, nullptr)) { + throw ARL::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_generate_key_ex failed."); + } + } + + template + void ExportKeyToFile(std::string_view FileName) const { + ARL::ResourceWrapper KeyFile{ ARL::ResourceTraits::OpensslBIO{} }; + + KeyFile.TakeOver(BIO_new_file(FileName.data(), "w")); + if (KeyFile.IsValid() == false) { + throw ARL::Exception(__FILE__, __LINE__, "BIO_new_file failed."); + } + + _WriteRSAToBIO<__Type, __Format>(Get(), KeyFile); + } + + template + [[nodiscard]] + std::string ExportKeyString() const { + ARL::ResourceWrapper TempMemory{ ARL::ResourceTraits::OpensslBIO{} }; + const char* lpsz = nullptr; + + TempMemory.TakeOver(BIO_new(BIO_s_mem())); + if (TempMemory.IsValid() == false) { + throw ARL::Exception(__FILE__, __LINE__, "BIO_new failed."); + } + + _WriteRSAToBIO<__Type, __Format>(Get(), TempMemory); + + auto l = BIO_get_mem_data(TempMemory.Get(), &lpsz); + + std::string KeyString(lpsz, l); + while (KeyString.back() == '\n' || KeyString.back() == '\r') { + KeyString.pop_back(); + } + + return KeyString; + } + + template + void ImportKeyFromFile(std::string_view FileName) { + ARL::ResourceWrapper KeyFile{ ARL::ResourceTraits::OpensslBIO{} }; + + KeyFile.TakeOver(BIO_new_file(FileName.data(), "r")); + if (KeyFile.IsValid() == false) { + throw ARL::Exception(__FILE__, __LINE__, "BIO_new_file failed."); + } + + ReleaseAndTakeOver(_ReadRSAFromBIO<__Type, __Format>(KeyFile)); + } + + template + void ImportKeyString(std::string_view KeyString) { + ARL::ResourceWrapper TempMemory{ ARL::ResourceTraits::OpensslBIO{} }; + + TempMemory.TakeOver(BIO_new(BIO_s_mem())); + if (TempMemory.IsValid() == false) { + throw ARL::Exception(__FILE__, __LINE__, "BIO_new failed."); + } + + if (BIO_puts(TempMemory.Get(), KeyString.data()) <= 0) { + throw ARL::Exception(__FILE__, __LINE__, "BIO_puts failed."); + } + + TakeOver(_ReadRSAFromBIO<__Type, __Format>(TempMemory)); + } + + template + size_t Encrypt(const void* lpFrom, size_t cbFrom, void* lpTo, int Padding) const { + int BytesWritten; + + if (cbFrom > static_cast(INT_MAX)) { + throw ARL::Exception(__FILE__, __LINE__, "Length overflowed."); + } + + if constexpr (__Type == RSAKeyType::PrivateKey) { + BytesWritten = RSA_private_encrypt( + static_cast(cbFrom), + reinterpret_cast(lpFrom), + reinterpret_cast(lpTo), + Get(), + Padding + ); + + if (BytesWritten == -1) { + throw ARL::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_private_encrypt failed."); } } else { - static_assert(_Format == RSAKeyFormat::PEM || _Format == RSAKeyFormat::PKCS1); - __builtin_unreachable(); + BytesWritten = RSA_public_encrypt( + static_cast(cbFrom), + reinterpret_cast(lpFrom), + reinterpret_cast(lpTo), + Get(), + Padding + ); + + if (BytesWritten == -1) { + throw ARL::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_public_encrypt failed."); + } } + + return BytesWritten; } - return lpRSA; - } + template + size_t Decrypt(const void* lpFrom, size_t cbFrom, void* lpTo, int Padding) const { + int BytesWritten; -public: - - RSACipher() : pvt_RsaObj(OpensslRSATraits{}, RSA_new()) { - if (pvt_RsaObj.IsValid() == false) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_new failed."); - } - } - - [[nodiscard]] - size_t Bits() const { -#if (OPENSSL_VERSION_NUMBER & 0xffff0000) == 0x10000000 // openssl 1.0.x - if (pvt_RsaObj->n == nullptr) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "RSA modulus has not been set."); - } else { - return BN_num_bits(pvt_RsaObj->n); - } -#elif (OPENSSL_VERSION_NUMBER & 0xffff0000) == 0x10100000 // openssl 1.1.x - return RSA_bits(pvt_RsaObj); -#else -#error "Unexpected openssl version!" -#endif - } - - void GenerateKey(int bits, unsigned int e = RSA_F4) { - ResourceOwned bn_e(OpensslBNTraits{}, BN_new()); - - if (bn_e.IsValid() == false) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::OpensslError(__FILE__, __LINE__, ERR_get_error(), "BN_new failed."); - } - - if (!BN_set_word(bn_e, e)) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "BN_set_word failed."); - } - - if (!RSA_generate_key_ex(pvt_RsaObj, bits, bn_e, nullptr)) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_generate_key_ex failed."); - } - } - - template - void ExportKeyToFile(const std::string& FileName) const { - ResourceOwned BioFile(OpensslBIOTraits{}, BIO_new_file(FileName.c_str(), "w")); - - if (BioFile.IsValid() == false) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "BIO_new_file failed."); - } - - pvt_WriteRSAToBIO<__Type, __Format>(pvt_RsaObj, BioFile); - } - - template - [[nodiscard]] - std::string ExportKeyString() const { - ResourceOwned BioMemory(OpensslBIOTraits{}, BIO_new(BIO_s_mem())); - long StringLength; - const char* StringChars = nullptr; - - if (BioMemory.IsValid() == false) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "BIO_new failed."); - } - - pvt_WriteRSAToBIO<__Type, __Format>(pvt_RsaObj, BioMemory); - - StringLength = BIO_get_mem_data(BioMemory, &StringChars); - - return std::string(StringChars, StringLength); - } - - template - void ImportKeyFromFile(const std::string& FileName) { - ResourceOwned BioFile(OpensslBIOTraits{}, BIO_new_file(FileName.c_str(), "r")); - - if (BioFile.IsValid() == false) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "BIO_new_file failed."); - } - - pvt_RsaObj.TakeOver(pvt_ReadRSAFromBIO<__Type, __Format>(BioFile)); - } - - template - void ImportKeyString(const std::string& KeyString) { - ResourceOwned BioMemory(OpensslBIOTraits{}, BIO_new(BIO_s_mem())); - RSA* NewRsaObj; - - if (BioMemory.IsValid() == false) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "BIO_new failed."); - } - - if (BIO_puts(BioMemory, KeyString.c_str()) <= 0) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "BIO_puts failed."); - } - - pvt_RsaObj.TakeOver(pvt_ReadRSAFromBIO<__Type, __Format>(BioMemory)); - } - - template - size_t Encrypt(const void* lpFrom, size_t cbFrom, void* lpTo, int Padding) const { - int BytesWritten; - - if (cbFrom > INT_MAX) { - // NOLINTNEXTLINE: allow exceptions that is not derived lpFrom std::exception - throw nkg::Exception(__FILE__, __LINE__, "Length overflowed."); - } - - if constexpr (__Type == RSAKeyType::PrivateKey) { - BytesWritten = RSA_private_encrypt( - static_cast(cbFrom), - reinterpret_cast(lpFrom), - reinterpret_cast(lpTo), - pvt_RsaObj, - Padding - ); - - if (BytesWritten == -1) { - // NOLINTNEXTLINE: allow exceptions that is not derived lpFrom std::exception - throw nkg::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_private_encrypt failed."); + if (cbFrom > static_cast(INT_MAX)) { + throw ARL::Exception(__FILE__, __LINE__, "Length overflowed."); } - } else { - BytesWritten = RSA_public_encrypt( - static_cast(cbFrom), - reinterpret_cast(lpFrom), - reinterpret_cast(lpTo), - pvt_RsaObj, - Padding - ); - if (BytesWritten == -1) { - // NOLINTNEXTLINE: allow exceptions that is not derived lpFrom std::exception - throw nkg::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_public_encrypt failed."); + if constexpr (__Type == RSAKeyType::PrivateKey) { + BytesWritten = RSA_private_decrypt( + static_cast(cbFrom), + reinterpret_cast(lpFrom), + reinterpret_cast(lpTo), + Get(), + Padding + ); + + if (BytesWritten == -1) { + throw ARL::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_private_decrypt failed.") + .PushHint("Are your sure you DO provide a correct private key?"); + } + } else { + BytesWritten = RSA_public_decrypt( + static_cast(cbFrom), + reinterpret_cast(lpFrom), + reinterpret_cast(lpTo), + Get(), + Padding + ); + + if (BytesWritten == -1) { + throw ARL::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_public_decrypt failed.") + .PushHint("Are your sure you DO provide a correct public key?"); + } } + + return BytesWritten; } + }; - return BytesWritten; - } +} - template - size_t Decrypt(const void* lpFrom, int cbFrom, void* lpTo, int Padding) const { - int BytesWritten; - - if (cbFrom > INT_MAX) { - // NOLINTNEXTLINE: allow exceptions that is not derived lpFrom std::exception - throw nkg::Exception(__FILE__, __LINE__, "Length overflowed."); - } - - if constexpr (__Type == RSAKeyType::PrivateKey) { - BytesWritten = RSA_private_decrypt( - cbFrom, - reinterpret_cast(lpFrom), - reinterpret_cast(lpTo), - pvt_RsaObj, - Padding - ); - - if (BytesWritten == -1) { - // NOLINTNEXTLINE: allow exceptions that is not derived lpFrom std::exception - throw nkg::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_private_decrypt failed."); - } - } else { - BytesWritten = RSA_public_decrypt( - cbFrom, - reinterpret_cast(lpFrom), - reinterpret_cast(lpTo), - pvt_RsaObj, - Padding - ); - - if (BytesWritten == -1) { - // NOLINTNEXTLINE: allow exceptions that is not derived lpFrom std::exception - throw nkg::OpensslError(__FILE__, __LINE__, ERR_get_error(), "RSA_public_decrypt failed."); - } - } - - return BytesWritten; - } -}; \ No newline at end of file diff --git a/common/ResourceOwned.hpp b/common/ResourceOwned.hpp deleted file mode 100644 index ffcbc38..0000000 --- a/common/ResourceOwned.hpp +++ /dev/null @@ -1,304 +0,0 @@ -#pragma once -#include -#include - -template -class ResourceOwned { -private: - - using __HandleType = typename __ResourceTraits::HandleType; - static_assert(std::is_pod_v<__HandleType>); - - __HandleType pvt_Handle; - __LambdaReleasor pvt_Releasor; - -public: - - // - // Construct from custom releasor. - // ``pvt_Handle` will be set to an invalid value. - // - ResourceOwned(__ResourceTraits, __LambdaReleasor&& Releasor) noexcept : - pvt_Handle(__ResourceTraits::InvalidValue), - pvt_Releasor(std::forward<__LambdaReleasor>(Releasor)) {} - - // - // Construct from handle given and custom releasor. - // - ResourceOwned(__ResourceTraits, const __HandleType& Handle, __LambdaReleasor&& Releasor) noexcept : - pvt_Handle(Handle), - pvt_Releasor(std::forward<__LambdaReleasor>(Releasor)) {} - - // - // ResourceOwned doesn't allow to copy. - // Because it takes `pvt_Handle` exclusively. - // - ResourceOwned(const ResourceOwned<__ResourceTraits, __LambdaReleasor>& Other) = delete; - - // - // ResourceOwned allows to move. - // - ResourceOwned(ResourceOwned<__ResourceTraits, __LambdaReleasor>&& Other) noexcept : - pvt_Handle(Other.pvt_Handle), - pvt_Releasor(std::move(Other.pvt_Releasor)) - { - Other.pvt_Handle = __ResourceTraits::InvalidValue; - } - - // - // ResourceOwned doesn't allow to copy. - // Because it takes `pvt_Handle` exclusively. - // - ResourceOwned<__ResourceTraits, __LambdaReleasor>& - operator=(const ResourceOwned<__ResourceTraits, __LambdaReleasor>& Other) = delete; - - // - // ResourceOwned allows to move. - // - ResourceOwned<__ResourceTraits, __LambdaReleasor>& - operator=(ResourceOwned<__ResourceTraits, __LambdaReleasor>&& Other) noexcept { - this->pvt_Handle = Other.pvt_Handle; - this->pvt_Releasor = std::move(Other.pvt_Releasor); - Other.pvt_Handle = __ResourceTraits::InvalidValue; - return *this; - } - - [[nodiscard]] - operator const __HandleType&() const noexcept { // NOLINT: Allow implicit conversion. - return this->pvt_Handle; - } - - template, typename = std::enable_if_t<__IsPointer>> - [[nodiscard]] - __AsType As() const noexcept { - return reinterpret_cast<__AsType>(this->pvt_Handle); - } - - template, typename = typename std::enable_if_t<__IsPointer>> - [[nodiscard]] - __HandleType operator->() const noexcept { - return this->pvt_Handle; - } - - [[nodiscard]] - bool IsValid() const noexcept { - return __ResourceTraits::IsValid(this->pvt_Handle); - } - - [[nodiscard]] - const __HandleType& Get() const noexcept { - return this->pvt_Handle; - } - - template - [[nodiscard]] - __ReturnType GetAddress() noexcept { - return reinterpret_cast<__ReturnType>(&this->pvt_Handle); - } - - void TakeOver(const __HandleType& Handle) { - if (__ResourceTraits::IsValid(this->pvt_Handle) == true) { - this->pvt_Releasor(this->pvt_Handle); - } - this->pvt_Handle = Handle; - } - - void Discard() noexcept { - this->pvt_Handle = __ResourceTraits::InvalidValue; - } - - [[nodiscard]] - __HandleType Transfer() noexcept { - __HandleType t = this->pvt_Handle; - this->pvt_Handle = __ResourceTraits::InvalidValue; - return t; - } - - void Release() { - if (__ResourceTraits::IsValid(this->pvt_Handle)) { - this->pvt_Releasor(this->pvt_Handle); - this->pvt_Handle = __ResourceTraits::InvalidValue; - } - } - - ~ResourceOwned() { - if (__ResourceTraits::IsValid(this->pvt_Handle)) { - this->pvt_Releasor(this->pvt_Handle); - this->pvt_Handle = __ResourceTraits::InvalidValue; - } - } -}; - -template -class ResourceOwned<__ResourceTraits, void> { -private: - - using __HandleType = typename __ResourceTraits::HandleType; - static_assert(std::is_pod_v<__HandleType>); - - __HandleType pvt_Handle; - -public: - - // - // Construct from custom releasor. - // ``pvt_Handle` will be set to an invalid value. - // - explicit - ResourceOwned(__ResourceTraits) noexcept : - pvt_Handle(__ResourceTraits::InvalidValue) {} - - // - // Construct from handle given and custom releasor. - // - ResourceOwned(__ResourceTraits, const __HandleType& Handle) noexcept : - pvt_Handle(Handle) {} - - // - // ResourceOwned doesn't allow to copy. - // Because it takes `pvt_Handle` exclusively. - // - ResourceOwned(const ResourceOwned<__ResourceTraits, void>& Other) = delete; - - // - // ResourceOwned allows to move. - // - ResourceOwned(ResourceOwned<__ResourceTraits, void>&& Other) noexcept : - pvt_Handle(Other.pvt_Handle) - { - Other.pvt_Handle = __ResourceTraits::InvalidValue; - } - - // - // ResourceOwned doesn't allow to copy. - // Because it takes `pvt_Handle` exclusively. - // - ResourceOwned<__ResourceTraits, void>& - operator=(const ResourceOwned<__ResourceTraits, void>& Other) = delete; - - // - // ResourceOwned allows to move. - // - ResourceOwned<__ResourceTraits, void>& - operator=(ResourceOwned<__ResourceTraits, void>&& Other) noexcept { - this->pvt_Handle = Other.pvt_Handle; - Other.pvt_Handle = __ResourceTraits::InvalidValue; - return *this; - } - - [[nodiscard]] - operator const __HandleType&() const noexcept { // NOLINT: Allow implicit conversion. - return this->pvt_Handle; - } - - template, typename = typename std::enable_if_t<__IsPointer>> - [[nodiscard]] - __AsType As() const noexcept { - return reinterpret_cast<__AsType>(this->pvt_Handle); - } - - template, typename = typename std::enable_if_t<__IsPointer>> - [[nodiscard]] - __HandleType operator->() const noexcept { - return this->pvt_Handle; - } - - [[nodiscard]] - bool IsValid() const noexcept { - return __ResourceTraits::IsValid(this->pvt_Handle); - } - - [[nodiscard]] - const __HandleType& Get() const noexcept { - return this->pvt_Handle; - } - - template - [[nodiscard]] - __ReturnType GetAddress() noexcept { - return reinterpret_cast<__ReturnType>(&this->pvt_Handle); - } - - void TakeOver(const __HandleType& Handle) { - if (__ResourceTraits::IsValid(this->pvt_Handle) == true) { - __ResourceTraits::Releasor(this->pvt_Handle); - } - this->pvt_Handle = Handle; - } - - void Discard() noexcept { - this->pvt_Handle = __ResourceTraits::InvalidValue; - } - - [[nodiscard]] - __HandleType Transfer() noexcept { - __HandleType t = this->pvt_Handle; - this->pvt_Handle = __ResourceTraits::InvalidValue; - return t; - } - - void Release() { - if (__ResourceTraits::IsValid(this->pvt_Handle)) { - __ResourceTraits::Releasor(this->pvt_Handle); - this->pvt_Handle = __ResourceTraits::InvalidValue; - } - } - - ~ResourceOwned() { - if (__ResourceTraits::IsValid(this->pvt_Handle)) { - __ResourceTraits::Releasor(this->pvt_Handle); - this->pvt_Handle = __ResourceTraits::InvalidValue; - } - } -}; - -template -ResourceOwned(__ResourceTraits) -> - ResourceOwned<__ResourceTraits, void>; - -template -ResourceOwned(__ResourceTraits, __ArgType&&) -> - ResourceOwned< - __ResourceTraits, - std::conditional_t< - std::is_same_v>, typename __ResourceTraits::HandleType> == false, - std::remove_reference_t<__ArgType>, - void - > - >; - -template -ResourceOwned(__ResourceTraits, const typename __ResourceTraits::HandleType&, __LambdaReleasor&&) -> - ResourceOwned<__ResourceTraits, std::remove_reference_t<__LambdaReleasor>>; - -template -struct CppObjectTraits { - using HandleType = __ClassType*; - - static inline const HandleType InvalidValue = nullptr; - - [[nodiscard]] - static bool IsValid(const HandleType& Handle) noexcept { - return Handle != InvalidValue; - } - - static void Releasor(const HandleType& Handle) { - delete Handle; - } -}; - -template -struct CppDynamicArrayTraits { - using HandleType = __ClassType*; - - static inline const HandleType InvalidValue = nullptr; - - [[nodiscard]] - static bool IsValid(const HandleType& Handle) noexcept { - return Handle != InvalidValue; - } - - static void Releasor(const HandleType& Handle) { - delete[] Handle; - } -}; diff --git a/common/ResourceTraitsOpenssl.hpp b/common/ResourceTraitsOpenssl.hpp index 8def6dd..1782eb5 100644 --- a/common/ResourceTraitsOpenssl.hpp +++ b/common/ResourceTraitsOpenssl.hpp @@ -2,63 +2,67 @@ #include #include -struct OpensslBIOTraits { - using HandleType = BIO*; +namespace ARL::ResourceTraits { - static inline const HandleType InvalidValue = nullptr; + struct OpensslBIO { + using HandleType = BIO*; - [[nodiscard]] - static bool IsValid(const HandleType& Handle) noexcept { - return Handle != InvalidValue; - } + static inline const HandleType InvalidValue = nullptr; - static void Releasor(const HandleType& Handle) noexcept { - BIO_free(Handle); - } -}; + [[nodiscard]] + static bool IsValid(const HandleType& Handle) noexcept { + return Handle != InvalidValue; + } -struct OpensslBIOChainTraits { - using HandleType = BIO*; + static void Release(const HandleType& Handle) noexcept { + BIO_free(Handle); + } + }; - static inline const HandleType InvalidValue = nullptr; + struct OpensslBIOChain { + using HandleType = BIO*; - [[nodiscard]] - static bool IsValid(const HandleType& Handle) noexcept { - return Handle != InvalidValue; - } + static inline const HandleType InvalidValue = nullptr; - static void Releasor(const HandleType& Handle) noexcept { - BIO_free_all(Handle); - } -}; + [[nodiscard]] + static bool IsValid(const HandleType& Handle) noexcept { + return Handle != InvalidValue; + } -struct OpensslBNTraits { - using HandleType = BIGNUM*; + static void Release(const HandleType& Handle) noexcept { + BIO_free_all(Handle); + } + }; - static inline const HandleType InvalidValue = nullptr; + struct OpensslBIGNUM { + using HandleType = BIGNUM*; - [[nodiscard]] - static bool IsValid(const HandleType& Handle) noexcept { - return Handle != InvalidValue; - } + static inline const HandleType InvalidValue = nullptr; - static void Releasor(const HandleType& Handle) noexcept { - BN_free(Handle); - } -}; + [[nodiscard]] + static bool IsValid(const HandleType& Handle) noexcept { + return Handle != InvalidValue; + } -struct OpensslRSATraits { - using HandleType = RSA*; + static void Release(const HandleType& Handle) noexcept { + BN_free(Handle); + } + }; - static inline const HandleType InvalidValue = nullptr; + struct OpensslRSA { + using HandleType = RSA*; - [[nodiscard]] - static bool IsValid(const HandleType& Handle) noexcept { - return Handle != InvalidValue; - } + static inline const HandleType InvalidValue = nullptr; - static void Releasor(const HandleType& Handle) noexcept { - RSA_free(Handle); - } -}; + [[nodiscard]] + static bool IsValid(const HandleType& Handle) noexcept { + return Handle != InvalidValue; + } + + static void Release(const HandleType& Handle) noexcept { + RSA_free(Handle); + } + }; + +} diff --git a/common/ResourceWrapper.hpp b/common/ResourceWrapper.hpp new file mode 100644 index 0000000..cbb077e --- /dev/null +++ b/common/ResourceWrapper.hpp @@ -0,0 +1,375 @@ +#pragma once +#include +#include +#include + +namespace ARL { + + template + class ResourceWrapper { + public: + + using HandleType = typename __ResourceTraits::HandleType; + + static_assert( + std::is_trivial_v && std::is_standard_layout_v, + "HandleType must be a POD type." + ); + + private: + + HandleType m_Handle; + + public: + + ResourceWrapper() noexcept : + m_Handle(__ResourceTraits::InvalidValue) {} + + ResourceWrapper(const HandleType& Handle) noexcept : + m_Handle(Handle) {} + + ResourceWrapper(__ResourceTraits) noexcept : + m_Handle(__ResourceTraits::InvalidValue) {} + + ResourceWrapper(__ResourceTraits, const HandleType& Handle) noexcept : + m_Handle(Handle) {} + + ResourceWrapper(const ResourceWrapper& Other) = delete; + + ResourceWrapper(ResourceWrapper&& Other) noexcept : + m_Handle(std::move(Other.m_Handle)) + { + Other.m_Handle = __ResourceTraits::InvalidValue; + } + + ResourceWrapper& operator=(const ResourceWrapper& Other) = delete; + + ResourceWrapper& operator=(ResourceWrapper&& Other) noexcept { + if (this != std::addressof(Other)) { + if (IsValid()) { + __ResourceTraits::Release(m_Handle); + } + + m_Handle = std::move(Other.m_Handle); + Other.m_Handle = __ResourceTraits::InvalidValue; + } + + return *this; + } + + [[nodiscard]] + operator HandleType() const noexcept { // NOLINT: Allow implicit conversion. + return m_Handle; + } + + template + [[nodiscard]] + __AsType As() const noexcept { + return reinterpret_cast<__AsType>(m_Handle); + } + + template> + [[nodiscard]] + std::enable_if_t<__Enable, HandleType> operator->() const noexcept { + return m_Handle; + } + + [[nodiscard]] + bool IsValid() const noexcept { + return __ResourceTraits::IsValid(m_Handle); + } + + [[nodiscard]] + HandleType Get() const noexcept { + return m_Handle; + } + + [[nodiscard]] + HandleType* GetAddressOf() noexcept { + return &m_Handle; + } + + template + [[nodiscard]] + __ReturnType GetAddressOfAs() noexcept { + return reinterpret_cast<__ReturnType>(&m_Handle); + } + + void TakeOver(const HandleType& Handle) { + if (IsValid() == false) { + m_Handle = Handle; + } else { + throw std::runtime_error("ResourceWrapper is already in use."); + } + } + + void Discard() noexcept { + m_Handle = __ResourceTraits::InvalidValue; + } + + [[nodiscard]] + HandleType Transfer() noexcept { + auto t = m_Handle; + m_Handle = __ResourceTraits::InvalidValue; + return t; + } + + template + [[nodiscard]] + __ReturnType TransferAs() noexcept { + static_assert( + std::is_trivial_v<__ReturnType> && std::is_standard_layout_v<__ReturnType>, + "__ReturnType should also be a POD type, just like HandleType." + ); + + auto t = reinterpret_cast<__ReturnType>(m_Handle); + m_Handle = __ResourceTraits::InvalidValue; + return t; + } + + void Release() { + if (IsValid()) { + __ResourceTraits::Release(m_Handle); + m_Handle = __ResourceTraits::InvalidValue; + } + } + + void ReleaseAndTakeOver(const HandleType& Handle) { + if (IsValid()) { + __ResourceTraits::Release(m_Handle); + } + + m_Handle = Handle; + } + + [[nodiscard]] + HandleType* ReleaseAndGetAddressOf() { + if (IsValid()) { + __ResourceTraits::Release(m_Handle); + m_Handle = __ResourceTraits::InvalidValue; + } + + return GetAddressOf(); + } + + template + __ReturnType ReleaseAndGetAddressOfAs() { + if (IsValid()) { + __ResourceTraits::Release(m_Handle); + m_Handle = __ResourceTraits::InvalidValue; + } + + return GetAddressOfAs<__ReturnType>(); + } + + ~ResourceWrapper() { + Release(); + } + }; + + template + class ResourceWrapperEx { + public: + + using HandleType = typename __ResourceTraits::HandleType; + using DeleterType = __DeleterType; + + static_assert( + std::is_trivial_v && std::is_standard_layout_v, + "HandleType must be a POD type." + ); + + private: + + HandleType m_Handle; + DeleterType m_Deleter; + + public: + + template + ResourceWrapperEx(__ResourceTraits, __DeleterArgType&& Deleter) noexcept : + m_Handle(__ResourceTraits::InvalidValue), + m_Deleter(std::forward<__DeleterArgType>(Deleter)) {} + + template + ResourceWrapperEx(__ResourceTraits, const HandleType& Handle, __DeleterArgType&& Deleter) noexcept : + m_Handle(Handle), + m_Deleter(std::forward<__DeleterArgType>(Deleter)) {} + + ResourceWrapperEx(const ResourceWrapperEx& Other) = delete; + + ResourceWrapperEx(ResourceWrapperEx&& Other) noexcept : + m_Handle(std::move(Other.m_Handle)), + m_Deleter(std::move(Other.m_Deleter)) + { + Other.m_Handle = __ResourceTraits::InvalidValue; + } + + ResourceWrapperEx& operator=(const ResourceWrapperEx& Other) = delete; + + ResourceWrapperEx& operator=(ResourceWrapperEx&& Other) noexcept { + if (this != std::addressof(Other)) { + if (IsValid()) { + m_Deleter(m_Handle); + } + + m_Handle = std::move(Other.m_Handle); + m_Deleter = std::move(Other.m_Deleter); + Other.m_Handle = __ResourceTraits::InvalidValue; + } + + return *this; + } + + [[nodiscard]] + operator HandleType() const noexcept { // NOLINT: Allow implicit conversion. + return m_Handle; + } + + template + [[nodiscard]] + __AsType As() const noexcept { + return reinterpret_cast<__AsType>(m_Handle); + } + + template> + [[nodiscard]] + std::enable_if_t<__Enable, HandleType> operator->() const noexcept { + return m_Handle; + } + + [[nodiscard]] + bool IsValid() const noexcept { + return __ResourceTraits::IsValid(m_Handle); + } + + [[nodiscard]] + HandleType Get() const noexcept { + return m_Handle; + } + + [[nodiscard]] + HandleType* GetAddressOf() noexcept { + return &m_Handle; + } + + template + [[nodiscard]] + __ReturnType GetAddressOfAs() noexcept { + return reinterpret_cast<__ReturnType>(&m_Handle); + } + + void TakeOver(const HandleType& Handle) { + if (IsValid() == false) { + m_Handle = Handle; + } else { + throw std::runtime_error("ResourceWrapperEx is already in use."); + } + } + + void Discard() noexcept { + m_Handle = __ResourceTraits::InvalidValue; + } + + [[nodiscard]] + HandleType Transfer() noexcept { + auto t = m_Handle; + m_Handle = __ResourceTraits::InvalidValue; + return t; + } + + template + [[nodiscard]] + __ReturnType TransferAs() noexcept { + static_assert(std::is_trivial_v<__ReturnType> && std::is_standard_layout_v<__ReturnType>); + + auto t = reinterpret_cast<__ReturnType>(m_Handle); + m_Handle = __ResourceTraits::InvalidValue; + return t; + } + + void Release() { + if (IsValid()) { + m_Deleter(m_Handle); + m_Handle = __ResourceTraits::InvalidValue; + } + } + + void ReleaseAndTakeOver(const HandleType& Handle) { + if (IsValid()) { + __ResourceTraits::Release(m_Handle); + } + + m_Handle = Handle; + } + + [[nodiscard]] + HandleType* ReleaseAndGetAddressOf() { + if (IsValid()) { + m_Deleter(m_Handle); + m_Handle = __ResourceTraits::InvalidValue; + } + + return GetAddressOf(); + } + + template + __ReturnType ReleaseAndGetAddressOfAs() { + if (IsValid()) { + m_Deleter(m_Handle); + m_Handle = __ResourceTraits::InvalidValue; + } + + return GetAddressOfAs<__ReturnType>(); + } + + ~ResourceWrapperEx() { + Release(); + } + }; + + template + ResourceWrapperEx(__ResourceTraits, __DeleterArgType&& Deleter) -> + ResourceWrapperEx<__ResourceTraits, std::remove_reference_t<__DeleterArgType>>; + + template + ResourceWrapperEx(__ResourceTraits, const typename __ResourceTraits::HandleType& Handle, __DeleterArgType&& Deleter) -> + ResourceWrapperEx<__ResourceTraits, std::remove_reference_t<__DeleterArgType>>; + + namespace ResourceTraits { + + template + struct CppObject { + using HandleType = __ClassType*; + + static inline const HandleType InvalidValue = nullptr; + + [[nodiscard]] + static bool IsValid(const HandleType& Handle) noexcept { + return Handle != InvalidValue; + } + + static void Release(const HandleType& Handle) { + delete Handle; + } + }; + + template + struct CppArray { + using HandleType = __ElementType*; + + static inline const HandleType InvalidValue = nullptr; + + [[nodiscard]] + static bool IsValid(const HandleType& Handle) noexcept { + return Handle != InvalidValue; + } + + static void Release(const HandleType& Handle) { + delete[] Handle; + } + }; + + } +} + diff --git a/navicat-keygen/Base32.hpp b/navicat-keygen/Base32.hpp new file mode 100644 index 0000000..cdaeaee --- /dev/null +++ b/navicat-keygen/Base32.hpp @@ -0,0 +1,134 @@ +#pragma once +#include +#include +#include + +[[nodiscard]] +inline std::string base32_encode(const void* lpBinary, size_t cbBinary) { + static const std::string::value_type Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + static constexpr std::string::value_type PaddingChar = '='; + + std::string szBase32; + + if (auto pbBinary = reinterpret_cast(lpBinary); cbBinary) { + szBase32.reserve((cbBinary * 8 + 4) / 5); + + uint8_t Idx = 0; + uint8_t BitsLeft = 8; + for (size_t i = 0; i < cbBinary;) { + if (BitsLeft < 5) { + Idx = pbBinary[i] << (5 - BitsLeft); + + ++i; + if (i != cbBinary) { + Idx |= pbBinary[i] >> (3 + BitsLeft); + } + + Idx &= 0x1F; + BitsLeft += 3; + } else { + Idx = pbBinary[i] >> (BitsLeft - 5); + + Idx &= 0x1F; + BitsLeft -= 5; + } + + szBase32.append(1, Alphabet[Idx]); + + if (BitsLeft == 0) { + BitsLeft = 8; + ++i; + } + } + + if (szBase32.length() % 8) { + size_t Padding = 8 - szBase32.length() % 8; + szBase32.append(Padding, PaddingChar); + } + } + + return szBase32; +} + +[[nodiscard]] +inline std::string base32_encode(const std::vector& Binary) { + return base32_encode(Binary.data(), Binary.size()); +} + +[[nodiscard]] +inline std::string base32_encode(const std::initializer_list& Binary) { + return base32_encode(Binary.begin(), Binary.size()); +} + +[[nodiscard]] +inline std::vector base32_decode(std::string_view szBase32) { + static constexpr std::string::value_type PaddingChar = '='; + + std::vector Binary; + + if (szBase32.length()) { + Binary.reserve((szBase32.length() * 5 + 7) / 8); + + uint8_t Byte = 0; + uint8_t BitsNeed = 8; + for (size_t i = 0; i < szBase32.length(); ++i) { + uint8_t Idx; + if ('A' <= szBase32[i] && szBase32[i] <= 'Z') { + Idx = szBase32[i] - 'A'; + } else if ('a' <= szBase32[i] && szBase32[i] <= 'z') { + Idx = szBase32[i] - 'a'; + } else if ('2' <= szBase32[i] && szBase32[i] <= '7') { + Idx = szBase32[i] - '2' + 26; + } else if (szBase32[i] == PaddingChar) { + for (size_t j = i + 1; j < szBase32.length(); ++j) { + if (szBase32[j] != PaddingChar) { + throw std::invalid_argument("base32_decode: invalid padding schema."); + } + } + + break; + } else { + throw std::invalid_argument("base32_decode: non-Base32 character is detected."); + } + + if (BitsNeed >= 5) { + Byte |= Idx; + + BitsNeed -= 5; + Byte <<= BitsNeed; + } else { + Byte |= Idx >> (5 - BitsNeed); + Binary.push_back(Byte); + + BitsNeed += 3; + Byte = Idx << BitsNeed; + if (BitsNeed > 5) { + Byte >>= BitsNeed - 5; + } + } + } + + switch (BitsNeed) { + case 1: + case 2: + case 3: + throw std::invalid_argument("base32_decode: base32 string is corrupted."); + case 4: + case 5: + case 6: + case 7: + if (Byte != 0) { + throw std::invalid_argument("base32_decode: base32 string is corrupted."); + } + break; + case 0: + case 8: + break; + default: + __builtin_unreachable(); + } + } + + return Binary; +} + diff --git a/navicat-keygen/Base64.cpp b/navicat-keygen/Base64.cpp deleted file mode 100644 index f0ebf43..0000000 --- a/navicat-keygen/Base64.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include -#include -#include -#include "../common/Exception.hpp" -#include "../common/ResourceOwned.hpp" -#include "../common/ResourceTraitsOpenssl.hpp" - -std::string base64_encode(const std::vector& bindata) { - ResourceOwned b64(OpensslBIOTraits{}, BIO_new(BIO_f_base64())); - if (b64.IsValid() == false) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "BIO_new failed."); - } - - BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); - - ResourceOwned mem(OpensslBIOTraits{}, BIO_new(BIO_s_mem())); - if (mem.IsValid() == false) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "BIO_new failed."); - } - - BIO_push(b64, mem); - - if (BIO_write(b64, bindata.data(), bindata.size()) != bindata.size()) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "BIO_write failed."); - } - - if (BIO_flush(b64) != 1) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "BIO_flush failed."); - } - - const char* data = nullptr; - auto len = BIO_get_mem_data(mem, &data); - - BIO_pop(b64); - - return std::string(data, len); -} - -std::vector base64_decode(const std::string& ascdata) { - ResourceOwned b64(OpensslBIOTraits{}, BIO_new(BIO_f_base64())); - if (b64.IsValid() == false) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "BIO_new failed."); - } - - BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); - - ResourceOwned mem(OpensslBIOTraits{}, BIO_new_mem_buf(ascdata.c_str(), -1)); - if (mem.IsValid() == false) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "BIO_new failed."); - } - - BIO_push(b64, mem); - - std::vector bindata(ascdata.length() / 4 * 3 + 1); - bindata.resize(BIO_read(b64, bindata.data(), bindata.size())); - - BIO_pop(b64); - - return bindata; -} diff --git a/navicat-keygen/Base64.hpp b/navicat-keygen/Base64.hpp new file mode 100644 index 0000000..7ce0359 --- /dev/null +++ b/navicat-keygen/Base64.hpp @@ -0,0 +1,134 @@ +#pragma once +#include +#include +#include + +[[nodiscard]] +inline std::string base64_encode(const void* lpBinary, size_t cbBinary) { + static const std::string::value_type Alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + static constexpr std::string::value_type PaddingChar = '='; + + std::string szBase64; + + if (auto pbBinary = reinterpret_cast(lpBinary); cbBinary) { + szBase64.reserve((cbBinary * 8 + 5) / 6); + + uint8_t Idx = 0; + uint8_t BitsLeft = 8; + for (size_t i = 0; i < cbBinary;) { + if (BitsLeft < 6) { + Idx = pbBinary[i] << (6 - BitsLeft); + + ++i; + if (i != cbBinary) { + Idx |= pbBinary[i] >> (2 + BitsLeft); + } + + Idx &= 0x3F; + BitsLeft += 2; + } else { + Idx = pbBinary[i] >> (BitsLeft - 6); + + Idx &= 0x3F; + BitsLeft -= 6; + } + + szBase64.append(1, Alphabet[Idx]); + + if (BitsLeft == 0) { + BitsLeft = 8; + ++i; + } + } + + if (szBase64.length() % 4) { + size_t Padding = 4 - szBase64.length() % 4; + szBase64.append(Padding, PaddingChar); + } + } + + return szBase64; +} + +[[nodiscard]] +inline std::string base64_encode(const std::vector& Binary) { + return base64_encode(Binary.data(), Binary.size()); +} + +[[nodiscard]] +inline std::string base64_encode(const std::initializer_list& Binary) { + return base64_encode(Binary.begin(), Binary.size()); +} + +[[nodiscard]] +inline std::vector base64_decode(std::string_view szBase64) { + static constexpr std::string::value_type PaddingChar = '='; + + std::vector Binary; + + if (szBase64.length()) { + Binary.reserve((szBase64.length() * 6 + 7) / 8); + + uint8_t Byte = 0; + uint8_t BitsNeed = 8; + for (size_t i = 0; i < szBase64.length(); ++i) { + uint8_t Idx; + if ('A' <= szBase64[i] && szBase64[i] <= 'Z') { + Idx = szBase64[i] - 'A'; + } else if ('a' <= szBase64[i] && szBase64[i] <= 'z') { + Idx = szBase64[i] - 'a' + 26; + } else if ('0' <= szBase64[i] && szBase64[i] <= '9') { + Idx = szBase64[i] - '0' + 26 + 26; + } else if (szBase64[i] == '+') { + Idx = 26 + 26 + 10; + } else if (szBase64[i] == '/') { + Idx = 26 + 26 + 10 + 1; + } else if (szBase64[i] == PaddingChar) { + for (size_t j = i + 1; j < szBase64.length(); ++j) { + if (szBase64[j] != PaddingChar) { + throw std::invalid_argument("base64_decode: invalid padding schema."); + } + } + + break; + } else { + throw std::invalid_argument("base64_decode: non-Base64 character is detected."); + } + + if (BitsNeed >= 6) { + Byte |= Idx; + + BitsNeed -= 6; + Byte <<= BitsNeed; + } else { + Byte |= Idx >> (6 - BitsNeed); + Binary.push_back(Byte); + + BitsNeed += 2; + Byte = Idx << BitsNeed; + if (BitsNeed > 6) { + Byte >>= BitsNeed - 6; + } + } + } + + switch (BitsNeed) { + case 2: + throw std::invalid_argument("base64_decode: base64 string is corrupted."); + case 4: + case 6: + if (Byte != 0) { + throw std::invalid_argument("base64_decode: base64 string is corrupted."); + } + break; + case 0: + case 8: + break; + default: + __builtin_unreachable(); + } + } + + return Binary; +} + diff --git a/navicat-keygen/CollectInformation.cpp b/navicat-keygen/CollectInformation.cpp new file mode 100644 index 0000000..f0f64d5 --- /dev/null +++ b/navicat-keygen/CollectInformation.cpp @@ -0,0 +1,121 @@ +#include "SerialNumberGenerator.hpp" +#include "ExceptionGeneric.hpp" +#include + +namespace nkg { + + [[nodiscard]] + static int ReadInt(int MinVal, int MaxVal, const char* lpszPrompt, const char* lpszErrorMessage) { + int val; + std::string s; + while (true) { + std::cout << lpszPrompt; + if (!std::getline(std::cin, s)) { + throw ARL::EOFError(__FILE__, __LINE__, "Abort."); + } + + if (s.empty()) + continue; + + try { + val = std::stoi(s, nullptr, 0); + if (MinVal <= val && val <= MaxVal) { + return val; + } else { + throw std::invalid_argument("Out of range."); + } + } catch (std::invalid_argument&) { + std::cout << lpszErrorMessage << std::endl; + } + } + } + + [[nodiscard]] + static int ReadInt(int MinVal, int MaxVal, int DefaultVal, const char* lpszPrompt, const char* lpszErrorMessage) { + int val; + std::string s; + while (true) { + std::cout << lpszPrompt; + if (!std::getline(std::cin, s)) { + throw ARL::EOFError(__FILE__, __LINE__, "Abort."); + } + + if (s.empty()) { + return DefaultVal; + } + + try { + val = std::stoi(s, nullptr, 0); + if (MinVal <= val && val <= MaxVal) { + return val; + } else { + throw std::invalid_argument("Out of range."); + } + } catch (std::invalid_argument&) { + std::cout << lpszErrorMessage << std::endl; + } + } + } + + [[nodiscard]] + SerialNumberGenerator CollectInformationNormal() { + SerialNumberGenerator Generator; + + Generator.SetProductSignature(NavicatProductType::Premium); + + std::cout << "[*] Select product language:" << std::endl; + std::cout << " 0. English" << std::endl; + std::cout << " 1. Simplified Chinese" << std::endl; + std::cout << " 2. Traditional Chinese" << std::endl; + std::cout << " 3. Japanese" << std::endl; + std::cout << " 4. Polish" << std::endl; + std::cout << " 5. Spanish" << std::endl; + std::cout << " 6. French" << std::endl; + std::cout << " 7. German" << std::endl; + std::cout << " 8. Korean" << std::endl; + std::cout << " 9. Russian" << std::endl; + std::cout << " 10. Portuguese" << std::endl; + std::cout << std::endl; + Generator.SetLanguageSignature( + static_cast(ReadInt(0, 10, "(Input index)> ", "Invalid index.")) + ); + + std::cout << std::endl; + std::cout << "[*] Input major version number:" << std::endl; + Generator.SetVersion( + static_cast(ReadInt(0, 15, 15, "(range: 0 ~ 15, default: 15)> ", "Invalid number.")) + ); + + std::cout << std::endl; + return Generator; + } + + [[nodiscard]] + SerialNumberGenerator CollectInformationAdvanced() { + SerialNumberGenerator Generator; + + std::cout << "[*] Navicat Product Signature:" << std::endl; + Generator.SetProductSignature( + static_cast(ReadInt(0x00, 0xff, "(range: 0x00 ~ 0xFF)> ", "Invalid number.")) + ); + + std::cout << std::endl; + std::cout << "[*] Navicat Language Signature 0:" << std::endl; + auto s1 = static_cast(ReadInt(0x00, 0xff, "(range: 0x00 ~ 0xFF)> ", "Invalid number.")); + + std::cout << std::endl; + std::cout << "[*] Navicat Language Signature 1:" << std::endl; + auto s2 = static_cast(ReadInt(0x00, 0xff, "(range: 0x00 ~ 0xFF)> ", "Invalid number.")); + + Generator.SetLanguageSignature(s1, s2); + + std::cout << std::endl; + std::cout << "[*] Input major version number:" << std::endl; + Generator.SetVersion( + static_cast(ReadInt(0, 15, 12, "(range: 0 ~ 15, default: 12)> ", "Invalid number.")) + ); + + std::cout << std::endl; + return Generator; + } +} diff --git a/navicat-keygen/DESCipher.hpp b/navicat-keygen/DESCipher.hpp deleted file mode 100644 index de15505..0000000 --- a/navicat-keygen/DESCipher.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once -#include -#include -#include - -class DESCipher { -private: - DES_cblock _Key; - DES_key_schedule _Schedule; -public: - - DESCipher() noexcept : _Key{}, _Schedule{} {} - - void SetKey(const void* pKey) noexcept { - memcpy(&_Key, pKey, sizeof(_Key)); - DES_set_odd_parity(&_Key); - DES_set_key(&_Key, &_Schedule); - } - - void Clear() noexcept { - OPENSSL_cleanse(&_Key, sizeof(_Key)); - OPENSSL_cleanse(&_Schedule, sizeof(_Schedule)); - } - - void EncryptBlock(void* lpBuffer) noexcept { - DES_cblock block; - DES_ecb_encrypt(reinterpret_cast(lpBuffer), &block, &_Schedule, DES_ENCRYPT); - memcpy(lpBuffer, &block, sizeof(block)); - } - - void DecryptBlock(void* lpBuffer) noexcept { - DES_cblock block; - DES_ecb_encrypt(reinterpret_cast(lpBuffer), &block, &_Schedule, DES_DECRYPT); - memcpy(lpBuffer, &block, sizeof(block)); - } - - ~DESCipher() noexcept { - Clear(); - } -}; - diff --git a/navicat-keygen/GenerateLicense.cpp b/navicat-keygen/GenerateLicense.cpp new file mode 100644 index 0000000..f694a64 --- /dev/null +++ b/navicat-keygen/GenerateLicense.cpp @@ -0,0 +1,131 @@ +#include "Exception.hpp" +#include "ExceptionGeneric.hpp" +#include "ResourceWrapper.hpp" +#include "ResourceTraitsOpenssl.hpp" +#include "RSACipher.hpp" +#include "Base64.hpp" +#include "SerialNumberGenerator.hpp" + +#include +#include +#include +#include +#include + +namespace nkg { + + void GenerateLicenseText(const RSACipher& Cipher, const SerialNumberGenerator& Generator) { + std::string utf8username; + std::string utf8organization; + + std::string b64RequestCode; + std::vector RequestCode; + std::string utf8RequestInfo; + std::string utf8ResponseInfo; + std::vector ResponseCode; + std::string b64ResponseCode; + + std::cout << "[*] Your name: "; + if (!std::getline(std::cin, utf8username)) { + throw ARL::EOFError(__FILE__, __LINE__, "Abort."); + } + + std::cout << "[*] Your organization: "; + if (!std::getline(std::cin, utf8organization)) { + throw ARL::EOFError(__FILE__, __LINE__, "Abort."); + } + + std::cout << std::endl; + + std::cout << "[*] Input request code in Base64: (Double press ENTER to end)" << std::endl; + while (true) { + std::string temp; + if (!std::getline(std::cin, temp)) { + throw ARL::EOFError(__FILE__, __LINE__, "Abort."); + } + + if (temp.empty()) { + break; + } + + b64RequestCode.append(temp); + } + + RequestCode = base64_decode(b64RequestCode); + if (RequestCode.size() != 256) { + throw ARL::AssertionError(__FILE__, __LINE__, "Broken request code. %zu", RequestCode.size()); + } + + utf8RequestInfo.resize((Cipher.Bits() + 7) / 8); + Cipher.Decrypt(RequestCode.data(), RequestCode.size(), utf8RequestInfo.data(), RSA_PKCS1_PADDING); + while (utf8RequestInfo.back() == '\x00') { + utf8RequestInfo.pop_back(); + } + + std::cout << "[*] Request Info:" << std::endl; + std::cout << utf8RequestInfo << std::endl; + std::cout << std::endl; + + rapidjson::Document json; + rapidjson::Value N_Key; + rapidjson::Value N_Value; + rapidjson::Value O_Key; + rapidjson::Value O_Value; + rapidjson::Value T_Key; + rapidjson::Value T_Value; + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + + // + // Begin to parse + // + json.Parse(utf8RequestInfo.c_str()); + // + // Remove "Platform" info + // + json.RemoveMember("P"); + // + // Set "Name" info + // + N_Key.SetString("N", 1); + N_Value.SetString(utf8username.c_str(), static_cast(utf8username.length())); + // + // Set "Organization" info + // + O_Key.SetString("O", 1); + O_Value.SetString(utf8organization.c_str(), static_cast(utf8organization.length())); + // + // Set "Time" info + // + T_Key.SetString("T", 1); + T_Value.SetUint(static_cast(std::time(nullptr))); + // + // Add "Name", "Organization" and "Time" + // + json.AddMember(N_Key, N_Value, json.GetAllocator()); + json.AddMember(O_Key, O_Value, json.GetAllocator()); + json.AddMember(T_Key, T_Value, json.GetAllocator()); + + json.Accept(writer); + if (buffer.GetSize() > 240) { + throw ARL::Exception(__FILE__, __LINE__, "Response info is too long."); + } + + utf8ResponseInfo.assign(buffer.GetString(), buffer.GetSize()); + + std::cout << "[*] Response Info:" << std::endl; + std::cout << utf8ResponseInfo << std::endl; + std::cout << std::endl; + + ResponseCode.resize((Cipher.Bits() + 7) / 8); + Cipher.Encrypt(utf8ResponseInfo.data(), utf8ResponseInfo.size(), ResponseCode.data(), RSA_PKCS1_PADDING); + + b64ResponseCode = base64_encode(ResponseCode); + + std::cout << "[*] Activation Code:" << std::endl; + std::cout << b64ResponseCode << std::endl; + std::cout << std::endl; + } + +} + diff --git a/navicat-keygen/NavicatKeygen.hpp b/navicat-keygen/NavicatKeygen.hpp deleted file mode 100644 index 8d690f3..0000000 --- a/navicat-keygen/NavicatKeygen.hpp +++ /dev/null @@ -1,195 +0,0 @@ -#pragma once -#include -#include -#include "DESCipher.hpp" - -class NavicatKeygen { -public: - enum class Language { - English, - SimplifiedChinese, - TraditionalChinese, - Japanese, - Polish, - Spanish, - French, - German, - Korean, - Russian, - Portuguese - }; - - enum class Product { - DataModeler, - Premium, - MySQL, - PostgreSQL, - Oracle, - SQLServer, - SQLite, - MariaDB, - MongoDB, - ReportViewer - }; -private: - std::random_device rand_dev; - std::default_random_engine rand_eng; - std::uniform_int_distribution rand; - uint8_t data[10]; -public: - - NavicatKeygen() : rand_eng(rand_dev()), rand(0, UINT8_MAX), data{} { - data[0] = 0x68; - data[1] = 0x2A; - } - - void SetLanguageSignature(Language _language) { - switch (_language) { - case Language::English: - data[5] = 0xAC; // Must be 0xAC for English version. - data[6] = 0x88; // Must be 0x88 for English version. - break; - case Language::SimplifiedChinese: - data[5] = 0xCE; // Must be 0xCE for Simplified Chinese version. - data[6] = 0x32; // Must be 0x32 for Simplified Chinese version. - break; - case Language::TraditionalChinese: - data[5] = 0xAA; // Must be 0xAA for Traditional Chinese version. - data[6] = 0x99; // Must be 0x99 for Traditional Chinese version. - break; - case Language::Japanese: - data[5] = 0xAD; // Must be 0xAD for Japanese version. Discoverer: @dragonflylee - data[6] = 0x82; // Must be 0x82 for Japanese version. Discoverer: @dragonflylee - break; - case Language::Polish: - data[5] = 0xBB; // Must be 0xBB for Polish version. Discoverer: @dragonflylee - data[6] = 0x55; // Must be 0x55 for Polish version. Discoverer: @dragonflylee - break; - case Language::Spanish: - data[5] = 0xAE; // Must be 0xAE for Spanish version. Discoverer: @dragonflylee - data[6] = 0x10; // Must be 0x10 for Spanish version. Discoverer: @dragonflylee - break; - case Language::French: - data[5] = 0xFA; // Must be 0xFA for French version. Discoverer: @Deltafox79 - data[6] = 0x20; // Must be 0x20 for French version. Discoverer: @Deltafox79 - break; - case Language::German: - data[5] = 0xB1; // Must be 0xB1 for German version. Discoverer: @dragonflylee - data[6] = 0x60; // Must be 0x60 for German version. Discoverer: @dragonflylee - break; - case Language::Korean: - data[5] = 0xB5; // Must be 0xB5 for Korean version. Discoverer: @dragonflylee - data[6] = 0x60; // Must be 0x60 for Korean version. Discoverer: @dragonflylee - break; - case Language::Russian: - data[5] = 0xEE; // Must be 0xB5 for Russian version. Discoverer: @dragonflylee - data[6] = 0x16; // Must be 0x60 for Russian version. Discoverer: @dragonflylee - break; - case Language::Portuguese: - data[5] = 0xCD; // Must be 0xCD for Portuguese version. Discoverer: @dragonflylee - data[6] = 0x49; // Must be 0x49 for Portuguese version. Discoverer: @dragonflylee - break; - default: - break; - } - } - - void SetLanguageSignature(uint8_t value0, uint8_t value1) { - data[5] = value0; - data[6] = value1; - } - - void SetProductSignature(Product _product) { - switch (_product) { - case Product::DataModeler: - data[7] = 0x47; - break; - case Product::Premium: - data[7] = 0x65; - break; - case Product::MySQL: - data[7] = 0x68; - break; - case Product::PostgreSQL: - data[7] = 0x6C; - break; - case Product::Oracle: - data[7] = 0x70; - break; - case Product::SQLServer: - data[7] = 0x74; - break; - case Product::SQLite: - data[7] = 0x78; - break; - case Product::MariaDB: - data[7] = 0x7C; - break; - case Product::MongoDB: - data[7] = 0x80; - break; - case Product::ReportViewer: - data[7] = 0xb; - default: - break; - } - } - - void SetProductSignature(uint8_t value) { - data[7] = value; - } - - void SetVersion(uint8_t version) { - data[8] = version << 4; - } - - void Generate() { - static uint8_t DESKey[] = { 0x64, 0xAD, 0xF3, 0x2F, 0xAE, 0xF2, 0x1A, 0x27 }; - DESCipher cipher; - - data[2] = static_cast(rand(rand_eng)); - data[3] = static_cast(rand(rand_eng)); - data[4] = static_cast(rand(rand_eng)); - data[9] = 0x32; - - cipher.SetKey(&DESKey); - cipher.EncryptBlock(data + 2); - } - - std::string GetKey() const { - static const char EncodeTable[] = "ABCDEFGH8JKLMN9PQRSTUVWXYZ234567"; - std::string Key; - - Key.resize(16); - - Key[0] = EncodeTable[data[0] >> 3]; - Key[1] = EncodeTable[(data[0] & 0x07) << 2 | data[1] >> 6]; - Key[2] = EncodeTable[data[1] >> 1 & 0x1F]; - Key[3] = EncodeTable[(data[1] & 0x1) << 4 | data[2] >> 4]; - Key[4] = EncodeTable[(data[2] & 0xF) << 1 | data[3] >> 7]; - Key[5] = EncodeTable[data[3] >> 2 & 0x1F]; - Key[6] = EncodeTable[(data[3] << 3 & 0x1F) | data[4] >> 5]; - Key[7] = EncodeTable[data[4] & 0x1F]; - - Key[8] = EncodeTable[data[5] >> 3]; - Key[9] = EncodeTable[(data[5] & 0x07) << 2 | data[6] >> 6]; - Key[10] = EncodeTable[data[6] >> 1 & 0x1F]; - Key[11] = EncodeTable[(data[6] & 0x1) << 4 | data[7] >> 4]; - Key[12] = EncodeTable[(data[7] & 0xF) << 1 | data[8] >> 7]; - Key[13] = EncodeTable[data[8] >> 2 & 0x1F]; - Key[14] = EncodeTable[(data[8] << 3 & 0x1F) | data[9] >> 5]; - Key[15] = EncodeTable[data[9] & 0x1F]; - - return Key; - } - - std::string GetFormatedKey() const { - std::string Key = GetKey(); - Key.insert(Key.begin() + 4, '-'); - Key.insert(Key.begin() + 5 + 4, '-'); - Key.insert(Key.begin() + 10 + 4, '-'); - return Key; - } - -}; - diff --git a/navicat-keygen/SerialNumberGenerator.cpp b/navicat-keygen/SerialNumberGenerator.cpp new file mode 100644 index 0000000..d737a64 --- /dev/null +++ b/navicat-keygen/SerialNumberGenerator.cpp @@ -0,0 +1,183 @@ +#include "SerialNumberGenerator.hpp" +#include "Exception.hpp" +#include +#include +#include +#include +#include "Base32.hpp" + +namespace nkg { + + SerialNumberGenerator::SerialNumberGenerator() noexcept : + m_Data{ 0x68 , 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32 } {} + + void SerialNumberGenerator::SetLanguageSignature(NavicatLanguage Language) noexcept { + switch (Language) { + case NavicatLanguage::English: + m_Data[5] = 0xAC; // Must be 0xAC for English version. + m_Data[6] = 0x88; // Must be 0x88 for English version. + break; + case NavicatLanguage::SimplifiedChinese: + m_Data[5] = 0xCE; // Must be 0xCE for Simplified Chinese version. + m_Data[6] = 0x32; // Must be 0x32 for Simplified Chinese version. + break; + case NavicatLanguage::TraditionalChinese: + m_Data[5] = 0xAA; // Must be 0xAA for Traditional Chinese version. + m_Data[6] = 0x99; // Must be 0x99 for Traditional Chinese version. + break; + case NavicatLanguage::Japanese: + m_Data[5] = 0xAD; // Must be 0xAD for Japanese version. Discoverer: @dragonflylee + m_Data[6] = 0x82; // Must be 0x82 for Japanese version. Discoverer: @dragonflylee + break; + case NavicatLanguage::Polish: + m_Data[5] = 0xBB; // Must be 0xBB for Polish version. Discoverer: @dragonflylee + m_Data[6] = 0x55; // Must be 0x55 for Polish version. Discoverer: @dragonflylee + break; + case NavicatLanguage::Spanish: + m_Data[5] = 0xAE; // Must be 0xAE for Spanish version. Discoverer: @dragonflylee + m_Data[6] = 0x10; // Must be 0x10 for Spanish version. Discoverer: @dragonflylee + break; + case NavicatLanguage::French: + m_Data[5] = 0xFA; // Must be 0xFA for French version. Discoverer: @Deltafox79 + m_Data[6] = 0x20; // Must be 0x20 for French version. Discoverer: @Deltafox79 + break; + case NavicatLanguage::German: + m_Data[5] = 0xB1; // Must be 0xB1 for German version. Discoverer: @dragonflylee + m_Data[6] = 0x60; // Must be 0x60 for German version. Discoverer: @dragonflylee + break; + case NavicatLanguage::Korean: + m_Data[5] = 0xB5; // Must be 0xB5 for Korean version. Discoverer: @dragonflylee + m_Data[6] = 0x60; // Must be 0x60 for Korean version. Discoverer: @dragonflylee + break; + case NavicatLanguage::Russian: + m_Data[5] = 0xEE; // Must be 0xB5 for Russian version. Discoverer: @dragonflylee + m_Data[6] = 0x16; // Must be 0x60 for Russian version. Discoverer: @dragonflylee + break; + case NavicatLanguage::Portuguese: + m_Data[5] = 0xCD; // Must be 0xCD for Portuguese version. Discoverer: @dragonflylee + m_Data[6] = 0x49; // Must be 0x49 for Portuguese version. Discoverer: @dragonflylee + break; + default: + break; + } + } + + void SerialNumberGenerator::SetLanguageSignature(uint8_t LanguageSignature0, uint8_t LanguageSignature1) noexcept { + m_Data[5] = LanguageSignature0; + m_Data[6] = LanguageSignature1; + } + + void SerialNumberGenerator::SetProductSignature(NavicatProductType ProductType) noexcept { + switch (ProductType) { + case NavicatProductType::DataModeler: + m_Data[7] = 0x84; + break; + case NavicatProductType::Premium: + m_Data[7] = 0x65; + break; + case NavicatProductType::MySQL: + m_Data[7] = 0x68; + break; + case NavicatProductType::PostgreSQL: + m_Data[7] = 0x6C; + break; + case NavicatProductType::Oracle: + m_Data[7] = 0x70; + break; + case NavicatProductType::SQLServer: + m_Data[7] = 0x74; + break; + case NavicatProductType::SQLite: + m_Data[7] = 0x78; + break; + case NavicatProductType::MariaDB: + m_Data[7] = 0x7C; + break; + case NavicatProductType::MongoDB: + m_Data[7] = 0x80; + break; + case NavicatProductType::ReportViewer: + m_Data[7] = 0x0b; + default: + break; + } + } + + void SerialNumberGenerator::SetProductSignature(uint8_t ProductSignature) noexcept { + m_Data[7] = ProductSignature; + } + + void SerialNumberGenerator::SetVersion(uint8_t Version) { + if (Version < 0x10) { + m_Data[8] = static_cast(Version << 4); + } else { + throw ARL::Exception(__FILE__, __LINE__, "Invalid version for Navicat."); + } + } + + void SerialNumberGenerator::Generate() { + static auto translator = [](char c) { +#ifdef __APPLE__ + //static const char StandardBase32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + //static const char NavicatBase32[] = "ABCDEFGH8JKLMN9PQRSTUVWXYZ234567"; + switch (c) { + case 'I': + return '8'; + case 'O': + return '9'; + default: + return c; + } +#else + //static const char StandardBase32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + //static const char NavicatBase32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + return c; +#endif + }; + + RAND_bytes(m_Data + 2, 3); + + const_DES_cblock key = { 0x64, 0xAD, 0xF3, 0x2F, 0xAE, 0xF2, 0x1A, 0x27 }; + DES_key_schedule schedule; + DES_set_key_unchecked(&key, &schedule); + DES_ecb_encrypt( + reinterpret_cast(m_Data + 2), + reinterpret_cast(m_Data + 2), + &schedule, + DES_ENCRYPT + ); + + + m_SerialNumberShort = base32_encode(m_Data, sizeof(m_Data)); + std::transform(m_SerialNumberShort.begin(), m_SerialNumberShort.end(), m_SerialNumberShort.begin(), translator); + + m_SerialNumberLong.resize(20); + snprintf(m_SerialNumberLong.data(), m_SerialNumberLong.length(), + "%.4s-%.4s-%.4s-%.4s", + m_SerialNumberShort.c_str() + 0, + m_SerialNumberShort.c_str() + 4, + m_SerialNumberShort.c_str() + 8, + m_SerialNumberShort.c_str() + 12 + ); + while (m_SerialNumberLong.back() == '\x00') { + m_SerialNumberLong.pop_back(); + } + } + + [[nodiscard]] + const std::string& SerialNumberGenerator::GetSerialNumberShort() const noexcept { + return m_SerialNumberShort; + } + + [[nodiscard]] + const std::string& SerialNumberGenerator::GetSerialNumberLong() const noexcept { + return m_SerialNumberLong; + } + + void SerialNumberGenerator::ShowInConsole() const { + std::cout << "[*] Serial number:" << std::endl; + std::cout << m_SerialNumberLong << std::endl; + std::cout << std::endl; + } +} + diff --git a/navicat-keygen/SerialNumberGenerator.hpp b/navicat-keygen/SerialNumberGenerator.hpp new file mode 100644 index 0000000..75f9554 --- /dev/null +++ b/navicat-keygen/SerialNumberGenerator.hpp @@ -0,0 +1,65 @@ +#pragma once +#include +#include + +namespace nkg { + + enum class NavicatLanguage { + English, + SimplifiedChinese, + TraditionalChinese, + Japanese, + Polish, + Spanish, + French, + German, + Korean, + Russian, + Portuguese + }; + + enum class NavicatProductType { + DataModeler, + Premium, + MySQL, + PostgreSQL, + Oracle, + SQLServer, + SQLite, + MariaDB, + MongoDB, + ReportViewer + }; + + class SerialNumberGenerator { + private: + + uint8_t m_Data[10]; + std::string m_SerialNumberShort; + std::string m_SerialNumberLong; + + public: + + SerialNumberGenerator() noexcept; + + void SetLanguageSignature(NavicatLanguage Language) noexcept; + void SetLanguageSignature(uint8_t LanguageSignature0, uint8_t LanguageSignature1) noexcept; + + void SetProductSignature(NavicatProductType ProductType) noexcept; + void SetProductSignature(uint8_t ProductSignature) noexcept; + + void SetVersion(uint8_t Version); + + void Generate(); + + [[nodiscard]] + const std::string& GetSerialNumberShort() const noexcept; + + [[nodiscard]] + const std::string& GetSerialNumberLong() const noexcept; + + void ShowInConsole() const; + }; + +} + diff --git a/navicat-keygen/main.cpp b/navicat-keygen/main.cpp index 4ad0784..63d3d81 100644 --- a/navicat-keygen/main.cpp +++ b/navicat-keygen/main.cpp @@ -1,251 +1,96 @@ -#include // NOLINT -#include // NOLINT -#include -#include -#include +#include +#include +#include "Exception.hpp" +#include "ExceptionGeneric.hpp" +#include "RSACipher.hpp" +#include "SerialNumberGenerator.hpp" -#include "../common/RSACipher.hpp" -#include "NavicatKeygen.hpp" +namespace nkg { + using fnCollectInformation = SerialNumberGenerator(); -#include -#include -#include + SerialNumberGenerator CollectInformationNormal(); + SerialNumberGenerator CollectInformationAdvanced(); + void GenerateLicenseText(const RSACipher& Cipher, const SerialNumberGenerator& Generator); +} -template -bool read_int(int& num, const char* prompt, const char* err_msg) { - int temp; - std::string input; +static void Welcome() { + puts("**********************************************************"); + puts("* Navicat Keygen (macOS) by @DoubleLabyrinth *"); + puts("* Version: 5.0 *"); + puts("**********************************************************"); + puts(""); +} - while (true) { - std::cout << prompt; - if (!std::getline(std::cin, input)) - return false; +static void Help() { + puts("Usage:"); + puts(" navicat-keygen [--adv] "); + puts(""); + puts(" [--adv] Enable advance mode."); + puts(" This parameter is optional."); + puts(""); + puts(" A path to an RSA-2048 private key file."); + puts(" This parameter must be specified."); + puts(""); + puts("Example:"); + puts(" ./navicat-keygen ./RegPrivateKey.pem"); +} - try { - temp = std::stoi(input, nullptr, 0); - if (min_num <= temp && temp <= max_num) { - num = temp; - return true; +int main(int argc, const char* argv[]) { + Welcome(); + + if (argc == 2 || argc == 3) { + nkg::fnCollectInformation* lpfnCollectInformation = nullptr; + + if (argc == 3) { + if (strcasecmp(argv[1], "--adv") == 0) { + lpfnCollectInformation = nkg::CollectInformationAdvanced; } else { - throw std::invalid_argument(err_msg); + Help(); + return -1; } - } catch (...) { - std::cout << err_msg << std::endl; + } else { + lpfnCollectInformation = nkg::CollectInformationNormal; } - } -} -std::string base64_encode(const std::vector& bindata); -std::vector base64_decode(const std::string& ascdata); - -void Help() { - std::cout << "***************************************************" << std::endl; - std::cout << "* Navicat Keygen by @DoubleLabyrinth *" << std::endl; - std::cout << "* Version: 4.0 *" << std::endl; - std::cout << "***************************************************" << std::endl; - std::cout << std::endl; - std::cout << "Usage:" << std::endl; - std::cout << " navicat-keygen " << std::endl; - std::cout << std::endl; - std::cout << " Path to a PEM-format RSA-2048 private key file." << std::endl; - std::cout << " This parameter must be specified." << std::endl; - std::cout << std::endl; -} - -int main(int argc, char* argv[]) { - if (argc != 2) { - Help(); - return -1; - } else { try { - std::cout << "***************************************************" << std::endl; - std::cout << "* Navicat Keygen by @DoubleLabyrinth *" << std::endl; - std::cout << "* Version: 4.0 *" << std::endl; - std::cout << "***************************************************" << std::endl; - std::cout << std::endl; + nkg::RSACipher Cipher; - RSACipher RsaCipher; - - int select_num; - - NavicatKeygen keygen; - std::string username; - std::string organization; - - std::string RequestCode_b64; - std::vector RequestCode; - char RequestInfo[256] = {}; - char ResponseInfo[256] = {}; - std::vector ResponseCode; - std::string ResponseCode_b64; - - rapidjson::Document json; - rapidjson::Value N_Key; - rapidjson::Value N_Value; - rapidjson::Value O_Key; - rapidjson::Value O_Value; - rapidjson::Value T_Key; - rapidjson::Value T_Value; - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - - RsaCipher.ImportKeyFromFile(argv[1]); - if (RsaCipher.Bits() != 2048) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "Not RSA-2048 private key file."); + Cipher.ImportKeyFromFile(argv[argc - 1]); + if (Cipher.Bits() != 2048) { + throw ARL::Exception(__FILE__, __LINE__, "RSA key length mismatches.") + .PushHint("You must provide an RSA key whose modulus length is 2048 bits."); } - keygen.SetProductSignature(NavicatKeygen::Product::Premium); + auto Generator = lpfnCollectInformation(); - std::cout - << "Which is your Navicat Premium language?" << std::endl - << "0. English" << std::endl - << "1. Simplified Chinese" << std::endl - << "2. Traditional Chinese" << std::endl - << "3. Japanese" << std::endl - << "4. Polish" << std::endl - << "5. Spanish" << std::endl - << "6. French" << std::endl - << "7. German" << std::endl - << "8. Korean" << std::endl - << "9. Russian" << std::endl - << "10. Portuguese" << std::endl - << std::endl; - if (read_int<0, 10>(select_num, "(Input index)> ", "Invalid index.")) { - keygen.SetLanguageSignature(static_cast(select_num)); - } else { - return -1; - } + Generator.Generate(); + Generator.ShowInConsole(); - std::cout << std::endl; - - if (read_int<0, 15>(select_num, "(Input major version number, range: 0 ~ 15, default: 12)> ", "Invalid number.")) { - keygen.SetVersion(static_cast(select_num)); - } else { - return -1; - } - - // - // Generate snKey - // - keygen.Generate(); - std::cout << std::endl; - std::cout << "Serial number:" << std::endl; - std::cout << keygen.GetFormatedKey() << std::endl; - std::cout << std::endl; - - // - // Get user name - // - std::cout << "Your name: "; - if (!std::getline(std::cin, username)) { - return -1; - } - - // - // Get organization name - // - std::cout << "Your organization: "; - if (!std::getline(std::cin, organization)) { - return -1; - } - - std::cout << std::endl; - - // - // Get request code in base64 - // - std::cout << "Input request code (in Base64), input empty line to end:" << std::endl; - while (true) { - std::string temp; - if (!std::getline(std::cin, temp)) - return 0; - - if (temp.empty()) - break; - - RequestCode_b64 += temp; - } - - // - // Get request code in raw bytes - // - RequestCode = base64_decode(RequestCode_b64); - if (RequestCode.size() > ((RsaCipher.Bits() + 7) / 8)) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "Request code is too long."); - } - - // - // Decrypt to get request info - // - RsaCipher.Decrypt(RequestCode.data(), static_cast(RequestCode.size()), RequestInfo, RSA_PKCS1_PADDING); - - // - // print out request info - // - std::cout << "Request Info:" << std::endl; - std::cout << RequestInfo << std::endl; - std::cout << std::endl; - - // - // Generate response info - // - json.Parse(RequestInfo); - json.RemoveMember("P"); // remove Platform info - - N_Key.SetString("N", 1); - N_Value.SetString(username.c_str(), static_cast(username.length())); - O_Key.SetString("O", 1); - O_Value.SetString(organization.c_str(), static_cast(organization.length())); - T_Key.SetString("T", 1); - T_Value.SetUint(static_cast(std::time(nullptr))); - - json.AddMember(N_Key, N_Value, json.GetAllocator()); - json.AddMember(O_Key, O_Value, json.GetAllocator()); - json.AddMember(T_Key, T_Value, json.GetAllocator()); - - json.Accept(writer); - - if (buffer.GetSize() > 240) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "Response info is too long."); - } - - // - // print out response info - // - memcpy(ResponseInfo, buffer.GetString(), buffer.GetSize()); - std::cout << "Response Info:" << std::endl; - std::cout << ResponseInfo << std::endl; - std::cout << std::endl; - - // - // encrypt response info - // - ResponseCode.resize((RsaCipher.Bits() + 7) / 8); - RsaCipher.Encrypt(ResponseInfo, static_cast(strlen(ResponseInfo)), ResponseCode.data(), RSA_PKCS1_PADDING); - - // - // encode encrypted response info in base64 format - // - ResponseCode_b64 = base64_encode(ResponseCode); - - // - // print out activation code - // - std::cout << "License:" << std::endl; - std::cout << ResponseCode_b64 << std::endl; + GenerateLicenseText(Cipher, Generator); return 0; - } catch (nkg::Exception& e) { - std::cout << "[-] " << e.File() << ":" << e.Line() << " ->" << std::endl; - std::cout << " " << e.Message() << std::endl; + } catch (ARL::EOFError&) { + return ECANCELED; + } catch (ARL::Exception& e) { + printf("[-] %s:%zu ->\n", e.ExceptionFile(), e.ExceptionLine()); + printf(" %s\n", e.ExceptionMessage()); + if (e.HasErrorCode()) { - std::cout << " " << e.ErrorString() << std::endl; + printf(" %s (0x%zx)\n", e.ErrorString(), e.ErrorCode()); } + + for (const auto& Hint : e.Hints()) { + printf(" Hints: %s\n", Hint.c_str()); + } + + return -1; + } catch (std::exception& e) { + printf("[-] %s\n", e.what()); return -1; } + } else { + Help(); + return -1; } } diff --git a/navicat-patcher/CapstoneDisassembler.cpp b/navicat-patcher/CapstoneDisassembler.cpp index b982b2e..37b8956 100644 --- a/navicat-patcher/CapstoneDisassembler.cpp +++ b/navicat-patcher/CapstoneDisassembler.cpp @@ -1,76 +1,95 @@ #include "CapstoneDisassembler.hpp" -CapstoneDisassembler CapstoneDisassembler::Create(cs_arch ArchType, cs_mode Mode) { - CapstoneDisassembler NewDisassembler; +namespace nkg { - auto err = cs_open(ArchType, Mode, NewDisassembler.pvt_Handle.GetAddress()); - if (err != CS_ERR_OK) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::CapstoneError(__FILE__, __LINE__, err, "cs_open failed."); + CapstoneDisassembler::CapstoneDisassembler(const CapstoneEngine& Engine) : + ARL::ResourceWrapper(cs_malloc(Engine)), + m_Engine(Engine), + m_CurrentState{}, + m_NextState{}, + m_lpCurrentInsn(nullptr) + { + if (IsValid() == false) { + throw ARL::CapstoneError(__FILE__, __LINE__, cs_errno(Engine), "cs_malloc failed."); + } } - NewDisassembler.pvt_Insn.TakeOver(cs_malloc(NewDisassembler.pvt_Handle)); - if (NewDisassembler.pvt_Insn.IsValid() == false) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::CapstoneError(__FILE__, __LINE__, cs_errno(NewDisassembler.pvt_Handle), "cs_malloc failed."); + CapstoneDisassembler& CapstoneDisassembler::SetContext(const CapstoneContext& Ctx) noexcept { + m_lpCurrentInsn = nullptr; + + m_CurrentState.lpMachineCode = nullptr; + m_CurrentState.cbMachineCode = 0; + m_CurrentState.Address = 0; + + m_NextState = Ctx; + + return *this; } - return NewDisassembler; -} - -void CapstoneDisassembler::Option(cs_opt_type Type, size_t Value) { - auto err = cs_option(pvt_Handle, Type, Value); - if (err != CS_ERR_OK) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::CapstoneError(__FILE__, __LINE__, err, "cs_option failed."); + [[nodiscard]] + const CapstoneContext& CapstoneDisassembler::GetContext() const noexcept { + return m_NextState; } - pvt_CurrentInsn = nullptr; + [[nodiscard]] + bool CapstoneDisassembler::Next() noexcept { + bool bSucceed; + CapstoneContext backup = m_NextState; - pvt_Insn.TakeOver(cs_malloc(pvt_Handle)); - if (pvt_Insn.IsValid() == false) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::CapstoneError(__FILE__, __LINE__, cs_errno(pvt_Handle), "cs_malloc failed."); + bSucceed = cs_disasm_iter(m_Engine.Get(), reinterpret_cast(&m_NextState.lpMachineCode), &m_NextState.cbMachineCode, &m_NextState.Address, Get()); + if (bSucceed) { + if (m_lpCurrentInsn == nullptr) { + m_lpCurrentInsn = Get(); + } + + m_CurrentState = backup; + } else { + m_lpCurrentInsn = nullptr; + } + + return bSucceed; } -} -void CapstoneDisassembler::SetContext(uintptr_t lpOpcode, size_t cbOpcode, uint64_t Address) noexcept { - pvt_CurrentCtx.pbOpcode = reinterpret_cast(lpOpcode); - pvt_CurrentCtx.cbOpcode = cbOpcode; - pvt_CurrentCtx.Address = Address; - pvt_CurrentInsn = nullptr; -} - -void CapstoneDisassembler::SetContext(const void* lpOpcode, size_t cbOpcode, uint64_t Address) noexcept { - pvt_CurrentCtx.pbOpcode = reinterpret_cast(lpOpcode); - pvt_CurrentCtx.cbOpcode = cbOpcode; - pvt_CurrentCtx.Address = Address; - pvt_CurrentInsn = nullptr; -} - -const CapstoneDisassembler::Context& CapstoneDisassembler::GetContext() const noexcept { - return pvt_CurrentCtx; -} - -const cs_insn* CapstoneDisassembler::GetInstruction() const noexcept { - return pvt_CurrentInsn; -} - -CapstoneDisassembler::Context CapstoneDisassembler::GetInstructionContext() const noexcept { - Context CtxOfInsn = {}; - CtxOfInsn.pbOpcode = pvt_CurrentCtx.pbOpcode - pvt_CurrentInsn->size; - CtxOfInsn.cbOpcode = pvt_CurrentCtx.cbOpcode + pvt_CurrentInsn->size; - CtxOfInsn.Address = pvt_CurrentCtx.Address - pvt_CurrentInsn->size; - return CtxOfInsn; -} - -bool CapstoneDisassembler::Next() noexcept { - bool bSucceed = cs_disasm_iter(pvt_Handle, &pvt_CurrentCtx.pbOpcode, &pvt_CurrentCtx.cbOpcode, &pvt_CurrentCtx.Address, pvt_Insn); - if (bSucceed) { - if (pvt_CurrentInsn == nullptr) pvt_CurrentInsn = pvt_Insn.Get(); - } else { - pvt_CurrentInsn = nullptr; + [[nodiscard]] + const cs_insn* CapstoneDisassembler::GetInstruction() const noexcept { + return m_lpCurrentInsn; } - return bSucceed; + + [[nodiscard]] + const CapstoneContext& CapstoneDisassembler::GetInstructionContext() const noexcept { + return m_CurrentState; + } + + CapstoneEngine::CapstoneEngine(cs_arch ArchType, cs_mode Mode) { + auto err = cs_open(ArchType, Mode, GetAddressOf()); + if (err != CS_ERR_OK) { + throw ARL::CapstoneError(__FILE__, __LINE__, err, "cs_open failed."); + } + } + + void CapstoneEngine::Option(cs_opt_type Type, cs_opt_value Value) { + auto err = cs_option(Get(), Type, Value); + if (err != CS_ERR_OK) { + throw ARL::CapstoneError(__FILE__, __LINE__, err, "cs_option failed."); + } + } + + const char* CapstoneEngine::GetGroupName(unsigned int group_id) const noexcept { + return cs_group_name(Get(), group_id); + } + + const char* CapstoneEngine::GetInstructionName(unsigned int instruction_id) const noexcept { + return cs_insn_name(Get(), instruction_id); + } + + const char* CapstoneEngine::GetRegisterName(unsigned int register_id) const noexcept { + return cs_reg_name(Get(), register_id); + } + + [[nodiscard]] + CapstoneDisassembler CapstoneEngine::CreateDisassembler() const { + return CapstoneDisassembler(*this); + } + } diff --git a/navicat-patcher/CapstoneDisassembler.hpp b/navicat-patcher/CapstoneDisassembler.hpp index 7c0cd7b..eae67d5 100644 --- a/navicat-patcher/CapstoneDisassembler.hpp +++ b/navicat-patcher/CapstoneDisassembler.hpp @@ -1,49 +1,63 @@ #pragma once #include -#include "../common/ResourceOwned.hpp" +#include "ExceptionCapstone.hpp" +#include "ResourceWrapper.hpp" #include "ResourceTraitsCapstone.hpp" -class CapstoneDisassembler { -public: +namespace nkg { - struct Context { - const uint8_t* pbOpcode; - size_t cbOpcode; - uint64_t Address; + struct CapstoneContext { + const void* lpMachineCode; + size_t cbMachineCode; + uint64_t Address; }; -private: + class CapstoneEngine; - ResourceOwned pvt_Handle; - ResourceOwned pvt_Insn; - Context pvt_CurrentCtx; - cs_insn* pvt_CurrentInsn; + class CapstoneDisassembler : private ARL::ResourceWrapper { + friend class CapstoneEngine; + private: - CapstoneDisassembler() noexcept : - pvt_Handle(CapstoneHandleTraits{}), - pvt_Insn(CapstoneInsnTraits{}), - pvt_CurrentCtx{}, - pvt_CurrentInsn(nullptr) {} -public: + const CapstoneEngine& m_Engine; + CapstoneContext m_CurrentState; + CapstoneContext m_NextState; + cs_insn* m_lpCurrentInsn; - [[nodiscard]] - static CapstoneDisassembler Create(cs_arch ArchType, cs_mode Mode); + CapstoneDisassembler(const CapstoneEngine& Engine); - void Option(cs_opt_type Type, size_t Value); + public: - void SetContext(uintptr_t lpOpcode, size_t cbOpcode, uint64_t Address = 0) noexcept ; + CapstoneDisassembler& SetContext(const CapstoneContext& Ctx) noexcept; - void SetContext(const void* lpOpcode, size_t cbOpcode, uint64_t Address = 0) noexcept; + [[nodiscard]] + const CapstoneContext& GetContext() const noexcept; - [[nodiscard]] - const Context& GetContext() const noexcept; + [[nodiscard]] + bool Next() noexcept; - [[nodiscard]] - const cs_insn* GetInstruction() const noexcept; + [[nodiscard]] + const cs_insn* GetInstruction() const noexcept; - [[nodiscard]] - Context GetInstructionContext() const noexcept; + [[nodiscard]] + const CapstoneContext& GetInstructionContext() const noexcept; + }; - [[nodiscard]] - bool Next() noexcept; -}; + class CapstoneEngine : private ARL::ResourceWrapper { + friend class CapstoneDisassembler; + public: + + CapstoneEngine(cs_arch ArchType, cs_mode Mode); + + void Option(cs_opt_type Type, cs_opt_value Value); + + const char* GetGroupName(unsigned int group_id) const noexcept; + + const char* GetInstructionName(unsigned int instruction_id) const noexcept; + + const char* GetRegisterName(unsigned int register_id) const noexcept; + + [[nodiscard]] + CapstoneDisassembler CreateDisassembler() const; + }; + +} diff --git a/navicat-patcher/ExceptionCapstone.hpp b/navicat-patcher/ExceptionCapstone.hpp index 3400b8f..7c88d9d 100644 --- a/navicat-patcher/ExceptionCapstone.hpp +++ b/navicat-patcher/ExceptionCapstone.hpp @@ -1,19 +1,20 @@ #pragma once -#include "../common/Exception.hpp" #include +#include "Exception.hpp" -namespace nkg { +namespace ARL { class CapstoneError final : public Exception { private: - cs_err pvt_ErrorCode; + cs_err m_ErrorCode; public: - CapstoneError(const char* File, unsigned Line, cs_err ErrorCode, const char* Message) noexcept : - Exception(File, Line, Message), - pvt_ErrorCode(ErrorCode) {} + template + CapstoneError(const char* SourceFile, size_t SourceLine, cs_err ErrorCode, const char* Format, __ArgTypes&&... Args) noexcept : + Exception(SourceFile, SourceLine, Format, std::forward<__ArgTypes>(Args)...), + m_ErrorCode(ErrorCode) {} [[nodiscard]] // NOLINTNEXTLINE: mark "virtual" explicitly for more readability @@ -24,13 +25,13 @@ namespace nkg { [[nodiscard]] // NOLINTNEXTLINE: mark "virtual" explicitly for more readability virtual intptr_t ErrorCode() const noexcept override { - return pvt_ErrorCode; + return m_ErrorCode; } [[nodiscard]] // NOLINTNEXTLINE: mark "virtual" explicitly for more readability virtual const char* ErrorString() const noexcept override { - return cs_strerror(pvt_ErrorCode); + return cs_strerror(m_ErrorCode); } }; diff --git a/navicat-patcher/ExceptionKeystone.hpp b/navicat-patcher/ExceptionKeystone.hpp index 183871e..5d66655 100644 --- a/navicat-patcher/ExceptionKeystone.hpp +++ b/navicat-patcher/ExceptionKeystone.hpp @@ -1,19 +1,20 @@ #pragma once -#include "../common/Exception.hpp" #include +#include "Exception.hpp" -namespace nkg { +namespace ARL { class KeystoneError final : public Exception { private: - ks_err pvt_ErrorCode; + ks_err m_ErrorCode; public: - KeystoneError(const char* File, unsigned Line, ks_err ErrorCode, const char* Message) noexcept : - Exception(File, Line, Message), - pvt_ErrorCode(ErrorCode) {} + template + KeystoneError(const char* SourceFile, size_t SourceLine, ks_err ErrorCode, const char* Format, __ArgTypes&&... Args) noexcept : + Exception(SourceFile, SourceLine, Format, std::forward<__ArgTypes>(Args)...), + m_ErrorCode(ErrorCode) {} [[nodiscard]] // NOLINTNEXTLINE: mark "virtual" explicitly for more readability @@ -24,13 +25,13 @@ namespace nkg { [[nodiscard]] // NOLINTNEXTLINE: mark "virtual" explicitly for more readability virtual intptr_t ErrorCode() const noexcept override { - return pvt_ErrorCode; + return m_ErrorCode; } [[nodiscard]] // NOLINTNEXTLINE: mark "virtual" explicitly for more readability virtual const char* ErrorString() const noexcept override { - return ks_strerror(pvt_ErrorCode); + return ks_strerror(m_ErrorCode); } }; diff --git a/navicat-patcher/HelperIsResolvedTo.cpp b/navicat-patcher/HelperIsResolvedTo.cpp deleted file mode 100644 index f59e852..0000000 --- a/navicat-patcher/HelperIsResolvedTo.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include // NOLINT -#include "ExceptionCapstone.hpp" -#include "CapstoneDisassembler.hpp" -#include "X64ImageInterpreter.hpp" - -namespace nkg { - - bool IsResolvedTo(const X64ImageInterpreter& Image, const void* StubHelperProc, const char *Symbol) { - CapstoneDisassembler Disassembler = CapstoneDisassembler::Create(CS_ARCH_X86, CS_MODE_64); - - Disassembler.Option(CS_OPT_DETAIL, CS_OPT_ON); - - // A stub-helper proc must look like: - // push xxxxh; - // jmp loc_xxxxxxxx - // which should be 10 bytes long. - Disassembler.SetContext(StubHelperProc, 10); - - if (Disassembler.Next() == false) { - return false; - } - - auto insn = Disassembler.GetInstruction(); - if (strcasecmp(insn->mnemonic, "push") != 0 || insn->detail->x86.operands[0].type != X86_OP_IMM) { - return false; - } - - auto bind_opcode_offset = static_cast(insn->detail->x86.operands[0].imm); - if (Image.CommandOf() == nullptr) { - return false; - } - - auto bind_opcode_ptr = - Image.ImageOffset(Image.CommandOf()->lazy_bind_off) + - bind_opcode_offset; - - while ((*bind_opcode_ptr & BIND_OPCODE_MASK) != BIND_OPCODE_DONE) { - switch (*bind_opcode_ptr & BIND_OPCODE_MASK) { - case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: // 0x10 - case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: // 0x30 - case BIND_OPCODE_SET_TYPE_IMM: // 0x50 - case BIND_OPCODE_DO_BIND: // 0x90 - case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: // 0xB0 - ++bind_opcode_ptr; - break; - case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: // 0x20 - case BIND_OPCODE_SET_ADDEND_SLEB: // 0x60 - case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: // 0x70 - case BIND_OPCODE_ADD_ADDR_ULEB: // 0x80 - case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: // 0xA0 - while (*(++bind_opcode_ptr) & 0x80u); - ++bind_opcode_ptr; - break; - case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: // 0x40 - return strcmp(reinterpret_cast(bind_opcode_ptr + 1), Symbol) == 0; - case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: // 0xC0 - // - // This opcode is too rare to appear, - // It is okay to dismiss this opcode - // - return false; - default: - return false; - } - } - - return false; - } - -} diff --git a/navicat-patcher/HelperPrintMemory.cpp b/navicat-patcher/HelperPrintMemory.cpp deleted file mode 100644 index e56a618..0000000 --- a/navicat-patcher/HelperPrintMemory.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include // NOLINT -#include // NOLINT -#include // NOLINT -#include // NOLINT -#include // NOLINT - -static jmp_buf env; - -static void SIGSEGV_Handler(int sig) { - siglongjmp(env, 1); -} - -// -// read byte(s) at address `p` as __Type to `out` -// succeed if return true, otherwise return false -// -template -static inline bool ProbeForRead(const void* p, void* out) { - int r = sigsetjmp(env, 1); - if (r == 0) { - *reinterpret_cast<__Type*>(out) = *reinterpret_cast(p); - return true; - } else { - return false; - } -} - -// -// Print memory data in [from, to) at least -// If `base` is not nullptr, print address as offset. Otherwise, as absolute address. -// NOTICE: -// `base` must >= `from` -// -namespace nkg { - - void PrintMemory(const void *from, const void *to, const void *base) { - auto start = reinterpret_cast(from); - auto end = reinterpret_cast(to); - auto base_ptr = reinterpret_cast(base); - - if (start >= end) - return; - - while (reinterpret_cast(start) % 16) - start--; - - while (reinterpret_cast(end) % 16) - end++; - - void (*prev_handler)(int) = signal(SIGSEGV, SIGSEGV_Handler); - while (start < end) { - uint16_t value[16] = {}; - - if (base_ptr) { - uintptr_t d = start >= base ? start - base_ptr : base_ptr - start; - if (start >= base) { - printf("+0x%.*zx ", static_cast(2 * sizeof(void *)), d); - } else { - printf("-0x%.*zx ", static_cast(2 * sizeof(void *)), d); - } - } else { - printf("0x%.*zx ", static_cast(2 * sizeof(void *)), reinterpret_cast(start)); - } - - for (int i = 0; i < 16; ++i) { - if (start + i < from) { - printf(" "); - value[i] = 0xfffe; - } else if (ProbeForRead(start + i, value + i)) { - printf("%02x ", value[i]); - } else { - printf("?? "); - value[i] = 0xffff; - } - } - - printf(" "); - - for (int i = 0; i < 16; ++i) { // NOLINT - if (0x20 <= value[i] && value[i] < 0x7f) { - printf("%c", value[i]); - } else if (value[i] == 0xfffe) { - printf(" "); - } else { - printf("."); - } - } - - printf("\n"); - - start += 0x10; - } - signal(SIGSEGV, prev_handler); - } - -} diff --git a/navicat-patcher/KeystoneAssembler.cpp b/navicat-patcher/KeystoneAssembler.cpp index b56b40d..ec3061d 100644 --- a/navicat-patcher/KeystoneAssembler.cpp +++ b/navicat-patcher/KeystoneAssembler.cpp @@ -1,50 +1,40 @@ #include "KeystoneAssembler.hpp" -[[nodiscard]] -KeystoneAssembler KeystoneAssembler::Create(ks_arch ArchType, ks_mode Mode) { - KeystoneAssembler NewAssembler; +namespace nkg { - auto err = ks_open(ArchType, Mode, NewAssembler.pvt_Engine.GetAddress()); - if (err != KS_ERR_OK) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::KeystoneError(__FILE__, __LINE__, err, "ks_open failed."); + KeystoneAssembler::KeystoneAssembler(const KeystoneEngine& Engine) noexcept : + m_Engine(Engine) {} + + [[nodiscard]] + std::vector KeystoneAssembler::GenerateMachineCode(std::string_view AssemblyCode, uint64_t Address) const { + ARL::ResourceWrapper pbMachineCode(ARL::ResourceTraits::KeystoneMalloc{}); + size_t cbMachineCode = 0; + size_t InstructionsProcessed = 0; + + if (ks_asm(m_Engine, AssemblyCode.data(), Address, pbMachineCode.GetAddressOf(), &cbMachineCode, &InstructionsProcessed) != 0) { + throw ARL::KeystoneError(__FILE__, __LINE__, ks_errno(m_Engine), "ks_asm failed."); + } + + return std::vector(pbMachineCode.Get(), pbMachineCode.Get() + cbMachineCode); } - return NewAssembler; -} - -void KeystoneAssembler::Option(ks_opt_type Type, size_t Value) { - auto err = ks_option(pvt_Engine, Type, Value); - if (err != KS_ERR_OK) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::KeystoneError(__FILE__, __LINE__, err, "ks_open failed."); - } -} - -[[nodiscard]] -std::vector KeystoneAssembler::GenerateOpcode(const char *AssemblyCode, uint64_t Address) const { - ResourceOwned pbOpcode(KeystoneMallocTraits{}); - size_t cbOpCode = 0; - size_t InstructionsProcessed = 0; - - if (ks_asm(pvt_Engine, AssemblyCode, Address, pbOpcode.GetAddress(), &cbOpCode, &InstructionsProcessed) != 0) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::KeystoneError(__FILE__, __LINE__, ks_errno(pvt_Engine), "ks_asm failed."); + KeystoneEngine::KeystoneEngine(ks_arch ArchType, ks_mode Mode) { + auto err = ks_open(ArchType, Mode, GetAddressOf()); + if (err != KS_ERR_OK) { + throw ARL::KeystoneError(__FILE__, __LINE__, err, "ks_open failed."); + } } - return std::vector(pbOpcode.Get(), pbOpcode.Get() + cbOpCode); -} - -[[nodiscard]] -std::vector KeystoneAssembler::GenerateOpcode(const std::string& AssemblyCode, uint64_t Address) const { - ResourceOwned pbOpcode(KeystoneMallocTraits{}); - size_t cbOpCode = 0; - size_t InstructionsProcessed = 0; - - if (ks_asm(pvt_Engine, AssemblyCode.c_str(), Address, pbOpcode.GetAddress(), &cbOpCode, &InstructionsProcessed) != 0) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::KeystoneError(__FILE__, __LINE__, ks_errno(pvt_Engine), "ks_asm failed."); + void KeystoneEngine::Option(ks_opt_type Type, ks_opt_value Value) { + auto err = ks_option(Get(), Type, Value); + if (err != KS_ERR_OK) { + throw ARL::KeystoneError(__FILE__, __LINE__, err, "ks_option failed."); + } + } + + KeystoneAssembler KeystoneEngine::CreateAssembler() const { + return KeystoneAssembler(*this); } - return std::vector(pbOpcode.Get(), pbOpcode.Get() + cbOpCode); } + diff --git a/navicat-patcher/KeystoneAssembler.hpp b/navicat-patcher/KeystoneAssembler.hpp index 6b375ed..4f1886b 100644 --- a/navicat-patcher/KeystoneAssembler.hpp +++ b/navicat-patcher/KeystoneAssembler.hpp @@ -1,28 +1,40 @@ -#pragma once -#include -#include "../common/ResourceOwned.hpp" + #pragma once +#include "ExceptionKeystone.hpp" +#include "ResourceWrapper.hpp" #include "ResourceTraitsKeystone.hpp" #include #include -class KeystoneAssembler { -private: +namespace nkg { - ResourceOwned pvt_Engine; + class KeystoneEngine; - KeystoneAssembler() noexcept : - pvt_Engine(KeystoneHandleTraits{}) {} -public: + class KeystoneAssembler { + friend class KeystoneEngine; + private: - [[nodiscard]] - static KeystoneAssembler Create(ks_arch ArchType, ks_mode Mode); + const KeystoneEngine& m_Engine; - void Option(ks_opt_type Type, size_t Value); + KeystoneAssembler(const KeystoneEngine& Engine) noexcept; - [[nodiscard]] - std::vector GenerateOpcode(const char* AssemblyCode, uint64_t Address = 0) const; + public: - [[nodiscard]] - std::vector GenerateOpcode(const std::string& AssemblyCode, uint64_t Address = 0) const; -}; + [[nodiscard]] + std::vector GenerateMachineCode(std::string_view AssemblyCode, uint64_t Address = 0) const; + + }; + + class KeystoneEngine : private ARL::ResourceWrapper { + friend class KeystoneAssembler; + public: + + KeystoneEngine(ks_arch ArchType, ks_mode Mode); + + void Option(ks_opt_type Type, ks_opt_value Value); + + [[nodiscard]] + KeystoneAssembler CreateAssembler() const; + }; + +} diff --git a/navicat-patcher/MemoryAccess.hpp b/navicat-patcher/MemoryAccess.hpp new file mode 100644 index 0000000..fb7a555 --- /dev/null +++ b/navicat-patcher/MemoryAccess.hpp @@ -0,0 +1,151 @@ +#pragma once +#include +#include +#include + +namespace ARL { + + template + [[nodiscard]] + inline ptrdiff_t AddressDelta(__PtrType1 p1, __PtrType2 p2) noexcept { + static_assert(std::is_pointer_v<__PtrType1>); + static_assert(std::is_pointer_v<__PtrType2>); + return reinterpret_cast(p1) - reinterpret_cast(p2); + } + + template + [[nodiscard]] + inline __PtrType AddressOffset(__PtrType p, ptrdiff_t offset) noexcept { + static_assert(std::is_pointer_v<__PtrType>); + return reinterpret_cast<__PtrType>( + const_cast( + reinterpret_cast(p) + offset + ) + ); + } + + template + [[nodiscard]] + inline __ReturnType AddressOffsetWithCast(__PtrType p, ptrdiff_t offset) noexcept { + static_assert(std::is_pointer_v<__ReturnType>); + static_assert(std::is_pointer_v<__PtrType>); + return reinterpret_cast<__ReturnType>( + const_cast( + reinterpret_cast(p) + offset + ) + ); + } + + template + [[nodiscard]] + inline bool AddressIsInRange(__PtrType p, __BeginPtrType begin, __EndPtrType end) { + static_assert(std::is_pointer_v<__PtrType>); + static_assert(std::is_pointer_v<__BeginPtrType>); + static_assert(std::is_pointer_v<__EndPtrType>); + return + reinterpret_cast(begin) <= reinterpret_cast(p) && + reinterpret_cast(p) < reinterpret_cast(end); + } + + template + [[nodiscard]] + inline bool AddressIsInRange(__PtrType p, __BasePtrType base, size_t size) { + static_assert(std::is_pointer_v<__PtrType>); + static_assert(std::is_pointer_v<__BasePtrType>); + return AddressIsInRange(p, base, AddressOffset(base, size)); + } + + template + [[nodiscard]] + inline bool AddressIsInRangeEx(__PtrType1 p1, __PtrType2 p2, __BeginPtrType begin, __EndPtrType end) { + static_assert(std::is_pointer_v<__PtrType1>); + static_assert(std::is_pointer_v<__PtrType2>); + static_assert(std::is_pointer_v<__BeginPtrType>); + static_assert(std::is_pointer_v<__EndPtrType>); + return + reinterpret_cast(begin) <= reinterpret_cast(p1) && + reinterpret_cast(p1) <= reinterpret_cast(p2) && + reinterpret_cast(p2) <= reinterpret_cast(end); + } + + template + [[nodiscard]] + inline bool AddressIsInRangeEx(__PtrType p, size_t s, __BeginPtrType begin, __EndPtrType end) { + static_assert(std::is_pointer_v<__PtrType>); + static_assert(std::is_pointer_v<__BeginPtrType>); + static_assert(std::is_pointer_v<__EndPtrType>); + return AddressIsInRange(p, AddressDelta(p, s), begin, end); + } + + template + [[nodiscard]] + inline bool AddressIsInRangeEx(__PtrType1 p1, __PtrType2 p2, __BasePtrType base, size_t size) { + static_assert(std::is_pointer_v<__PtrType1>); + static_assert(std::is_pointer_v<__PtrType2>); + static_assert(std::is_pointer_v<__BasePtrType>); + return AddressIsInRangeEx(p1, p2, base, AddressOffset(base, size)); + } + + template + [[nodiscard]] + inline bool AddressIsInRangeEx(__PtrType p, size_t s, __BasePtrType base, size_t size) { + static_assert(std::is_pointer_v<__PtrType>); + static_assert(std::is_pointer_v<__BasePtrType>); + return AddressIsInRangeEx(p, AddressOffset(p, s), base, AddressOffset(base, size)); + } + + template + [[nodiscard]] + inline __ReadType AddressRead(__PtrType p) noexcept { + static_assert(std::is_trivial_v<__ReadType> && std::is_standard_layout_v<__ReadType>); + static_assert(std::is_pointer_v<__PtrType>); + return *reinterpret_cast(p); + } + + template + [[nodiscard]] + inline __ReadType AddressRead(__PtrType p, ptrdiff_t offset) noexcept { + static_assert(std::is_trivial_v<__ReadType> && std::is_standard_layout_v<__ReadType>); + static_assert(std::is_pointer_v<__PtrType>); + return *reinterpret_cast( + reinterpret_cast(p) + offset + ); + } + + template + [[nodiscard]] + inline __ReadType AddressRead(__PtrType p, size_t scale, ptrdiff_t index) noexcept { + static_assert(std::is_trivial_v<__ReadType> && std::is_standard_layout_v<__ReadType>); + static_assert(std::is_pointer_v<__PtrType>); + return *reinterpret_cast( + reinterpret_cast(p) + scale * index + ); + } + + template + inline void AddressWrite(__PtrType p, const __WriteType& value) noexcept { + static_assert(std::is_trivial_v<__WriteType> && std::is_standard_layout_v<__WriteType>); + static_assert(std::is_pointer_v<__PtrType>); + *reinterpret_cast(p) = value; + } + + template + inline void AddressWrite(__PtrType p, ptrdiff_t offset, const __WriteType& value) noexcept { + static_assert(std::is_trivial_v<__WriteType> && std::is_standard_layout_v<__WriteType>); + static_assert(std::is_pointer_v<__PtrType>); + *reinterpret_cast( + reinterpret_cast(p) + offset + ) = value; + } + + template + inline void AddressWrite(__PtrType p, size_t scale, ptrdiff_t index, const __WriteType& value) noexcept { + static_assert(std::is_trivial_v<__WriteType> && std::is_standard_layout_v<__WriteType>); + static_assert(std::is_pointer_v<__PtrType>); + *reinterpret_cast( + reinterpret_cast(p) + scale * index + ) = value; + } + +} + diff --git a/navicat-patcher/Misc.cpp b/navicat-patcher/Misc.cpp new file mode 100644 index 0000000..d84b35a --- /dev/null +++ b/navicat-patcher/Misc.cpp @@ -0,0 +1,159 @@ +#include "Misc.hpp" +#include +#include +#include +#include +#include +#include "ExceptionSystem.hpp" + +static jmp_buf g_jmbuf; + +static void SIGSEGV_handler(int sig) { + siglongjmp(g_jmbuf, 1); +} + +// +// read byte(s) at address `p` as __Type to `out` +// succeed if return true, otherwise return false +// +template +static inline bool probe_for_read(const void* p, void* out) { + int r = sigsetjmp(g_jmbuf, 1); + if (r == 0) { + *reinterpret_cast<__Type*>(out) = *reinterpret_cast(p); + return true; + } else { + return false; + } +} + +namespace nkg::Misc { + + // + // Print memory data in [lpMemBegin, lpMemEnd) + // If `base` is not nullptr, print address as offset. Otherwise, as absolute address. + // NOTICE: + // `base` must >= `from` + // + void PrintMemory(const void* lpMemBegin, const void* lpMemEnd, const void* lpBase) noexcept { + auto pbBegin = reinterpret_cast(lpMemBegin); + auto pbEnd = reinterpret_cast(lpMemEnd); + auto pbBase = reinterpret_cast(lpBase); + + if (pbBegin >= pbEnd) + return; + + while (reinterpret_cast(pbBegin) % 16) + pbBegin--; + + while (reinterpret_cast(pbEnd) % 16) + pbEnd++; + + while (pbBegin < pbEnd) { + uint16_t Values[16] = {}; + + if (pbBase) { + uintptr_t d = pbBegin >= lpBase ? pbBegin - pbBase : pbBase - pbBegin; + if (pbBegin >= lpBase) { + printf("+0x%.*zx ", static_cast(2 * sizeof(void*)), d); + } else { + printf("-0x%.*zx ", static_cast(2 * sizeof(void*)), d); + } + } else { + printf("0x%.*zx ", static_cast(2 * sizeof(void*)), reinterpret_cast(pbBegin)); + } + + for (int i = 0; i < 16; ++i) { + if (pbBegin + i < lpMemBegin || pbBegin + i >= lpMemEnd) { + printf(" "); + Values[i] = 0xfffe; + } else if (probe_for_read(pbBegin + i, Values + i)) { + printf("%02x ", Values[i]); + } else { + printf("?? "); + Values[i] = 0xffff; + } + } + + printf(" "); + + for (int i = 0; i < 16; ++i) { + if (0x20 <= Values[i] && Values[i] < 0x7f) { + printf("%c", Values[i]); + } else if (Values[i] == 0xfffe) { + printf(" "); + } else { + printf("."); + } + } + + printf("\n"); + + pbBegin += 0x10; + } + } + + // + // Print memory data in [lpMem, lpMem + cbMem) + // If `base` is not nullptr, print address as offset. Otherwise, as absolute address. + // NOTICE: + // `base` must >= `from` + // + void PrintMemory(const void* lpMem, size_t cbMem, const void* lpBase) noexcept { + PrintMemory(lpMem, reinterpret_cast(lpMem) + cbMem, lpBase); + } + + [[nodiscard]] + bool FsIsExist(std::string_view szPath) { + std::error_code ec; + if (std::filesystem::exists(szPath, ec)) { + return true; + } else { + if (ec) { + throw ARL::SystemError(__FILE__, __LINE__, ec.value(), "std::filesystem::exists failed."); + } else { + return false; + } + } + } + + [[nodiscard]] + bool FsIsFile(std::string_view szPath) { + std::error_code ec; + if (std::filesystem::is_regular_file(szPath, ec)) { + return true; + } else { + if (ec) { + throw ARL::SystemError(__FILE__, __LINE__, ec.value(), "std::filesystem::is_regular_file failed."); + } else { + return false; + } + } + } + + [[nodiscard]] + bool FsIsDirectory(std::string_view szPath) { + std::error_code ec; + if (std::filesystem::is_directory(szPath, ec)) { + return true; + } else { + if (ec) { + throw ARL::SystemError(__FILE__, __LINE__, ec.value(), "std::filesystem::is_directory failed."); + } else { + return false; + } + } + } + + [[nodiscard]] + std::string FsCurrentWorkingDirectory() { + std::error_code ec; + std::string path = std::filesystem::current_path(ec); + if (ec) { + throw ARL::SystemError(__FILE__, __LINE__, ec.value(), "std::filesystem::current_path failed."); + } else { + return path; + } + } +} + diff --git a/navicat-patcher/Misc.hpp b/navicat-patcher/Misc.hpp new file mode 100644 index 0000000..feae497 --- /dev/null +++ b/navicat-patcher/Misc.hpp @@ -0,0 +1,31 @@ +#pragma once +#include +#include + +namespace nkg::Misc { + + // + // Print memory data in [lpMemBegin, lpMemEnd) + // If `base` is not nullptr, print address as offset. Otherwise, as absolute address. + // + void PrintMemory(const void* lpMemBegin, const void* lpMemEnd, const void* lpBase) noexcept; + + // + // Print memory data in [lpMem, lpMem + cbMem) + // If `base` is not nullptr, print address as offset. Otherwise, as absolute address. + // + void PrintMemory(const void* lpMem, size_t cbMem, const void* lpBase) noexcept; + + [[nodiscard]] + bool FsIsExist(std::string_view szPath); + + [[nodiscard]] + bool FsIsFile(std::string_view szPath); + + [[nodiscard]] + bool FsIsDirectory(std::string_view szPath); + + [[nodiscard]] + std::string FsCurrentWorkingDirectory(); +} + diff --git a/navicat-patcher/PatchSolution0.cpp b/navicat-patcher/PatchSolution0.cpp index b01d092..db50a2b 100644 --- a/navicat-patcher/PatchSolution0.cpp +++ b/navicat-patcher/PatchSolution0.cpp @@ -1,78 +1,88 @@ #include "PatchSolutions.hpp" +#include "Misc.hpp" #include -const char PatchSolution0::Keyword[452] = - "-----BEGIN PUBLIC KEY-----\x00" - "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I\x00" - "qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv\x00" - "a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF\x00" - "R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2\x00" - "WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt\x00" - "YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ\x00" - "awIDAQAB\x00" - "-----END PUBLIC KEY-----\x00"; +namespace nkg { -PatchSolution0::PatchSolution0(const X64ImageInterpreter& Image) noexcept : - pvt_Image(Image), - pvt_PatchOffset(X64ImageInterpreter::InvalidOffset) {} + const char PatchSolution0::Keyword[451] = + "-----BEGIN PUBLIC KEY-----\x00" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I\x00" + "qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv\x00" + "a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF\x00" + "R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2\x00" + "WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt\x00" + "YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ\x00" + "awIDAQAB\x00" + "-----END PUBLIC KEY-----"; -bool PatchSolution0::FindPatchOffset() noexcept { - try { - pvt_PatchOffset = pvt_Image.SearchSectionOffset("__TEXT", "__cstring", [](const uint8_t* p) { - return memcmp(p, Keyword, sizeof(Keyword) - 1) == 0; - }); + PatchSolution0::PatchSolution0(const X64ImageInterpreter& Image) : + m_Image(Image) {} - printf("[+] PatchSolution0 ...... Ready to apply.\n"); - printf(" Keyword offset = +0x%.8x\n", pvt_PatchOffset); - return true; - } catch (...) { - printf("[-] PatchSolution0 ...... Omitted.\n"); - return false; - } -} + [[nodiscard]] + bool PatchSolution0::FindPatchOffset() noexcept { + try { + auto lpPatch = m_Image.SearchSection("__TEXT", "__cstring", [](const void* base, size_t i, size_t size) { + if (i + sizeof(Keyword) <= size) { + auto p = ARL::AddressOffset(base, i); + return memcmp(p, Keyword, sizeof(Keyword) - 1) == 0; + } else { + return false; + } + }); + if (lpPatch) { + m_PatchOffset = m_Image.ConvertPtrToOffset(lpPatch); + } else { + throw ARL::Exception(__FILE__, __LINE__, "not found."); + } -bool PatchSolution0::CheckKey(const RSACipher& RsaCipher) const noexcept { - try { - if (RsaCipher.Bits() != 2048) { + printf("[+] PatchSolution0 ...... Ready to apply.\n"); + printf(" Keyword offset = +0x%.8x\n", m_PatchOffset.value()); + return true; + } catch (...) { + printf("[-] PatchSolution0 ...... Omitted.\n"); return false; } + } - std::string PublicKeyPEM = RsaCipher.ExportKeyString(); - for (auto& c : PublicKeyPEM) { - if (c == '\n') c = '\x00'; + [[nodiscard]] + bool PatchSolution0::CheckKey(const RSACipher& Cipher) const noexcept { + try { + return Cipher.Bits() == 2048; + } catch (...) { + return false; } - - return PublicKeyPEM.length() == sizeof(Keyword) - 1; - } catch (...) { - return false; } + + void PatchSolution0::MakePatch(const RSACipher& Cipher) const { + if (m_PatchOffset.has_value()) { + std::string szPublicKeyPEM = Cipher.ExportKeyString(); + for (auto& c : szPublicKeyPEM) { + if (c == '\n') { + c = '\x00'; + } + } + + while (szPublicKeyPEM.length() < sizeof(Keyword)) { + szPublicKeyPEM.push_back('\x00'); + } + + auto lpPatch = m_Image.ImageOffset(m_PatchOffset.value()); + + puts("**************************************************************"); + puts("* PatchSolution0 *"); + puts("**************************************************************"); + + printf("[*] Previous:\n"); + Misc::PrintMemory(lpPatch, sizeof(Keyword), m_Image.ImageBase()); + memcpy(lpPatch, szPublicKeyPEM.data(), sizeof(Keyword)); + printf("[*] After:\n"); + Misc::PrintMemory(lpPatch, sizeof(Keyword), m_Image.ImageBase()); + printf("\n"); + + } else { + throw ARL::Exception(__FILE__, __LINE__, "PatchSolution0: not ready yet."); + } + } + } -void PatchSolution0::MakePatch(const RSACipher& RsaCipher) const { - if (pvt_PatchOffset == X64ImageInterpreter::InvalidOffset) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "PatchSolution0 is not ready."); - } - - std::string PublicKeyPEM = RsaCipher.ExportKeyString(); - for (auto& c : PublicKeyPEM) { - if (c == '\n') c = '\x00'; - } - - auto pbPatch = pvt_Image.ImageOffset(pvt_PatchOffset); - - puts("**************************************************************"); - puts("* PatchSolution0 *"); - puts("**************************************************************"); - printf("@+0x%.8x\n", pvt_PatchOffset); - - puts("Previous:"); - nkg::PrintMemory(pbPatch, pbPatch + sizeof(Keyword), pbPatch); - - memcpy(pbPatch, PublicKeyPEM.data(), PublicKeyPEM.size()); - - puts("After:"); - nkg::PrintMemory(pbPatch, pbPatch + sizeof(Keyword), pbPatch); - - puts(""); -} diff --git a/navicat-patcher/PatchSolution1.cpp b/navicat-patcher/PatchSolution1.cpp index f515812..8acc6f3 100644 --- a/navicat-patcher/PatchSolution1.cpp +++ b/navicat-patcher/PatchSolution1.cpp @@ -1,127 +1,129 @@ #include "PatchSolutions.hpp" +#include "Misc.hpp" #include -const uint8_t PatchSolution1::Keyword[0x188] = { - 0xfe, 0xfd, 0xfc, 0xf4, 0xfe, 0xd2, 0xf8, 0xf4, 0xf1, 0xd3, 0xde, 0xc7, 0xdf, 0xd3, 0xd0, 0xfd, - 0x8a, 0xc3, 0x85, 0xf4, 0xf6, 0xe9, 0xfc, 0xfc, 0xf2, 0xf5, 0xfa, 0xf5, 0xf6, 0xe9, 0x81, 0xfb, - 0xfe, 0xfd, 0xfc, 0xf4, 0xf4, 0xdf, 0xf2, 0xf9, 0xf2, 0xe5, 0xf0, 0xf7, 0xc0, 0x89, 0xdd, 0xcb, - 0xf5, 0x87, 0xe6, 0xdd, 0xf4, 0xd9, 0xf8, 0xfb, 0xde, 0xf9, 0xcf, 0xc5, 0x8f, 0x80, 0x80, 0xf3, - 0xc2, 0xd0, 0xe2, 0x8f, 0xfa, 0x8a, 0xdd, 0xf3, 0xd7, 0xdc, 0x86, 0xdc, 0xf0, 0x81, 0xc0, 0xea, - 0xd0, 0xd9, 0xf9, 0xd8, 0xda, 0xf2, 0xd0, 0xfd, 0xc3, 0xf6, 0xf3, 0x82, 0xf2, 0x81, 0xef, 0xf2, - 0xe0, 0xf9, 0xf2, 0xd3, 0x8f, 0xd7, 0xe9, 0xfb, 0xca, 0x86, 0xde, 0xfc, 0xf3, 0xd5, 0xdd, 0xf4, - 0xc7, 0x80, 0xf7, 0xd5, 0xf2, 0xc1, 0xde, 0xcc, 0xc0, 0xc7, 0xf0, 0xd0, 0xd0, 0xd1, 0xd7, 0xcc, - 0xd2, 0x81, 0xc1, 0x83, 0xdd, 0xd5, 0x8a, 0x8f, 0x81, 0xe1, 0xf4, 0xd9, 0xf3, 0xd7, 0xca, 0xef, - 0xf9, 0xdf, 0xe1, 0xee, 0xf0, 0xe9, 0xd1, 0xca, 0xf2, 0xe3, 0xf8, 0xf0, 0x83, 0xde, 0xfb, 0xd7, - 0xf1, 0xc4, 0xfa, 0x85, 0xf2, 0xdd, 0xdd, 0xfd, 0x85, 0x86, 0xc7, 0xf9, 0xc4, 0xc9, 0xf4, 0xf8, - 0xd4, 0xd9, 0xe6, 0xd2, 0xf6, 0xc1, 0xc1, 0xf9, 0xe0, 0xe4, 0xf7, 0xe4, 0xfd, 0xf1, 0xf6, 0xfc, - 0xe1, 0x84, 0xe4, 0xd1, 0xed, 0xfe, 0xdb, 0xe8, 0xdd, 0xe1, 0x85, 0xd0, 0xc5, 0xd2, 0x8a, 0x8e, - 0xd5, 0xdd, 0xe3, 0xdb, 0xd0, 0xe1, 0xd0, 0xf6, 0xc6, 0xee, 0xe6, 0xf7, 0xda, 0xf1, 0xdb, 0xc9, - 0x8b, 0xee, 0xcd, 0xdf, 0xff, 0xe8, 0xdd, 0xca, 0x82, 0xdb, 0xf1, 0x82, 0xc3, 0xed, 0xc9, 0xcc, - 0xc0, 0xf2, 0xd6, 0xdf, 0x83, 0xe9, 0xf3, 0xce, 0xea, 0xfa, 0xdf, 0xf8, 0xd9, 0xff, 0xec, 0x88, - 0xe4, 0xe4, 0xfd, 0x80, 0xc5, 0xce, 0xfa, 0xd2, 0xf4, 0xd8, 0x84, 0xff, 0xe5, 0xf3, 0xcb, 0xc2, - 0xfe, 0xc0, 0xc4, 0xfa, 0xde, 0xdd, 0xd5, 0xc9, 0xc5, 0xd5, 0xdf, 0xe3, 0xdd, 0xc1, 0xcb, 0xdd, - 0xfc, 0xf7, 0x83, 0xf8, 0xda, 0xc1, 0xd4, 0xe3, 0xfe, 0xc2, 0xef, 0xf8, 0xf2, 0xea, 0x8a, 0xd2, - 0xc7, 0xf2, 0xf0, 0xc2, 0xfb, 0x89, 0xdc, 0xeb, 0xd1, 0xf7, 0xcc, 0xe2, 0xd1, 0xfc, 0xd4, 0xce, - 0xea, 0xcd, 0xe4, 0x87, 0xe0, 0xcc, 0x8d, 0xf5, 0xc7, 0x85, 0x87, 0xda, 0xcf, 0xde, 0x89, 0xcd, - 0xe5, 0xfd, 0xe7, 0x83, 0xda, 0xdb, 0xfe, 0xf4, 0x84, 0xec, 0xf6, 0xee, 0xfd, 0xea, 0xf1, 0xf5, - 0xf5, 0xfc, 0xe6, 0xd0, 0x86, 0xdf, 0xc3, 0xe2, 0xe4, 0xd5, 0xd7, 0xe4, 0xe4, 0xce, 0xd4, 0xce, - 0x82, 0xda, 0xc7, 0xda, 0x80, 0xcb, 0xee, 0x8c, 0xd0, 0xde, 0xcd, 0xda, 0xdd, 0xcd, 0xcc, 0xeb, - 0xd2, 0xc3, 0xfc, 0xf2, 0xf6, 0xe9, 0xf8, 0xf8 -}; +namespace nkg { -PatchSolution1::PatchSolution1(const X64ImageInterpreter& Image) noexcept : - pvt_Image(Image), - pvt_PatchOffset(X64ImageInterpreter::InvalidOffset) {} + const uint8_t PatchSolution1::Keyword[0x188] = { + 0xfe, 0xfd, 0xfc, 0xf4, 0xfe, 0xd2, 0xf8, 0xf4, 0xf1, 0xd3, 0xde, 0xc7, 0xdf, 0xd3, 0xd0, 0xfd, + 0x8a, 0xc3, 0x85, 0xf4, 0xf6, 0xe9, 0xfc, 0xfc, 0xf2, 0xf5, 0xfa, 0xf5, 0xf6, 0xe9, 0x81, 0xfb, + 0xfe, 0xfd, 0xfc, 0xf4, 0xf4, 0xdf, 0xf2, 0xf9, 0xf2, 0xe5, 0xf0, 0xf7, 0xc0, 0x89, 0xdd, 0xcb, + 0xf5, 0x87, 0xe6, 0xdd, 0xf4, 0xd9, 0xf8, 0xfb, 0xde, 0xf9, 0xcf, 0xc5, 0x8f, 0x80, 0x80, 0xf3, + 0xc2, 0xd0, 0xe2, 0x8f, 0xfa, 0x8a, 0xdd, 0xf3, 0xd7, 0xdc, 0x86, 0xdc, 0xf0, 0x81, 0xc0, 0xea, + 0xd0, 0xd9, 0xf9, 0xd8, 0xda, 0xf2, 0xd0, 0xfd, 0xc3, 0xf6, 0xf3, 0x82, 0xf2, 0x81, 0xef, 0xf2, + 0xe0, 0xf9, 0xf2, 0xd3, 0x8f, 0xd7, 0xe9, 0xfb, 0xca, 0x86, 0xde, 0xfc, 0xf3, 0xd5, 0xdd, 0xf4, + 0xc7, 0x80, 0xf7, 0xd5, 0xf2, 0xc1, 0xde, 0xcc, 0xc0, 0xc7, 0xf0, 0xd0, 0xd0, 0xd1, 0xd7, 0xcc, + 0xd2, 0x81, 0xc1, 0x83, 0xdd, 0xd5, 0x8a, 0x8f, 0x81, 0xe1, 0xf4, 0xd9, 0xf3, 0xd7, 0xca, 0xef, + 0xf9, 0xdf, 0xe1, 0xee, 0xf0, 0xe9, 0xd1, 0xca, 0xf2, 0xe3, 0xf8, 0xf0, 0x83, 0xde, 0xfb, 0xd7, + 0xf1, 0xc4, 0xfa, 0x85, 0xf2, 0xdd, 0xdd, 0xfd, 0x85, 0x86, 0xc7, 0xf9, 0xc4, 0xc9, 0xf4, 0xf8, + 0xd4, 0xd9, 0xe6, 0xd2, 0xf6, 0xc1, 0xc1, 0xf9, 0xe0, 0xe4, 0xf7, 0xe4, 0xfd, 0xf1, 0xf6, 0xfc, + 0xe1, 0x84, 0xe4, 0xd1, 0xed, 0xfe, 0xdb, 0xe8, 0xdd, 0xe1, 0x85, 0xd0, 0xc5, 0xd2, 0x8a, 0x8e, + 0xd5, 0xdd, 0xe3, 0xdb, 0xd0, 0xe1, 0xd0, 0xf6, 0xc6, 0xee, 0xe6, 0xf7, 0xda, 0xf1, 0xdb, 0xc9, + 0x8b, 0xee, 0xcd, 0xdf, 0xff, 0xe8, 0xdd, 0xca, 0x82, 0xdb, 0xf1, 0x82, 0xc3, 0xed, 0xc9, 0xcc, + 0xc0, 0xf2, 0xd6, 0xdf, 0x83, 0xe9, 0xf3, 0xce, 0xea, 0xfa, 0xdf, 0xf8, 0xd9, 0xff, 0xec, 0x88, + 0xe4, 0xe4, 0xfd, 0x80, 0xc5, 0xce, 0xfa, 0xd2, 0xf4, 0xd8, 0x84, 0xff, 0xe5, 0xf3, 0xcb, 0xc2, + 0xfe, 0xc0, 0xc4, 0xfa, 0xde, 0xdd, 0xd5, 0xc9, 0xc5, 0xd5, 0xdf, 0xe3, 0xdd, 0xc1, 0xcb, 0xdd, + 0xfc, 0xf7, 0x83, 0xf8, 0xda, 0xc1, 0xd4, 0xe3, 0xfe, 0xc2, 0xef, 0xf8, 0xf2, 0xea, 0x8a, 0xd2, + 0xc7, 0xf2, 0xf0, 0xc2, 0xfb, 0x89, 0xdc, 0xeb, 0xd1, 0xf7, 0xcc, 0xe2, 0xd1, 0xfc, 0xd4, 0xce, + 0xea, 0xcd, 0xe4, 0x87, 0xe0, 0xcc, 0x8d, 0xf5, 0xc7, 0x85, 0x87, 0xda, 0xcf, 0xde, 0x89, 0xcd, + 0xe5, 0xfd, 0xe7, 0x83, 0xda, 0xdb, 0xfe, 0xf4, 0x84, 0xec, 0xf6, 0xee, 0xfd, 0xea, 0xf1, 0xf5, + 0xf5, 0xfc, 0xe6, 0xd0, 0x86, 0xdf, 0xc3, 0xe2, 0xe4, 0xd5, 0xd7, 0xe4, 0xe4, 0xce, 0xd4, 0xce, + 0x82, 0xda, 0xc7, 0xda, 0x80, 0xcb, 0xee, 0x8c, 0xd0, 0xde, 0xcd, 0xda, 0xdd, 0xcd, 0xcc, 0xeb, + 0xd2, 0xc3, 0xfc, 0xf2, 0xf6, 0xe9, 0xf8, 0xf8 + }; -bool PatchSolution1::FindPatchOffset() noexcept { - uint8_t* pbPatch = nullptr; + PatchSolution1::PatchSolution1(const X64ImageInterpreter& Image) : + m_Image(Image) {} - for (size_t i = 0; i < pvt_Image.NumberOfSections(); ++i) { - auto pbSectionView = pvt_Image.SectionView(i); - auto cbSectiveView = pvt_Image.ImageSection(i)->size; + [[nodiscard]] + bool PatchSolution1::FindPatchOffset() noexcept { + try { + void* lpPatch = nullptr; - if (cbSectiveView < sizeof(Keyword)) { - continue; - } + for (size_t i = 0; i < m_Image.NumberOfSections(); ++i) { + auto section = m_Image.ImageSection(i); + if (section->size >= sizeof(Keyword)) { + lpPatch = m_Image.SearchSection(section, [](const void* base, size_t i, size_t size) { + if (i + sizeof(Keyword) <= size) { + return memcmp(ARL::AddressOffset(base, i), Keyword, sizeof(Keyword)) == 0; + } else { + return false; + } + }); - for (size_t j = 0; j < cbSectiveView - sizeof(Keyword); ++j) { - if (memcmp(pbSectionView + j, Keyword, sizeof(Keyword)) == 0) { - pbPatch = pbSectionView + j; - i = pvt_Image.NumberOfSections(); - break; + if (lpPatch) { + m_PatchOffset = m_Image.ConvertPtrToOffset(lpPatch); + break; + } + } } - } - } - if (pbPatch == nullptr) { - printf("[-] PatchSolution1 ...... Omitted.\n"); - return false; - } else { - pvt_PatchOffset = pbPatch - pvt_Image.ImageBase(); - - printf("[+] PatchSolution1 ...... Ready to apply.\n"); - printf(" Keyword offset = +0x%.8x\n", pvt_PatchOffset); - return true; - } -} - -bool PatchSolution1::CheckKey(const RSACipher& RsaCipher) const noexcept { - try { - std::string PublicKeyPEM = RsaCipher.ExportKeyString(); - - PublicKeyPEM.erase(PublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"), 26); - PublicKeyPEM.erase(PublicKeyPEM.find("-----END PUBLIC KEY-----"), 24); - { - std::string::size_type pos = 0; - while ((pos = PublicKeyPEM.find('\n', pos)) != std::string::npos) { - PublicKeyPEM.erase(pos, 1); + if (lpPatch) { + printf("[+] PatchSolution1 ...... Ready to apply.\n"); + printf(" Keyword offset = +0x%.8x\n", m_PatchOffset.value()); + return true; + } else { + throw ARL::Exception(__FILE__, __LINE__, "not found."); } - } - - return PublicKeyPEM.length() == sizeof(Keyword); - } catch (...) { - return false; - } -} - -void PatchSolution1::MakePatch(const RSACipher& RsaCipher) const { - if (pvt_PatchOffset == X64ImageInterpreter::InvalidOffset) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "PatchSolution1 is not ready."); - } - - auto pbPatch = pvt_Image.ImageOffset(pvt_PatchOffset); - std::string PublicKeyPEM = RsaCipher.ExportKeyString(); - - PublicKeyPEM.erase(PublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"), 26); - PublicKeyPEM.erase(PublicKeyPEM.find("-----END PUBLIC KEY-----"), 24); - { - std::string::size_type pos = 0; - while ((pos = PublicKeyPEM.find('\n', pos)) != std::string::npos) { - PublicKeyPEM.erase(pos, 1); + } catch (...) { + printf("[-] PatchSolution1 ...... Omitted.\n"); + return false; } } - uint8_t key = 8; - for (auto& c : PublicKeyPEM) { - if (key == 0) key = 8; - reinterpret_cast(c) ^= 0xbbu - key; - --key; + [[nodiscard]] + bool PatchSolution1::CheckKey(const RSACipher& Cipher) const noexcept { + try { + return Cipher.Bits() == 2048; + } catch (...) { + return false; + } } - puts("**************************************************************"); - puts("* PatchSolution1 *"); - puts("**************************************************************"); - printf("@+0x%.8x\n", pvt_PatchOffset); + void PatchSolution1::MakePatch(const RSACipher& Cipher) const { + if (m_PatchOffset.has_value()) { + auto szPublicKeyPEM = Cipher.ExportKeyString(); - puts("Previous:"); - nkg::PrintMemory(pbPatch, pbPatch + sizeof(Keyword), pbPatch); + for (auto pos = szPublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"); pos != std::string::npos; pos = szPublicKeyPEM.find("-----BEGIN PUBLIC KEY-----", pos)) { + szPublicKeyPEM.erase(pos, strlen("-----BEGIN PUBLIC KEY-----")); + } - memcpy(pbPatch, PublicKeyPEM.data(), PublicKeyPEM.length()); + for (auto pos = szPublicKeyPEM.find("-----END PUBLIC KEY-----"); pos != std::string::npos; pos = szPublicKeyPEM.find("-----END PUBLIC KEY-----", pos)) { + szPublicKeyPEM.erase(pos, strlen("-----END PUBLIC KEY-----")); + } - puts("After:"); - nkg::PrintMemory(pbPatch, pbPatch + sizeof(Keyword), pbPatch); + for (auto pos = szPublicKeyPEM.find('\n'); pos != std::string::npos; pos = szPublicKeyPEM.find('\n', pos)) { + szPublicKeyPEM.erase(pos, 1); + } + + for (size_t i = 0, key = 8; i < szPublicKeyPEM.length(); ++i, --key) { + if (key == 0) { + key = 8; + } + szPublicKeyPEM[i] ^= static_cast(0xBB - key); + } + + while (szPublicKeyPEM.length() < sizeof(Keyword)) { + szPublicKeyPEM.push_back('\x00'); + } + + auto lpPatch = m_Image.ImageOffset(m_PatchOffset.value()); + + puts("**************************************************************"); + puts("* PatchSolution1 *"); + puts("**************************************************************"); + + printf("[*] Previous:\n"); + Misc::PrintMemory(lpPatch, sizeof(Keyword), m_Image.ImageBase()); + memcpy(lpPatch, szPublicKeyPEM.data(), sizeof(Keyword)); + printf("[*] After:\n"); + Misc::PrintMemory(lpPatch, sizeof(Keyword), m_Image.ImageBase()); + printf("\n"); + + } else { + throw ARL::Exception(__FILE__, __LINE__, "PatchSolution1: not ready yet."); + } + } - puts(""); } diff --git a/navicat-patcher/PatchSolution2.cpp b/navicat-patcher/PatchSolution2.cpp index 0756354..61fe9b4 100644 --- a/navicat-patcher/PatchSolution2.cpp +++ b/navicat-patcher/PatchSolution2.cpp @@ -1,254 +1,301 @@ #include "PatchSolutions.hpp" -#include +#include "Misc.hpp" +#include -const char PatchSolution2::Keyword[1114] = - "BIjWyoeRR0NBgkqnDZWxCgKCEAw1dqF3DTvOB91ZHwecJYFrdM1KEh" - "1yVeRoGqSdLLGZGUlngig3OD5mMzs889IqWqqfHSeHMvzyg1p6UPCY" - "nesxa9M2dDUrXHomRHOFHSfsbSXRFwt5GivtnJG9lLJHZ7XWeIQABi" - "dKionYD3O6c9tvUAoDosUJAdQ1RaSXTzyETbHTRtnTPeLpO3EedGMs" - "v3jG9yPcmmdYkddSeJRwn2raPJmnvdHScHUACw0sUNuosAqPaQbTQN" - "PATDzcrnd1Sf8RIbUp4MQJFVJugPLVZbP53Gjtyyniqe5q75kva8Qm" - "Hr1uOuXkVppe3cwECaGamupG43L1XfcpRjCMrxRep3s2VlbL01xmfz" - "5cIhrj34iVmgZSAmIb8ZxiHPdp1oDMFkbNetZyWegqjAHQQ9eoSOTD" - "bERbKEwZ5FLeLsbNAxfqsapB1XBvCavFHualx6bxVxuRQceh4z8kaZ" - "iv2pOKbZQSJ2Dx5HEq0bYZ6y6b7sN9IaeDFNQwjzQn1K7k3XlYAPWC" - "IvDe8Ln0FUe4yMNmuUhu5RTjxE05hUqtz1HjJvYQ9Es1VA6LflKQ87" - "TwIXBNvfrcHaZ72QM4dQtDUyEMrLgMDkJBDM9wqIDps65gSlAz6eHD" - "8tYWUttrWose0cH0yykVnqFzPtdRiZyZRfio6lGyK48mIC9z7T6MN3" - "a7OaLZHZSwzcpQLcGi7M9q1wXLq4Ms1UvlwntB9FLHc63tHPpG8rhn" - "XhZIk4QrSm4GYuEKQVHwku6ulw6wfggVL8FZPhoPCGsrb2rQGurBUL" - "3lkVJ6RO9VGHcczDYomXqAJqlt4y9pkQIj9kgwTrxTzEZgMGdYZqsV" - "4Bd5JjtrL7u3LA0N2Hq9Xvmmis2jDVhSQoUoGukNIoqng3SBsf0E7b" - "4W0S1aZSSOJ90nQHQkQShE9YIMDBbNwIg2ncthwADYqibYUgIvJcK9" - "89XHnYmZsdMWtt53lICsXE1vztR5WrQjSw4WXDiB31LXTrvudCB6vw" - "kCQa4leutETpKLJ2bYaOYBdoiBFOwvf36YaSuRoY4SP2x1pWOwGFTg" - "d90J2uYyCqUa3Q3iX52iigT4EKL2vJKdJ"; +namespace nkg { -const uint8_t PatchSolution2::FunctionHeader[9] = { - 0x55, // push rbp - 0x48, 0x89, 0xe5, // mov rbp, rsp - 0x41, 0x57, // push r15 - 0x41, 0x56, // push r14 - 0x53, // push rbx -}; + const char PatchSolution2::Keyword[1114] = + "BIjWyoeRR0NBgkqnDZWxCgKCEAw1dqF3DTvOB91ZHwecJYFrdM1KEh" + "1yVeRoGqSdLLGZGUlngig3OD5mMzs889IqWqqfHSeHMvzyg1p6UPCY" + "nesxa9M2dDUrXHomRHOFHSfsbSXRFwt5GivtnJG9lLJHZ7XWeIQABi" + "dKionYD3O6c9tvUAoDosUJAdQ1RaSXTzyETbHTRtnTPeLpO3EedGMs" + "v3jG9yPcmmdYkddSeJRwn2raPJmnvdHScHUACw0sUNuosAqPaQbTQN" + "PATDzcrnd1Sf8RIbUp4MQJFVJugPLVZbP53Gjtyyniqe5q75kva8Qm" + "Hr1uOuXkVppe3cwECaGamupG43L1XfcpRjCMrxRep3s2VlbL01xmfz" + "5cIhrj34iVmgZSAmIb8ZxiHPdp1oDMFkbNetZyWegqjAHQQ9eoSOTD" + "bERbKEwZ5FLeLsbNAxfqsapB1XBvCavFHualx6bxVxuRQceh4z8kaZ" + "iv2pOKbZQSJ2Dx5HEq0bYZ6y6b7sN9IaeDFNQwjzQn1K7k3XlYAPWC" + "IvDe8Ln0FUe4yMNmuUhu5RTjxE05hUqtz1HjJvYQ9Es1VA6LflKQ87" + "TwIXBNvfrcHaZ72QM4dQtDUyEMrLgMDkJBDM9wqIDps65gSlAz6eHD" + "8tYWUttrWose0cH0yykVnqFzPtdRiZyZRfio6lGyK48mIC9z7T6MN3" + "a7OaLZHZSwzcpQLcGi7M9q1wXLq4Ms1UvlwntB9FLHc63tHPpG8rhn" + "XhZIk4QrSm4GYuEKQVHwku6ulw6wfggVL8FZPhoPCGsrb2rQGurBUL" + "3lkVJ6RO9VGHcczDYomXqAJqlt4y9pkQIj9kgwTrxTzEZgMGdYZqsV" + "4Bd5JjtrL7u3LA0N2Hq9Xvmmis2jDVhSQoUoGukNIoqng3SBsf0E7b" + "4W0S1aZSSOJ90nQHQkQShE9YIMDBbNwIg2ncthwADYqibYUgIvJcK9" + "89XHnYmZsdMWtt53lICsXE1vztR5WrQjSw4WXDiB31LXTrvudCB6vw" + "kCQa4leutETpKLJ2bYaOYBdoiBFOwvf36YaSuRoY4SP2x1pWOwGFTg" + "d90J2uYyCqUa3Q3iX52iigT4EKL2vJKdJ"; -PatchSolution2::PatchSolution2(const X64ImageInterpreter& Image) noexcept : - pvt_Image(Image), - pvt_Disassembler(CapstoneDisassembler::Create(CS_ARCH_X86, CS_MODE_64)), - pvt_Assembler(KeystoneAssembler::Create(KS_ARCH_X86, KS_MODE_64)), - pvt_FunctionOffset(X64ImageInterpreter::InvalidOffset), - pvt_KeywordOffset(X64ImageInterpreter::InvalidOffset), - pvt_StdStringAppendStubRva(X64ImageInterpreter::InvalidAddress) -{ - pvt_Disassembler.Option(CS_OPT_DETAIL, CS_OPT_ON); -} + PatchSolution2::PatchSolution2(const X64ImageInterpreter& Image) : + m_Image(Image), + m_DisassemblerEngine(CS_ARCH_X86, CS_MODE_64), + m_AssemblerEngine(KS_ARCH_X86, KS_MODE_64) + { + m_DisassemblerEngine.Option(CS_OPT_DETAIL, CS_OPT_ON); + } -bool PatchSolution2::FindPatchOffset() noexcept { - auto FunctionOffset = X64ImageInterpreter::InvalidOffset; - auto KeywordOffset = X64ImageInterpreter::InvalidOffset; - auto StdStringAppendStubRva = X64ImageInterpreter::InvalidAddress; + [[nodiscard]] + const char* PatchSolution2::TryResolveStubHelper(const void* lpStubHelperProc) const { + if (auto dyld_info = m_Image.SpecialLoadCommand(); dyld_info) { + auto Disassembler = m_DisassemblerEngine.CreateDisassembler(); + + // A stub-helper proc must look like: + // push xxxxh; + // jmp loc_xxxxxxxx + // which should be 10 bytes long. + Disassembler.SetContext({ lpStubHelperProc, 10, 0 }); - try { - auto Sec__text = pvt_Image.ImageSection("__TEXT", "__text"); - auto Sec__const = pvt_Image.ImageSection("__TEXT", "__const"); - auto Sec__stubs = pvt_Image.ImageSection("__TEXT", "__stubs"); - auto SecView__text = pvt_Image.SectionView(Sec__text); - auto SecView__const = pvt_Image.SectionView(Sec__const); - auto SecView__stubs = pvt_Image.SectionView(Sec__stubs); + if (Disassembler.Next()) { + auto lpInsn = Disassembler.GetInstruction(); + if (strcasecmp(lpInsn->mnemonic, "push") == 0 && lpInsn->detail->x86.operands[0].type == X86_OP_IMM) { + auto pbBindOpcode = + m_Image.ImageOffset(dyld_info->lazy_bind_off) + + lpInsn->detail->x86.operands[0].imm; - KeywordOffset = pvt_Image.SearchSectionOffset("__TEXT", "__const", [](const uint8_t* p) { - return memcmp(p, Keyword, sizeof(Keyword)) == 0; - }); + while ((*pbBindOpcode & BIND_OPCODE_MASK) != BIND_OPCODE_DONE) { + switch (*pbBindOpcode & BIND_OPCODE_MASK) { + case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: // 0x10 + case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: // 0x30 + case BIND_OPCODE_SET_TYPE_IMM: // 0x50 + case BIND_OPCODE_DO_BIND: // 0x90 + case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: // 0xB0 + ++pbBindOpcode; + break; + case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: // 0x20 + case BIND_OPCODE_SET_ADDEND_SLEB: // 0x60 + case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: // 0x70 + case BIND_OPCODE_ADD_ADDR_ULEB: // 0x80 + case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: // 0xA0 + while (*(++pbBindOpcode) & 0x80u) {} + ++pbBindOpcode; + break; + case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: // 0x40 + return reinterpret_cast(pbBindOpcode + 1); + case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: // 0xC0 + // + // This opcode is too rare to appear, + // It is okay to dismiss this opcode + // + return nullptr; + default: + return nullptr; + } + } - auto KeywordRva = pvt_Image.OffsetToRva(KeywordOffset); - - auto Hint = pvt_Image.SearchSectionOffset("__TEXT", "__text", [Sec__text, SecView__text, KeywordRva](const uint8_t* p) { - auto rip = (p - SecView__text) + Sec__text->addr + 4; - auto off = *reinterpret_cast(p); - return rip + off == KeywordRva; - }) - 0xc0; - - for (uint32_t i = 0; i < 0xc0; ++i) { - if (memcmp(pvt_Image.ImageOffset(Hint + i), FunctionHeader, sizeof(FunctionHeader)) == 0) { - FunctionOffset = Hint + i; - break; - } - } - - if (FunctionOffset == X64ImageInterpreter::InvalidOffset) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "Not found."); - } - - pvt_Disassembler.SetContext(SecView__stubs, Sec__stubs->size, Sec__stubs->addr); - while (pvt_Disassembler.Next()) { - auto insn = pvt_Disassembler.GetInstruction(); - - // - // As far as I know, all stub functions have a pattern looking like: - // jmp qword ptr [RIP + xxxx] - // - 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) { - uint64_t la_symbol_ptr_rva = pvt_Disassembler.GetContext().Address + insn->detail->x86.operands[0].mem.disp; - uint32_t la_symbol_ptr_offset = pvt_Image.RvaToOffset(la_symbol_ptr_rva); - if (la_symbol_ptr_offset == X64ImageInterpreter::InvalidOffset) - continue; - - uint64_t stub_helper_rva = *pvt_Image.ImageOffset(la_symbol_ptr_offset); - uint32_t stub_helper_offset = pvt_Image.RvaToOffset(stub_helper_rva); - if (stub_helper_offset == X64ImageInterpreter::InvalidOffset) - continue; - - // - // __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc - // is the mangled name of "std::__1::basic_string, std::__1::allocator >::append(char const*)", - // which is, as known as, "std::string::append(const char*)" - // You can demangle it by c++flit - // e.g. - // c++filt -_ '__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc' - // - if (nkg::IsResolvedTo(pvt_Image, pvt_Image.ImageOffset(stub_helper_offset), "__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc")) { - auto StdStringAppendStubOffset = pvt_Disassembler.GetInstructionContext().pbOpcode - pvt_Image.ImageBase(); - StdStringAppendStubRva = pvt_Image.OffsetToRva(StdStringAppendStubOffset); - break; } } } - - if (StdStringAppendStubRva == X64ImageInterpreter::InvalidAddress) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "Not found."); - } - - pvt_FunctionOffset = FunctionOffset; - pvt_KeywordOffset = KeywordOffset; - pvt_StdStringAppendStubRva = StdStringAppendStubRva; - - printf("[+] PatchSolution2 ...... Ready to apply.\n"); - printf(" Function offset = +0x%.8x\n", pvt_FunctionOffset); - printf(" Keyword offset = +0x%.8x\n", pvt_KeywordOffset); - printf(" std::string::append(const char*) RVA = 0x%.16llx\n", pvt_StdStringAppendStubRva); - return true; - } catch (...) { - printf("[-] PatchSolution2 ...... Omitted.\n"); - return false; + return nullptr; } -} -[[nodiscard]] -bool PatchSolution2::CheckKey(const RSACipher& RsaCipher) const noexcept { - std::string PublicKeyPEM = RsaCipher.ExportKeyString(); + [[nodiscard]] + bool PatchSolution2::FindPatchOffset() noexcept { + try { + std::optional KeywordOffset; + std::optional FunctionOffset; + std::optional StdStringAppendStubRva; - PublicKeyPEM.erase(PublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"), 26); - PublicKeyPEM.erase(PublicKeyPEM.find("-----END PUBLIC KEY-----"), 24); - { - std::string::size_type pos = 0; - while ((pos = PublicKeyPEM.find('\n', pos)) != std::string::npos) { - PublicKeyPEM.erase(pos, 1); + auto section__text = m_Image.ImageSection("__TEXT", "__text"); + auto section__stubs = m_Image.ImageSection("__TEXT", "__stubs"); + auto sectionview__text = m_Image.ImageSectionView(section__text); + auto sectionview__stubs = m_Image.ImageSectionView(section__stubs); + auto Disassembler = m_DisassemblerEngine.CreateDisassembler(); + + auto p = m_Image.SearchSection("__TEXT", "__const", [](const void* base, size_t i, size_t size) { + if (i + sizeof(Keyword) <= size) { + return memcmp(ARL::AddressOffset(base, i), Keyword, sizeof(Keyword)) == 0; + } else { + return false; + } + }); + if (p) { + KeywordOffset = m_Image.ConvertPtrToOffset(p); + } else { + throw ARL::Exception(__FILE__, __LINE__, "Keyword is not found."); + } + + p = m_Image.SearchSection(section__text, [section__text, KeywordRva = m_Image.ConvertOffsetToRva(KeywordOffset.value())](const void* base, size_t i, size_t size) { + if (i + sizeof(uint32_t) <= size) { + auto rip = i + section__text->addr + sizeof(uint32_t); + auto off = ARL::AddressRead(ARL::AddressOffset(base, i)); + return rip + off == KeywordRva; + } else { + return false; + } + }); + if (p) { + p = m_Image.SearchSection( + section__text, + ARL::AddressDelta(p, sectionview__text) >= 0xc0 ? ARL::AddressDelta(p, sectionview__text) - 0xc0 : 0, + [p](const void* base, size_t i, size_t size) { + static const uint8_t FunctionHeader[9] = { + 0x55, // push rbp + 0x48, 0x89, 0xe5, // mov rbp, rsp + 0x41, 0x57, // push r15 + 0x41, 0x56, // push r14 + 0x53, // push rbx + }; + if (ARL::AddressOffset(base, i + sizeof(FunctionHeader)) <= p) { + return memcmp(ARL::AddressOffset(base, i), FunctionHeader, sizeof(FunctionHeader)) == 0; + } else { + return false; + } + } + ); + if (p) { + FunctionOffset = m_Image.ConvertPtrToOffset(p); + } else { + throw ARL::Exception(__FILE__, __LINE__, "Function header is not found."); + } + } else { + throw ARL::Exception(__FILE__, __LINE__, "Xref of Keyword is not found."); + } + + Disassembler.SetContext({ sectionview__stubs, section__stubs->size, section__stubs->addr }); + while (Disassembler.Next()) { + auto lpInsn = Disassembler.GetInstruction(); + + // + // As far as I know, all stub functions have a pattern looking like: + // jmp qword ptr [RIP + xxxx] + // + if (strcasecmp(lpInsn->mnemonic, "jmp") == 0 && lpInsn->detail->x86.operands[0].type == X86_OP_MEM && lpInsn->detail->x86.operands[0].mem.base == X86_REG_RIP) { + try { + X64ImageAddress la_symbol_ptr_rva = Disassembler.GetContext().Address + lpInsn->detail->x86.operands[0].mem.disp; + X64ImageOffset la_symbol_ptr_offset = m_Image.ConvertRvaToOffset(la_symbol_ptr_rva); + + X64ImageAddress stub_helper_rva = ARL::AddressRead(m_Image.ImageBase(), la_symbol_ptr_offset); + X64ImageOffset stub_helper_offset = m_Image.ConvertRvaToOffset(stub_helper_rva); + + // + // __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc + // is the mangled name of "std::__1::basic_string, std::__1::allocator >::append(char const*)", + // which is, as known as, "std::string::append(const char*)" + // You can demangle it by c++flit + // e.g. + // c++filt -_ '__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc' + // + auto lpszSymbolName = TryResolveStubHelper(m_Image.ImageOffset(stub_helper_offset)); + if (lpszSymbolName && strcmp(lpszSymbolName, "__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc") == 0) { + StdStringAppendStubRva = Disassembler.GetInstructionContext().Address; + break; + } + } catch (...) { + continue; + } + } + } + if (StdStringAppendStubRva.has_value()) { + m_KeywordOffset = KeywordOffset.value(); + m_FunctionOffset = FunctionOffset.value(); + m_StdStringAppendStubRva = StdStringAppendStubRva.value(); + } else { + throw ARL::Exception(__FILE__, __LINE__, "std::string::append(const char*) is not found."); + } + + printf("[+] PatchSolution2 ...... Ready to apply.\n"); + printf(" Keyword offset = +0x%.8x\n", m_KeywordOffset.value()); + printf(" CSRegistrationCenter::obtainPublicKey RVA = 0x%.16llx\n", m_Image.ConvertOffsetToRva(m_FunctionOffset.value())); + printf(" std::string::append(const char*) RVA = 0x%.16llx\n", m_StdStringAppendStubRva.value()); + return true; + } catch (...) { + printf("[-] PatchSolution2 ...... Omitted.\n"); + return false; } } - return PublicKeyPEM.length() == 0x188; -} - -void PatchSolution2::MakePatch(const RSACipher& RsaCipher) const { - if (pvt_FunctionOffset == X64ImageInterpreter::InvalidOffset || - pvt_KeywordOffset == X64ImageInterpreter::InvalidOffset || - pvt_StdStringAppendStubRva == X64ImageInterpreter::InvalidAddress) - { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "PatchSolution2 is not ready."); + [[nodiscard]] + bool PatchSolution2::CheckKey(const RSACipher& Cipher) const noexcept { + return Cipher.Bits() == 2048; } - // - // Prepare public key string - // - std::string PublicKeyPEM = RsaCipher.ExportKeyString(); + void PatchSolution2::MakePatch(const RSACipher& Cipher) const { + if (m_KeywordOffset.has_value() && m_FunctionOffset.has_value() && m_StdStringAppendStubRva.has_value()) { + // + // Prepare public key string + // + auto szPublicKeyPEM = Cipher.ExportKeyString(); - PublicKeyPEM.erase(PublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"), 26); - PublicKeyPEM.erase(PublicKeyPEM.find("-----END PUBLIC KEY-----"), 24); - { - std::string::size_type pos = 0; - while ((pos = PublicKeyPEM.find('\n', pos)) != std::string::npos) { - PublicKeyPEM.erase(pos, 1); + for (auto pos = szPublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"); pos != std::string::npos; pos = szPublicKeyPEM.find("-----BEGIN PUBLIC KEY-----", pos)) { + szPublicKeyPEM.erase(pos, strlen("-----BEGIN PUBLIC KEY-----")); + } + + for (auto pos = szPublicKeyPEM.find("-----END PUBLIC KEY-----"); pos != std::string::npos; pos = szPublicKeyPEM.find("-----END PUBLIC KEY-----", pos)) { + szPublicKeyPEM.erase(pos, strlen("-----END PUBLIC KEY-----")); + } + + for (auto pos = szPublicKeyPEM.find('\n'); pos != std::string::npos; pos = szPublicKeyPEM.find('\n', pos)) { + szPublicKeyPEM.erase(pos, 1); + } + + // + // Prepare new function opcodes + // + auto Assembler = m_AssemblerEngine.CreateAssembler(); + auto MachineCode = Assembler.GenerateMachineCode( + [KeywordRva = m_Image.ConvertOffsetToRva(m_KeywordOffset.value()), StdStringAppendStubRva = m_StdStringAppendStubRva.value()]() -> std::string { + const char asm_template[] = + "push rbp;" + "mov rbp, rsp;" + + "xor rax, rax;" // initialize std::string with null + "mov qword ptr[rdi], rax;" + "mov qword ptr[rdi + 0x8], rax;" + "mov qword ptr[rdi + 0x10], rax;" + + "lea rsi, qword ptr[0x%.16llx];" // filled with address to Keyword + "call 0x%.16llx;" // filled with address to std::string::append(const char*) + + "leave;" + "ret;"; + std::string asm_string; + + int l = snprintf(nullptr, 0, asm_template, KeywordRva, StdStringAppendStubRva); + if (l < 0) { + std::terminate(); + } + + asm_string.resize(l + 1); + + l = snprintf(asm_string.data(), asm_string.length(), asm_template, KeywordRva, StdStringAppendStubRva); + if (l < 0) { + std::terminate(); + } + + while (asm_string.back() == '\x00') { + asm_string.pop_back(); + } + + return asm_string; + }().c_str(), + m_Image.ConvertOffsetToRva(m_FunctionOffset.value()) + ); + + puts("**************************************************************"); + puts("* PatchSolution2 *"); + puts("**************************************************************"); + + auto lpKeyword = m_Image.ImageOffset(m_KeywordOffset.value()); + auto lpFunction = m_Image.ImageOffset(m_FunctionOffset.value()); + + printf("[*] Previous:\n"); + Misc::PrintMemory(lpKeyword, szPublicKeyPEM.length() + 1, m_Image.ImageBase()); + memcpy(lpKeyword, szPublicKeyPEM.c_str(), szPublicKeyPEM.length() + 1); // with a null-terminator + printf("[*] After:\n"); + Misc::PrintMemory(lpKeyword, szPublicKeyPEM.length() + 1, m_Image.ImageBase()); + printf("\n"); + + printf("[*] Previous:\n"); + Misc::PrintMemory(lpFunction, MachineCode.size(), m_Image.ImageBase()); + memcpy(lpFunction, MachineCode.data(), MachineCode.size()); + printf("[*] After:\n"); + Misc::PrintMemory(lpFunction, MachineCode.size(), m_Image.ImageBase()); + printf("\n"); + } else { + throw ARL::Exception(__FILE__, __LINE__, "PatchSolution2: not ready yet."); } } - // - // Prepare new function opcodes - // - uint64_t FunctionRVA = pvt_Image.OffsetToRva(pvt_FunctionOffset); - uint64_t KeywordRVA = pvt_Image.OffsetToRva(pvt_KeywordOffset); - - char AssemblyCode[512] = {}; - sprintf(AssemblyCode, - "push rbp;" - "mov rbp, rsp;" - "push r15;" - "push r14;" - "push rbx;" - "sub rsp, 0x48;" - - "mov rbx, rdi;" - - "xor rax, rax;" // initialize std::string with null - "mov qword ptr[rsp], rax;" - "mov qword ptr[rsp + 0x8], rax;" - "mov qword ptr[rsp + 0x10], rax;" - - "lea rdi, qword ptr[rsp];" - "lea rsi, qword ptr[0x%.16llx];" // filled with address to Keyword - "call 0x%.16llx;" // filled with address to std::string::append(const char*) - - "mov rax, qword ptr[rsp];" - "mov qword ptr[rbx], rax;" - "mov rax, qword ptr[rsp + 0x8];" - "mov qword ptr[rbx + 0x8], rax;" - "mov rax, qword ptr[rsp + 0x10];" - "mov qword ptr[rbx + 0x10], rax;" - - "mov rax, rbx;" - "add rsp, 0x48;" - "pop rbx;" - "pop r14;" - "pop r15;" - "pop rbp;" - "ret;", - KeywordRVA, - pvt_StdStringAppendStubRva - ); - - auto NewFunctionOpcode = pvt_Assembler.GenerateOpcode(AssemblyCode, FunctionRVA); - - auto pbFunctionPatch = pvt_Image.ImageOffset(pvt_FunctionOffset); - auto pbKeywordPatch = pvt_Image.ImageOffset(pvt_KeywordOffset); - - puts("**************************************************************"); - puts("* PatchSolution2 *"); - puts("**************************************************************"); - printf("@+0x%.8x\n", pvt_KeywordOffset); - - puts("Previous:"); - nkg::PrintMemory(pbKeywordPatch, pbKeywordPatch + sizeof(Keyword) - 1, pbKeywordPatch); - - memcpy(pbKeywordPatch, PublicKeyPEM.c_str(), PublicKeyPEM.length() + 1); // with a null-terminator - - puts("After:"); - nkg::PrintMemory(pbKeywordPatch, pbKeywordPatch + sizeof(Keyword) - 1, pbKeywordPatch); - - - - - puts(""); - printf("@+0x%.8x\n", pvt_FunctionOffset); - - puts("Previous:"); - nkg::PrintMemory(pbFunctionPatch, pbFunctionPatch + NewFunctionOpcode.size(), pbFunctionPatch); - - memcpy(pbFunctionPatch, NewFunctionOpcode.data(), NewFunctionOpcode.size()); - - puts("After:"); - nkg::PrintMemory(pbFunctionPatch, pbFunctionPatch + NewFunctionOpcode.size(), pbFunctionPatch); - - puts(""); } + diff --git a/navicat-patcher/PatchSolutions.hpp b/navicat-patcher/PatchSolutions.hpp index 36cd28f..0f3b838 100644 --- a/navicat-patcher/PatchSolutions.hpp +++ b/navicat-patcher/PatchSolutions.hpp @@ -1,114 +1,107 @@ #pragma once -#include "../common/Exception.hpp" -#include "../common/ResourceOwned.hpp" -#include "../common/RSACipher.hpp" +#include "Exception.hpp" +#include "RSACipher.hpp" #include "ResourceTraitsUnix.hpp" -#include "X64ImageInterpreter.hpp" #include "CapstoneDisassembler.hpp" #include "KeystoneAssembler.hpp" +#include "X64ImageInterpreter.hpp" +#include namespace nkg { - // - // Print memory data in [from, to) at least - // If `base` is not nullptr, print address as offset. Otherwise, as absolute address. - // NOTICE: - // `base` must >= `from` - // - void PrintMemory(const void *from, const void *to, const void *base = nullptr); - bool IsResolvedTo(const X64ImageInterpreter& Image, const void* StubHelperProc, const char *Symbol); + + class PatchSolution { + public: + + [[nodiscard]] + virtual bool FindPatchOffset() noexcept = 0; + + [[nodiscard]] + virtual bool CheckKey(const RSACipher& Cipher) const noexcept = 0; + + virtual void MakePatch(const RSACipher& Cipher) const = 0; + + virtual ~PatchSolution() = default; + }; + + class PatchSolution0 final : public PatchSolution { + private: + + static const char Keyword[451]; + + const X64ImageInterpreter& m_Image; + std::optional m_PatchOffset; + + public: + + PatchSolution0(const X64ImageInterpreter& Image); + + [[nodiscard]] + // NOLINTNEXTLINE: mark "virtual" explicitly for more readability + virtual bool FindPatchOffset() noexcept override; + + [[nodiscard]] + // NOLINTNEXTLINE: mark "virtual" explicitly for more readability + virtual bool CheckKey(const RSACipher& Cipher) const noexcept override; + + // NOLINTNEXTLINE: mark "virtual" explicitly for more readability + virtual void MakePatch(const RSACipher& Cipher) const override; + }; + + class PatchSolution1 final : public PatchSolution { + private: + + static const uint8_t Keyword[0x188]; + + const X64ImageInterpreter& m_Image; + std::optional m_PatchOffset; + + public: + + PatchSolution1(const X64ImageInterpreter& Image); + + [[nodiscard]] + // NOLINTNEXTLINE: mark "virtual" explicitly for more readability + virtual bool FindPatchOffset() noexcept override; + + [[nodiscard]] + // NOLINTNEXTLINE: mark "virtual" explicitly for more readability + virtual bool CheckKey(const RSACipher& Cipher) const noexcept override; + + // NOLINTNEXTLINE: mark "virtual" explicitly for more readability + virtual void MakePatch(const RSACipher& Cipher) const override; + }; + + class PatchSolution2 final : public PatchSolution { + private: + + static const char Keyword[1114]; + + const X64ImageInterpreter& m_Image; + CapstoneEngine m_DisassemblerEngine; + KeystoneEngine m_AssemblerEngine; + + std::optional m_KeywordOffset; + std::optional m_FunctionOffset; + std::optional m_StdStringAppendStubRva; + + [[nodiscard]] + const char* TryResolveStubHelper(const void* lpStubHelperProc) const; + + public: + + PatchSolution2(const X64ImageInterpreter& Image); + + [[nodiscard]] + // NOLINTNEXTLINE: mark "virtual" explicitly for more readability + virtual bool FindPatchOffset() noexcept override; + + [[nodiscard]] + // NOLINTNEXTLINE: mark "virtual" explicitly for more readability + virtual bool CheckKey(const RSACipher& Cipher) const noexcept override; + + // NOLINTNEXTLINE: mark "virtual" explicitly for more readability + virtual void MakePatch(const RSACipher& Cipher) const override; + }; + } -class PatchSolution { -public: - - [[nodiscard]] - virtual bool FindPatchOffset() noexcept = 0; - - [[nodiscard]] - virtual bool CheckKey(const RSACipher& RsaCipher) const noexcept = 0; - - virtual void MakePatch(const RSACipher& RsaCipher) const = 0; - - virtual ~PatchSolution() = default; -}; - -class PatchSolution0 : public PatchSolution { -private: - - static const char Keyword[452]; - - const X64ImageInterpreter& pvt_Image; - uint32_t pvt_PatchOffset; - -public: - - explicit - PatchSolution0(const X64ImageInterpreter& Image) noexcept; - - [[nodiscard]] - // NOLINTNEXTLINE: mark "virtual" explicitly for more readability - virtual bool FindPatchOffset() noexcept override; - - [[nodiscard]] - // NOLINTNEXTLINE: mark "virtual" explicitly for more readability - virtual bool CheckKey(const RSACipher& RsaCipher) const noexcept override; - - // NOLINTNEXTLINE: mark "virtual" explicitly for more readability - virtual void MakePatch(const RSACipher& RsaCipher) const override; -}; - -class PatchSolution1 : public PatchSolution { -private: - - static const uint8_t Keyword[0x188]; - - const X64ImageInterpreter& pvt_Image; - uint32_t pvt_PatchOffset; - -public: - - explicit - PatchSolution1(const X64ImageInterpreter& Image) noexcept; - - [[nodiscard]] - // NOLINTNEXTLINE: mark "virtual" explicitly for more readability - virtual bool FindPatchOffset() noexcept override; - - [[nodiscard]] - // NOLINTNEXTLINE: mark "virtual" explicitly for more readability - virtual bool CheckKey(const RSACipher& RsaCipher) const noexcept override; - - // NOLINTNEXTLINE: mark "virtual" explicitly for more readability - virtual void MakePatch(const RSACipher& RsaCipher) const override; -}; - -class PatchSolution2 : public PatchSolution { -private: - - static const char Keyword[1114]; - static const uint8_t FunctionHeader[9]; - - const X64ImageInterpreter& pvt_Image; - CapstoneDisassembler pvt_Disassembler; - KeystoneAssembler pvt_Assembler; - uint32_t pvt_FunctionOffset; - uint32_t pvt_KeywordOffset; - uint64_t pvt_StdStringAppendStubRva; - -public: - - explicit - PatchSolution2(const X64ImageInterpreter& Image) noexcept; - - [[nodiscard]] - // NOLINTNEXTLINE: mark "virtual" explicitly for more readability - virtual bool FindPatchOffset() noexcept override; - - [[nodiscard]] - // NOLINTNEXTLINE: mark "virtual" explicitly for more readability - virtual bool CheckKey(const RSACipher& RsaCipher) const noexcept override; - - // NOLINTNEXTLINE: mark "virtual" explicitly for more readability - virtual void MakePatch(const RSACipher& RsaCipher) const override; -}; - diff --git a/navicat-patcher/ResourceTraitsCapstone.hpp b/navicat-patcher/ResourceTraitsCapstone.hpp index 2728943..9d5df56 100644 --- a/navicat-patcher/ResourceTraitsCapstone.hpp +++ b/navicat-patcher/ResourceTraitsCapstone.hpp @@ -2,37 +2,39 @@ #include #include "ExceptionCapstone.hpp" -struct CapstoneHandleTraits { - using HandleType = csh; +namespace ARL::ResourceTraits { - static inline const HandleType InvalidValue = 0; + struct CapstoneHandle { + using HandleType = csh; - [[nodiscard]] - static bool IsValid(const HandleType& Handle) noexcept { - return Handle != InvalidValue; - } + static inline const HandleType InvalidValue = 0; - static void Releasor(HandleType& Handle) { - auto err = cs_close(&Handle); - if (err != CS_ERR_OK) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::CapstoneError(__FILE__, __LINE__, err, "ks_close failed."); + [[nodiscard]] + static bool IsValid(const HandleType& Handle) noexcept { + return Handle != InvalidValue; } - } -}; -struct CapstoneInsnTraits { - using HandleType = cs_insn*; + static void Release(HandleType& Handle) { + if (auto err = cs_close(&Handle); err != CS_ERR_OK) { + throw ARL::CapstoneError(__FILE__, __LINE__, err, "ks_close failed."); + } + } + }; - static inline const HandleType InvalidValue = nullptr; + struct CapstoneInsn { + using HandleType = cs_insn*; - [[nodiscard]] - static bool IsValid(const HandleType& Handle) noexcept { - return Handle != InvalidValue; - } + static inline const HandleType InvalidValue = nullptr; - static void Releasor(const HandleType& Handle) noexcept { - cs_free(Handle, 1); - } -}; + [[nodiscard]] + static bool IsValid(const HandleType& Handle) noexcept { + return Handle != InvalidValue; + } + + static void Release(const HandleType& Handle) noexcept { + cs_free(Handle, 1); + } + }; + +} diff --git a/navicat-patcher/ResourceTraitsKeystone.hpp b/navicat-patcher/ResourceTraitsKeystone.hpp index 0af1be6..f1d8b7b 100644 --- a/navicat-patcher/ResourceTraitsKeystone.hpp +++ b/navicat-patcher/ResourceTraitsKeystone.hpp @@ -2,37 +2,40 @@ #include #include "ExceptionKeystone.hpp" -struct KeystoneHandleTraits { - using HandleType = ks_engine*; +namespace ARL::ResourceTraits { - static inline const HandleType InvalidValue = nullptr; + struct KeystoneHandle { + using HandleType = ks_engine*; - [[nodiscard]] - static bool IsValid(const HandleType& Handle) noexcept { - return Handle != InvalidValue; - } + static inline const HandleType InvalidValue = nullptr; - static void Releasor(const HandleType& Handle) { - auto err = ks_close(Handle); - if (err != KS_ERR_OK) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::KeystoneError(__FILE__, __LINE__, err, "ks_close failed."); + [[nodiscard]] + static bool IsValid(const HandleType& Handle) noexcept { + return Handle != InvalidValue; } - } -}; -struct KeystoneMallocTraits { - using HandleType = uint8_t*; + static void Release(const HandleType& Handle) { + auto err = ks_close(Handle); + if (err != KS_ERR_OK) { + throw ARL::KeystoneError(__FILE__, __LINE__, err, "ks_close failed."); + } + } + }; - static inline const HandleType InvalidValue = nullptr; + struct KeystoneMalloc { + using HandleType = uint8_t*; - [[nodiscard]] - static bool IsValid(const HandleType& Handle) noexcept { - return Handle != InvalidValue; - } + static inline const HandleType InvalidValue = nullptr; - static void Releasor(const HandleType& Handle) noexcept { - ks_free(Handle); - } -}; + [[nodiscard]] + static bool IsValid(const HandleType& Handle) noexcept { + return Handle != InvalidValue; + } + + static void Release(const HandleType& Handle) noexcept { + ks_free(Handle); + } + }; + +} diff --git a/navicat-patcher/ResourceTraitsUnix.hpp b/navicat-patcher/ResourceTraitsUnix.hpp index dca01ee..041d9a2 100644 --- a/navicat-patcher/ResourceTraitsUnix.hpp +++ b/navicat-patcher/ResourceTraitsUnix.hpp @@ -1,35 +1,38 @@ #pragma once -#include // NOLINT +#include #include #include -#include "../common/ExceptionSystem.hpp" +#include "ExceptionSystem.hpp" -struct FileHandleTraits { - using HandleType = int; +namespace ARL::ResourceTraits { - static inline const HandleType InvalidValue = -1; + struct FileDescriptor { + using HandleType = int; - [[nodiscard]] - static bool IsValid(const HandleType& Handle) noexcept { - return Handle != InvalidValue; - } + static inline const HandleType InvalidValue = -1; - static void Releasor(const HandleType& Handle) { - if (close(Handle) != 0) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::SystemError(__FILE__, __LINE__, errno, "close failed."); + [[nodiscard]] + static bool IsValid(const HandleType& Handle) noexcept { + return Handle != InvalidValue; } - } -}; -struct MapViewTraits { - using HandleType = void*; + static void Release(const HandleType& Handle) { + if (close(Handle) != 0) { + throw ARL::SystemError(__FILE__, __LINE__, errno, "close failed."); + } + } + }; - static inline const HandleType InvalidValue = MAP_FAILED; + struct MapView { + using HandleType = void*; - [[nodiscard]] - static bool IsValid(const HandleType& Handle) noexcept { - return Handle != InvalidValue; - } -}; + static inline const HandleType InvalidValue = MAP_FAILED; + + [[nodiscard]] + static bool IsValid(const HandleType& Handle) noexcept { + return Handle != InvalidValue; + } + }; + +} diff --git a/navicat-patcher/X64ImageInterpreter.cpp b/navicat-patcher/X64ImageInterpreter.cpp index 9588827..39c7596 100644 --- a/navicat-patcher/X64ImageInterpreter.cpp +++ b/navicat-patcher/X64ImageInterpreter.cpp @@ -1,131 +1,124 @@ #include "X64ImageInterpreter.hpp" -[[nodiscard]] -X64ImageInterpreter X64ImageInterpreter::Parse(void* ImageBase) { - X64ImageInterpreter NewImage; +namespace nkg { - NewImage.pvt_MachHeader = reinterpret_cast(ImageBase); - if (NewImage.pvt_MachHeader->magic != MH_MAGIC_64) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "Bad Image"); + [[nodiscard]] + X64ImageInterpreter X64ImageInterpreter::Parse(const void* lpImage, size_t cbImage) { + X64ImageInterpreter Interpreter; + + Interpreter.m_MachOSize = cbImage; + Interpreter.m_MachOHeader = reinterpret_cast(lpImage); + if (Interpreter.m_MachOHeader->magic != MH_MAGIC_64) { + throw ARL::AssertionError(__FILE__, __LINE__, "X64ImageInterpreter: bad MachO file: header magic check failure."); + } + + auto cmd_p = ARL::AddressOffsetWithCast(Interpreter.m_MachOHeader, sizeof(mach_header_64)); + for (decltype(mach_header_64::ncmds) i = 0; i < Interpreter.m_MachOHeader->ncmds; ++i) { + switch (cmd_p->cmd) { + case LC_SEGMENT_64: { + auto segcmd_p = reinterpret_cast(cmd_p); + auto section_p = ARL::AddressOffsetWithCast(segcmd_p, sizeof(segment_command_64)); + + Interpreter.m_Segments.emplace_back(segcmd_p); + + for (decltype(segment_command_64::nsects) j = 0; j < segcmd_p->nsects; ++j) { + Interpreter.m_Sections.emplace_back(§ion_p[j]); + Interpreter.m_SectionsAddressMap[section_p[j].addr] = §ion_p[j]; + Interpreter.m_SectionsOffsetMap[section_p[j].offset] = §ion_p[j]; + } + + break; + } + case LC_DYSYMTAB: { + Interpreter.m_SpecialLoadCommands.dysymtab = reinterpret_cast(cmd_p); + break; + } + case LC_SYMTAB: { + Interpreter.m_SpecialLoadCommands.symtab = reinterpret_cast(cmd_p); + break; + } + case LC_DYLD_INFO_ONLY: { + Interpreter.m_SpecialLoadCommands.dyld_info = reinterpret_cast(cmd_p); + break; + } + default: + break; + } + + cmd_p = ARL::AddressOffset(cmd_p, cmd_p->cmdsize); + } + + return Interpreter; } - auto cmd_p = reinterpret_cast(NewImage.pvt_MachHeader + 1); - for (uint32_t i = 0; i < NewImage.pvt_MachHeader->ncmds; ++i) { - switch (cmd_p->cmd) { - case LC_SEGMENT_64: { - auto segcmd_p = reinterpret_cast(cmd_p); - auto section_p = reinterpret_cast(segcmd_p + 1); + [[nodiscard]] + size_t X64ImageInterpreter::NumberOfSegmentCommands() const noexcept { + return m_Segments.size(); + } - NewImage.pvt_SegmentCommands.emplace_back(segcmd_p); + [[nodiscard]] + size_t X64ImageInterpreter::NumberOfSections() const noexcept { + return m_Sections.size(); + } - for (uint32_t j = 0; j < segcmd_p->nsects; ++j) { - NewImage.pvt_Sections.ByIndex[NewImage.pvt_Sections.ByIndex.size()] = §ion_p[j]; - NewImage.pvt_Sections.ByMapAddress[section_p[j].addr] = §ion_p[j]; - NewImage.pvt_Sections.ByFileOffset[section_p[j].offset] = §ion_p[j]; + [[nodiscard]] + const section_64* X64ImageInterpreter::ImageSection(size_t Index) const { + if (Index < m_Sections.size()) { + return m_Sections[Index]; + } else { + throw ARL::IndexError(__FILE__, __LINE__, "X64ImageInterpreter: Index is out of range."); + } + } + + [[nodiscard]] + const section_64* X64ImageInterpreter::ImageSection(const char* SegmentName, const char* SectionName) const { + for (const auto& segment : m_Segments) { + if (strncmp(SegmentName, segment->segname, sizeof(segment->segname)) == 0) { + auto section = reinterpret_cast(segment + 1); + + for (uint32_t i = 0; i < segment->nsects; ++i) { + if (strncmp(SectionName, section[i].sectname, sizeof(section[i].sectname)) == 0) { + return §ion[i]; + } } break; } - case LC_DYSYMTAB: { - NewImage.pvt_DynamicSymbol.SegmentCommand = reinterpret_cast(cmd_p); - break; - } - case LC_SYMTAB: { - NewImage.pvt_Symbol.SegmentCommand = reinterpret_cast(cmd_p); - NewImage.pvt_Symbol.StringTable = NewImage.ImageOffset(NewImage.pvt_Symbol.SegmentCommand->stroff); - NewImage.pvt_Symbol.SymbolTable = NewImage.ImageOffset(NewImage.pvt_Symbol.SegmentCommand->symoff); - break; - } - case LC_DYLD_INFO_ONLY: { // NOLINT - NewImage.pvt_DynamicLoaderInfoOnly.SegmentCommand = reinterpret_cast(cmd_p); - } - default: - break; } - cmd_p = reinterpret_cast( - reinterpret_cast(cmd_p) + cmd_p->cmdsize - ); + throw ARL::KeyError(__FILE__, __LINE__, "X64ImageInterpreter: section is not found."); } - return NewImage; -} - -[[nodiscard]] -size_t X64ImageInterpreter::NumberOfSections() const noexcept { - return pvt_Sections.ByIndex.size(); -} - -[[nodiscard]] -section_64* X64ImageInterpreter::ImageSection(size_t Index) const { - auto it = pvt_Sections.ByIndex.find(Index); - if (it != pvt_Sections.ByIndex.cend()) { - return it->second; - } else { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "Section is not found."); - } -} - -[[nodiscard]] -section_64* X64ImageInterpreter::ImageSection(const char* SegmentName, const char* SectionName) const { - for (auto segcmd_p : pvt_SegmentCommands) { - if (strncmp(SegmentName, segcmd_p->segname, sizeof(segcmd_p->segname)) == 0) { - auto sec_p = reinterpret_cast(segcmd_p + 1); - - for (uint32_t i = 0; i < segcmd_p->nsects; ++i) { - if (strncmp(SectionName, sec_p[i].sectname, sizeof(sec_p[i].sectname)) == 0) - return &sec_p[i]; - } - - break; + [[nodiscard]] + const section_64* X64ImageInterpreter::ImageSectionFromOffset(X64ImageOffset Offset) const { + auto it = m_SectionsOffsetMap.upper_bound(Offset); + if (it != m_SectionsOffsetMap.begin() && (--it, it->first <= Offset && Offset < it->first + it->second->size)) { + return it->second; + } else { + throw ARL::KeyError(__FILE__, __LINE__, "X64ImageInterpreter: section is not found."); } } - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "Section is not found."); -} - -[[nodiscard]] -section_64* X64ImageInterpreter::ImageSectionByOffset(uint32_t Offset) const { - for (const auto& it : pvt_Sections.ByFileOffset) { - if (it.first <= Offset && Offset < it.first + it.second->size) { - return it.second; + [[nodiscard]] + const section_64* X64ImageInterpreter::ImageSectionFromRva(X64ImageAddress Rva) const { + auto it = m_SectionsAddressMap.upper_bound(Rva); + if (it != m_SectionsAddressMap.begin() && (--it, it->first <= Rva && Rva < it->first + it->second->size)) { + return it->second; + } else { + throw ARL::KeyError(__FILE__, __LINE__, "X64ImageInterpreter: section is not found."); } } - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "Section is not found."); -} - -[[nodiscard]] -section_64* X64ImageInterpreter::ImageSectionByRva(uint64_t Rva) const { - for (const auto& it : pvt_Sections.ByMapAddress) { - if (it.first <= Rva && Rva < it.first + it.second->size) { - return it.second; - } + [[nodiscard]] + X64ImageAddress X64ImageInterpreter::ConvertOffsetToRva(X64ImageOffset Offset) const { + auto section = ImageSectionFromOffset(Offset); + return section->addr + static_cast(Offset - section->offset); + } + + [[nodiscard]] + X64ImageOffset X64ImageInterpreter::ConvertRvaToOffset(X64ImageAddress Rva) const { + auto section = ImageSectionFromRva(Rva); + return section->offset + static_cast(Rva - section->addr); } - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "Section is not found."); } - -[[nodiscard]] -uint64_t X64ImageInterpreter::OffsetToRva(uint32_t Offset) const { - auto section = ImageSectionByOffset(Offset); - return section->addr + (Offset - section->offset); -} - -[[nodiscard]] -uint32_t X64ImageInterpreter::RvaToOffset(uint64_t Rva) const { - auto section = ImageSectionByRva(Rva); - return section->offset + (Rva - section->addr); -} - -[[nodiscard]] -nlist_64* X64ImageInterpreter::ImageSymbolTable() const noexcept { - return pvt_Symbol.SymbolTable; -} - - diff --git a/navicat-patcher/X64ImageInterpreter.hpp b/navicat-patcher/X64ImageInterpreter.hpp index 4bfcb1e..d347e71 100644 --- a/navicat-patcher/X64ImageInterpreter.hpp +++ b/navicat-patcher/X64ImageInterpreter.hpp @@ -1,199 +1,222 @@ #pragma once -#include // NOLINT #include #include +#include #include #include -#include "../common/Exception.hpp" +#include "Exception.hpp" +#include "ExceptionGeneric.hpp" +#include "MemoryAccess.hpp" -class X64ImageInterpreter { -public: - static constexpr uint64_t InvalidAddress = static_cast(-1); - static constexpr uint32_t InvalidOffset = static_cast(-1); -private: +namespace nkg { - mach_header_64* pvt_MachHeader; - std::vector pvt_SegmentCommands; + using X64ImageAddress = decltype(section_64::addr); + using X64ImageOffset = decltype(section_64::offset); - struct { - std::map ByIndex; - std::map ByMapAddress; - std::map ByFileOffset; - } pvt_Sections; + class X64ImageInterpreter { + private: - struct { - dysymtab_command* SegmentCommand; - } pvt_DynamicSymbol; + size_t m_MachOSize; + const mach_header_64* m_MachOHeader; + std::vector m_Segments; + std::vector m_Sections; + std::map m_SectionsAddressMap; + std::map m_SectionsOffsetMap; + struct { + const dysymtab_command* dysymtab; + const symtab_command* symtab; + const dyld_info_command* dyld_info; + } m_SpecialLoadCommands; - struct { - symtab_command* SegmentCommand; - char* StringTable; - nlist_64* SymbolTable; - } pvt_Symbol; + X64ImageInterpreter() : + m_MachOSize(0), + m_MachOHeader(nullptr), + m_SpecialLoadCommands{} {} - struct { - dyld_info_command* SegmentCommand; - } pvt_DynamicLoaderInfoOnly; + public: - X64ImageInterpreter() : - pvt_MachHeader(nullptr), - pvt_DynamicSymbol{}, - pvt_Symbol{}, - pvt_DynamicLoaderInfoOnly{} {} + [[nodiscard]] + static X64ImageInterpreter Parse(const void* lpImage, size_t cbImage); -public: - - [[nodiscard]] - static X64ImageInterpreter Parse(void* ImageBase); - - template - [[nodiscard]] - __ReturnType ImageBase() const noexcept { - static_assert(std::is_pointer_v<__ReturnType>); - return reinterpret_cast<__ReturnType>(pvt_MachHeader); - } - - template - [[nodiscard]] - __ReturnType ImageOffset(size_t Offset) const noexcept { - static_assert(std::is_pointer_v<__ReturnType>); - return reinterpret_cast<__ReturnType>( - reinterpret_cast(pvt_MachHeader) + Offset - ); - } - - template - [[nodiscard]] - auto CommandOf() const noexcept { - if constexpr (__CommandMacro == LC_DYSYMTAB) { - return pvt_DynamicSymbol.SegmentCommand; - } else if constexpr (__CommandMacro == LC_SYMTAB) { - return pvt_Symbol.SegmentCommand; - } else if constexpr (__CommandMacro == LC_DYLD_INFO_ONLY) { // NOLINT - return pvt_DynamicLoaderInfoOnly.SegmentCommand; - } else { - return nullptr; + template + [[nodiscard]] + __ReturnType ImageBase() const noexcept { + static_assert(std::is_pointer_v<__ReturnType>); + return reinterpret_cast<__ReturnType>( + const_cast(m_MachOHeader) + ); } - } - [[nodiscard]] - size_t NumberOfSections() const noexcept; - - [[nodiscard]] - section_64* ImageSection(size_t Index) const; - - [[nodiscard]] - section_64* ImageSection(const char* SegmentName, const char* SectionName) const; - - [[nodiscard]] - section_64* ImageSectionByOffset(uint32_t Offset) const; - - [[nodiscard]] - section_64* ImageSectionByRva(uint64_t Rva) const; - - template - [[nodiscard]] - __ReturnType SectionView(size_t Index) const { - auto Section = ImageSection(Index); - return ImageOffset<__ReturnType>(Section->offset); - } - - template - [[nodiscard]] - __ReturnType SectionView(const char* SegmentName, const char* SectionName) const { - auto Section = ImageSection(SegmentName, SectionName); - return ImageOffset<__ReturnType>(Section->offset); - } - - template - [[nodiscard]] - __ReturnType SectionView(section_64* Section) const { - return ImageOffset<__ReturnType>(Section->offset); - } - - template - [[nodiscard]] - __ReturnType SearchSection(size_t Index, __Hint&& Hint) const { - return SearchSection<__ReturnType>(ImageSection(Index), std::forward<__Hint>(Hint)); - } - - template - [[nodiscard]] - __ReturnType SearchSection(const char* SegmentName, const char* SectionName, __Hint&& Hint) const { - return SearchSection<__ReturnType>(ImageSection(SegmentName, SectionName), std::forward<__Hint>(Hint)); - } - - template - [[nodiscard]] - __ReturnType SearchSection(section_64* Section, __Hint&& Hint) const { - static_assert(std::is_pointer_v<__ReturnType>); - - auto begin = SectionView(Section); - auto end = begin + Section->size; - - for (; begin < end; ++begin) { - if (Hint(begin) == true) { - return reinterpret_cast<__ReturnType>(const_cast(begin)); + template + [[nodiscard]] + __ReturnType ImageOffset(size_t Offset) const { + if (Offset < m_MachOSize) { + return ARL::AddressOffsetWithCast<__ReturnType>(m_MachOHeader, Offset); + } else { + throw ARL::OverflowError(__FILE__, __LINE__, "X64ImageInterpreter: out of range."); } } - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "Data is not found."); - } + [[nodiscard]] + size_t ImageSize() const noexcept { + return m_MachOSize; + } - template - [[nodiscard]] - uint32_t SearchSectionOffset(size_t Index, __Hint&& Hint) const { - return SearchSection(Index, std::forward<__Hint>(Hint)) - ImageBase(); - } + [[nodiscard]] + size_t NumberOfSegmentCommands() const noexcept; - template - [[nodiscard]] - uint32_t SearchSectionOffset(const char* SegmentName, const char* SectionName, __Hint&& Hint) const { - return SearchSection(SegmentName, SectionName, std::forward<__Hint>(Hint)) - ImageBase(); - } + [[nodiscard]] + size_t NumberOfSections() const noexcept; - template - [[nodiscard]] - uint32_t SearchSectionOffset(section_64* Section, __Hint&& Hint) const { - return SearchSection(Section, std::forward<__Hint>(Hint)) - ImageBase(); - } + [[nodiscard]] + const section_64* ImageSection(size_t Index) const; - template - [[nodiscard]] - uint64_t SearchSectionRva(size_t Index, __Hint&& Hint) const { - auto Section = ImageSection(Index); - auto Offset = SearchSection(Section, std::forward<__Hint>(Hint)) - SectionView(Section); - return Section->addr + Offset; - } + [[nodiscard]] + const section_64* ImageSection(const char* SegmentName, const char* SectionName) const; - template - [[nodiscard]] - uint64_t SearchSectionRva(const char* SegmentName, const char* SectionName, __Hint&& Hint) const { - auto Section = ImageSection(SegmentName, SectionName); - auto Offset = SearchSection(Section, std::forward<__Hint>(Hint)) - SectionView(Section); - return Section->addr + Offset; - } + [[nodiscard]] + const section_64* ImageSectionFromOffset(X64ImageOffset Offset) const; - template - [[nodiscard]] - uint64_t SearchSectionRva(section_64* Section, __Hint&& Hint) const { - auto Offset = SearchSection(Section, std::forward<__Hint>(Hint)) - SectionView(Section); - return Section->addr + Offset; - } + [[nodiscard]] + const section_64* ImageSectionFromRva(X64ImageAddress Rva) const; - [[nodiscard]] - uint64_t OffsetToRva(uint32_t Offset) const; - [[nodiscard]] - uint32_t RvaToOffset(uint64_t Address) const; - [[nodiscard]] - nlist_64* ImageSymbolTable() const noexcept; + template + [[nodiscard]] + __ReturnType ImageSectionView(const section_64* Section) const noexcept { + return ImageOffset<__ReturnType>(Section->offset); + } - [[nodiscard]] - char* LookupStringTable(size_t Offset) const noexcept { - return pvt_Symbol.StringTable + Offset; - } -}; + template + [[nodiscard]] + __ReturnType ImageSectionView(size_t Index) const { + return ImageSectionView<__ReturnType>(ImageSection(Index)); + } + template + [[nodiscard]] + __ReturnType ImageSectionView(const char* SegmentName, const char* SectionName) const { + return ImageSectionView<__ReturnType>(ImageSection(SegmentName, SectionName)); + } + + template + [[nodiscard]] + auto SpecialLoadCommand() const noexcept { + if constexpr (__CommandMacro == LC_DYSYMTAB) { + return m_SpecialLoadCommands.dysymtab; + } else if constexpr (__CommandMacro == LC_SYMTAB) { + return m_SpecialLoadCommands.symtab; + } else if constexpr (__CommandMacro == LC_DYLD_INFO_ONLY) { + return m_SpecialLoadCommands.dyld_info; + } else { +#pragma clang diagnostic push +#pragma GCC diagnostic ignored "-Wtautological-compare" + constexpr bool always_false = __CommandMacro != __CommandMacro; +#pragma clang diagnostic pop + static_assert(always_false); + + } + } + + template + [[nodiscard]] + __ReturnType SearchSection(const section_64* Section, __HintType&& Hint) const { + static_assert(std::is_pointer_v<__ReturnType>); + + auto base = ImageSectionView(Section); + + for (decltype(section_64::size) i = 0; i < Section->size; ++i) { + if (Hint(base, i, Section->size) == true) { + return ARL::AddressOffsetWithCast<__ReturnType>(base, i); + } + } + + return nullptr; + } + + template + [[nodiscard]] + __ReturnType SearchSection(const section_64* Section, size_t Offset, __HintType&& Hint) const { + static_assert(std::is_pointer_v<__ReturnType>); + + auto base = ImageSectionView(Section); + + for (decltype(section_64::size) i = Offset; i < Section->size; ++i) { + if (Hint(base, i, Section->size) == true) { + return ARL::AddressOffsetWithCast<__ReturnType>(base, i); + } + } + + return nullptr; + } + + template + [[nodiscard]] + __ReturnType SearchSection(size_t Index, __HintType&& Hint) const { + return SearchSection<__ReturnType>(ImageSection(Index), std::forward<__HintType>(Hint)); + } + + template + [[nodiscard]] + __ReturnType SearchSection(size_t Index, size_t Offset, __HintType&& Hint) const { + return SearchSection<__ReturnType>(ImageSection(Index), Offset, std::forward<__HintType>(Hint)); + } + + template + [[nodiscard]] + __ReturnType SearchSection(const char* SegmentName, const char* SectionName, __HintType&& Hint) const { + return SearchSection<__ReturnType>(ImageSection(SegmentName, SectionName), std::forward<__HintType>(Hint)); + } + + template + [[nodiscard]] + __ReturnType SearchSection(const char* SegmentName, const char* SectionName, size_t Offset, __HintType&& Hint) const { + return SearchSection<__ReturnType>(ImageSection(SegmentName, SectionName), Offset, std::forward<__HintType>(Hint)); + } + + + + [[nodiscard]] + X64ImageAddress ConvertOffsetToRva(X64ImageOffset Offset) const; + + template + [[nodiscard]] + __ReturnType ConvertOffsetToPtr(X64ImageOffset Offset) const { + return ImageOffset<__ReturnType>(Offset); + } + + [[nodiscard]] + X64ImageOffset ConvertRvaToOffset(X64ImageAddress Address) const; + + template + [[nodiscard]] + __ReturnType ConvertRvaToPtr(X64ImageAddress Rva) const { + return ConvertOffsetToPtr<__ReturnType>(ConvertRvaToOffset(Rva)); + } + + template + [[nodiscard]] + X64ImageAddress ConvertPtrToRva(__PtrType Ptr) const { + auto offset = ARL::AddressDelta(Ptr, m_MachOHeader); + if (offset < m_MachOSize) { + return ConvertOffsetToRva(offset); + } else { + throw ARL::OverflowError(__FILE__, __LINE__, "X64ImageInterpreter: out of range."); + } + } + + template + [[nodiscard]] + X64ImageAddress ConvertPtrToOffset(__PtrType Ptr) const { + auto offset = ARL::AddressDelta(Ptr, m_MachOHeader); + if (offset < m_MachOSize) { + return offset; + } else { + throw ARL::OverflowError(__FILE__, __LINE__, "X64ImageInterpreter: out of range."); + } + } + + }; + +} diff --git a/navicat-patcher/main.cpp b/navicat-patcher/main.cpp index b162bc0..3aaaccd 100644 --- a/navicat-patcher/main.cpp +++ b/navicat-patcher/main.cpp @@ -4,254 +4,381 @@ #include #include -#include "../common/ExceptionSystem.hpp" -#include "../common/ResourceOwned.hpp" +#include "ExceptionSystem.hpp" +#include "ResourceWrapper.hpp" #include "ResourceTraitsUnix.hpp" #include "PatchSolutions.hpp" +#include "Misc.hpp" -static void Welcome() { - puts("***************************************************"); - puts("* Navicat Patcher by @DoubleLabyrinth *"); - puts("* Version: 4.0 *"); - puts("***************************************************"); +static void Welcome(bool bWait) { + puts("**********************************************************"); + puts("* Navicat Patcher (macOS) by @DoubleLabyrinth *"); + puts("* Version: 5.0 *"); + puts("**********************************************************"); puts(""); - puts("Press Enter to continue or Ctrl + C to abort."); - getchar(); + if (bWait) { + puts("Press Enter to continue or Ctrl + C to abort."); + getchar(); + } } static void Help() { - puts("***************************************************"); - puts("* Navicat Patcher by @DoubleLabyrinth *"); - puts("* Version: 4.0 *"); - puts("***************************************************"); - puts(""); puts("Usage:"); - puts(" navicat-patcher [RSA-2048 Private Key File]"); + puts(" navicat-patcher [--dry-run] [RSA-2048 Private Key File]"); puts(""); - puts(" Path to `Navicat Premium.app`."); - puts(" Example:"); - puts(" /Applications/Navicat\\ Premium.app/"); - puts(" This parameter must be specified."); + puts(" [--dry-run] Run patcher without applying any patches."); + puts(" This parameter is optional."); puts(""); - puts(" [RSA-2048 Private Key File] Path to a PEM-format RSA-2048 private key file."); - puts(" This parameter is optional."); + puts(" Path to `Navicat Premium.app`."); + puts(" Example:"); + puts(" /Applications/Navicat\\ Premium.app/"); + puts(" This parameter must be specified."); + puts(""); + puts(" [RSA-2048 Private Key File] Path to a PEM-format RSA-2048 private key file."); + puts(" This parameter is optional."); puts(""); } -static std::string GetNavicatVersion(const char* AppPath) { - ResourceOwned hInfoPlist(FileHandleTraits{}); - ResourceOwned InfoPlist(CppObjectTraits{}); +static bool ParseCommandLine(int argc, char* argv[], bool& bDryrun, std::string& szInstallPath, std::string& szKeyFilePath) { + if (argc == 2) { + bDryrun = false; + szInstallPath = argv[1]; + szKeyFilePath.clear(); + return true; + } else if (argc == 3) { + if (strcasecmp(argv[1], "--dry-run") == 0) { + bDryrun = true; + szInstallPath = argv[2]; + szKeyFilePath.clear(); + return true; + } else { + bDryrun = false; + szInstallPath = argv[1]; + szKeyFilePath = argv[2]; + return true; + } + } else if (argc == 4) { + if (strcasecmp(argv[1], "--dry-run") == 0) { + bDryrun = true; + szInstallPath = argv[2]; + szKeyFilePath = argv[3]; + return true; + } else { + return false; + } + } else { + return false; + } +} + +static std::string GetNavicatVersion(std::string_view AppPath) { + ARL::ResourceWrapper hInfoPlist{ ARL::ResourceTraits::FileDescriptor{} }; + ARL::ResourceWrapper lpInfoPlist{ ARL::ResourceTraits::CppObject{} }; hInfoPlist.TakeOver(open((std::string(AppPath) + "/Contents/Info.plist").c_str(), O_RDONLY)); if (hInfoPlist.IsValid() == false) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::SystemError(__FILE__, __LINE__, errno, "Failed to open Contents/Info.plist."); + throw ARL::SystemError(__FILE__, __LINE__, errno, "Failed to open Contents/Info.plist."); } struct stat statInfoPlist = {}; if (fstat(hInfoPlist, &statInfoPlist) != 0) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::SystemError(__FILE__, __LINE__, errno, "Failed to get file size of Contents/Info.plist."); + throw ARL::SystemError(__FILE__, __LINE__, errno, "Failed to get file size of Contents/Info.plist."); } std::string contentInfoPlist(statInfoPlist.st_size, '\x00'); if (read(hInfoPlist, contentInfoPlist.data(), contentInfoPlist.size()) != contentInfoPlist.size()) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::SystemError(__FILE__, __LINE__, errno, "Failed to read Contents/Info.plist."); + throw ARL::SystemError(__FILE__, __LINE__, errno, "Failed to read Contents/Info.plist."); } - InfoPlist.TakeOver(dynamic_cast(PList::Structure::FromXml(contentInfoPlist))); - if (InfoPlist.IsValid() == false) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "Failed to parse Contents/Info.plist."); + lpInfoPlist.TakeOver(dynamic_cast(PList::Structure::FromXml(contentInfoPlist))); + if (lpInfoPlist.IsValid() == false) { + throw ARL::Exception(__FILE__, __LINE__, "Failed to parse Contents/Info.plist."); } - auto kv = InfoPlist->Find("CFBundleShortVersionString"); - if (kv == InfoPlist->End()) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "Cannot find CFBundleShortVersionString in Contents/Info.plist."); + auto key_value = lpInfoPlist->Find("CFBundleShortVersionString"); + if (key_value == lpInfoPlist->End()) { + throw ARL::Exception(__FILE__, __LINE__, "Cannot find CFBundleShortVersionString in Contents/Info.plist."); } - if (kv->second->GetType() == PLIST_STRING) { - return dynamic_cast(kv->second)->GetValue(); + if (key_value->second->GetType() == PLIST_STRING) { + return dynamic_cast(key_value->second)->GetValue(); } else { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "Failed to get Navicat version."); + throw ARL::Exception(__FILE__, __LINE__, "Type check failed for CFBundleShortVersionString."); } } -static void LoadKey(RSACipher& RsaCipher, const char* RsaKeyFileName, - PatchSolution* lpSolution0, - PatchSolution* lpSolution1, - PatchSolution* lpSolution2) { - if (RsaKeyFileName) { - RsaCipher.ImportKeyFromFile(RsaKeyFileName); +static void LoadKey(nkg::RSACipher& Cipher, std::string_view szKeyFileName, + nkg::PatchSolution* lpSolution0, + nkg::PatchSolution* lpSolution1, + nkg::PatchSolution* lpSolution2) { + if (szKeyFileName.empty() == false) { + printf("[*] Import RSA-2048 key from %s\n", szKeyFileName.data()); - if ((lpSolution0 && !lpSolution0->CheckKey(RsaCipher)) || - (lpSolution1 && !lpSolution1->CheckKey(RsaCipher)) || - (lpSolution2 && !lpSolution2->CheckKey(RsaCipher))) + Cipher.ImportKeyFromFile(szKeyFileName); + + if ((lpSolution0 && lpSolution0->CheckKey(Cipher) == false) || + (lpSolution1 && lpSolution1->CheckKey(Cipher) == false) || + (lpSolution2 && lpSolution2->CheckKey(Cipher) == false)) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::Exception(__FILE__, __LINE__, "The RSA private key you provide cannot be used."); + throw ARL::Exception(__FILE__, __LINE__, "The RSA private key you provide cannot be used."); } } else { - puts(""); puts("[*] Generating new RSA private key, it may take a long time..."); do { - RsaCipher.GenerateKey(2048); - } while ((lpSolution0 && !lpSolution0->CheckKey(RsaCipher)) || - (lpSolution1 && !lpSolution1->CheckKey(RsaCipher)) || - (lpSolution2 && !lpSolution2->CheckKey(RsaCipher))); // re-generate RSA key if CheckKey return false - - RsaCipher.ExportKeyToFile("RegPrivateKey.pem"); - - puts("[+] New RSA private key has been saved to RegPrivateKey.pem."); + Cipher.GenerateKey(2048); + } while ((lpSolution0 && lpSolution0->CheckKey(Cipher) == false) || + (lpSolution1 && lpSolution1->CheckKey(Cipher) == false) || + (lpSolution2 && lpSolution2->CheckKey(Cipher) == false)); // re-generate RSA key if CheckKey return false } - std::string PublicKeyPEM = RsaCipher.ExportKeyString(); - puts(""); - puts("[*] Your RSA public key:"); - puts(PublicKeyPEM.c_str()); + printf("[*] Your RSA private key:\n"); + printf(" %s\n", + [&Cipher]() -> std::string { + auto szPrivateKey = Cipher.ExportKeyString(); + for (size_t pos = 0; (pos = szPrivateKey.find('\n', pos)) != std::string::npos; pos += strlen("\n ")) { + szPrivateKey.replace(pos, 1, "\n "); + } + return szPrivateKey; + }().c_str() + ); + + printf("[*] Your RSA public key:\n"); + printf(" %s\n", + [&Cipher]() -> std::string { + auto szPublicKey = Cipher.ExportKeyString(); + for (size_t pos = 0; (pos = szPublicKey.find('\n', pos)) != std::string::npos; pos += strlen("\n ")) { + szPublicKey.replace(pos, 1, "\n "); + } + return szPublicKey; + }().c_str() + ); + + printf("\n"); } int main(int argc, char* argv[]) { - if (argc != 2 && argc != 3) { + bool bDryrun; + std::string szInstallPath; + std::string szKeyFilePath; + + if (ParseCommandLine(argc, argv, bDryrun, szInstallPath, szKeyFilePath) == false) { + Welcome(false); Help(); return -1; } else { - Welcome(); + Welcome(true); try { - RSACipher RsaCipher; - ResourceOwned Solution0(CppObjectTraits{}); - ResourceOwned Solution1(CppObjectTraits{}); - ResourceOwned Solution2(CppObjectTraits{}); - - ResourceOwned hMainApp(FileHandleTraits{}, open((std::string(argv[1]) + "/Contents/MacOS/Navicat Premium").c_str(), O_RDWR)); - if (hMainApp.IsValid() == false) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::SystemError(__FILE__, __LINE__, errno, "open failed."); + if (nkg::Misc::FsIsDirectory(szInstallPath) == false) { + throw ARL::Exception(__FILE__, __LINE__, "Navicat installation path doesn't point to a directory.") + .PushHint("Are you sure the path you specified is correct?") + .PushFormatHint("The path you specified: %s", szInstallPath.c_str()); } - struct stat statMainApp = {}; - if (fstat(hMainApp, &statMainApp) != 0) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::SystemError(__FILE__, __LINE__, errno, "fstat failed."); + if (szKeyFilePath.empty() == false && nkg::Misc::FsIsFile(szKeyFilePath) == false) { + throw ARL::Exception(__FILE__, __LINE__, "RSA key file path doesn't point to a file.") + .PushHint("Are you sure the path you specified is correct?") + .PushFormatHint("The path you specified: %s", szKeyFilePath.c_str()); } - ResourceOwned lpMainApp(MapViewTraits{}, - mmap(nullptr, static_cast(statMainApp.st_size), PROT_READ | PROT_WRITE, MAP_SHARED, hMainApp, 0), - [&statMainApp](void* p) { munmap(p, statMainApp.st_size); } - ); - if (lpMainApp.IsValid() == false) { - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::SystemError(__FILE__, __LINE__, errno, "mmap failed."); + while (szInstallPath.back() == '/') { + szInstallPath.pop_back(); } - int Ver0, Ver1, Ver2; - if (sscanf(GetNavicatVersion(argv[1]).c_str(), "%d.%d.%d", &Ver0, &Ver1, &Ver2) != 3) { // NOLINT - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::SystemError(__FILE__, __LINE__, errno, "Failed to get version of Navicat."); - } + nkg::RSACipher Cipher; + ARL::ResourceWrapper lpSolution0{ ARL::ResourceTraits::CppObject{} }; + ARL::ResourceWrapper lpSolution1{ ARL::ResourceTraits::CppObject{} }; + ARL::ResourceWrapper lpSolution2{ ARL::ResourceTraits::CppObject{} }; - X64ImageInterpreter MainApp = X64ImageInterpreter::Parse(lpMainApp); + std::string main_path; + ARL::ResourceWrapper main_fd{ ARL::ResourceTraits::FileDescriptor{} }; + ARL::ResourceWrapper main_stat{ ARL::ResourceTraits::CppObject{} }; + ARL::ResourceWrapperEx main_mmap{ ARL::ResourceTraits::MapView{}, [&main_stat](void* p) { + if (munmap(p, main_stat->st_size) < 0) { + throw ARL::SystemError(__FILE__, __LINE__, errno, "munmap failed."); + } + } }; + ARL::ResourceWrapper main_interpreter{ ARL::ResourceTraits::CppObject{} }; - printf("[*] Your Navicat version: %d.%d.%d\n", Ver0, Ver1, Ver2); - printf("\n"); - - Solution0.TakeOver(new PatchSolution0(MainApp)); - Solution1.TakeOver(new PatchSolution1(MainApp)); - Solution2.TakeOver(new PatchSolution2(MainApp)); - - if (Solution0->FindPatchOffset() == false) { - Solution0.Release(); - } - if (Solution1->FindPatchOffset() == false) { - Solution1.Release(); - } - if (Solution2->FindPatchOffset() == false) { - Solution2.Release(); + // + // try open "/Contents/MacOS/Navicat Premium" + // + main_path = szInstallPath + "/Contents/MacOS/Navicat Premium"; + main_fd.TakeOver(open(main_path.c_str(), O_RDWR)); + if (main_fd.IsValid()) { + printf("[+] Try to open \"%s\" ... Ok!\n", "Contents/MacOS/Navicat Premium"); + } else { + if (errno == ENOENT) { + printf("[-] Try to open \"%s\" ... Not found!\n", "Contents/MacOS/Navicat Premium"); + } else { + throw ARL::SystemError(__FILE__, __LINE__, errno, "open failed."); + } } // - // Begin strategies by different Navicat versions + // try map "/Contents/MacOS/Navicat Premium" // - if (Ver0 < 12) { // ver < 12.0.0 - // NOLINTNEXTLINE: allow exceptions that is not derived from std::exception - throw nkg::SystemError(__FILE__, __LINE__, errno, "Unsupported version of Navicat."); - } else if (Ver0 == 12 && Ver1 == 0 && Ver2 < 24) { // ver < 12.0.24 - std::string path(argv[1]); - while(path.back() == '/') { - path.pop_back(); + if (main_fd.IsValid()) { + main_stat.TakeOver(new struct stat()); + if (fstat(main_fd, main_stat) != 0) { + throw ARL::SystemError(__FILE__, __LINE__, errno, "fstat failed."); } - printf("[*] Your Navicat version is < 12.0.24. So there would be nothing patched.\n"); - printf(" Just use `openssl` to generate `RegPrivateKey.pem` and `rpk` file:\n"); - printf(" openssl genrsa -out RegPrivateKey.pem 2048\n"); - printf(" openssl rsa -in RegPrivateKey.pem -pubout -out rpk\n"); - printf(" and replace `%s/Contents/Resources/rpk` with the `rpk` file you just generated.\n", path.c_str()); - printf("\n"); - - return 0; - } else if (Ver0 == 12 && (Ver1 == 0 || (Ver1 == 1 && Ver2 < 14))) { // 12.0.24 <= ver && ver < 12.1.14 - // In this case, Solution0 must be applied - if (Solution0.IsValid() == false) { - puts("[-] Patch abort. None of PatchSolutions will be applied."); - puts(" Are you sure your Navicat has not been patched before?"); - return -1; + main_mmap.TakeOver(mmap(nullptr, main_stat->st_size, PROT_READ | PROT_WRITE, MAP_SHARED, main_fd, 0)); + if (main_mmap.IsValid() == false) { + throw ARL::SystemError(__FILE__, __LINE__, errno, "mmap failed."); } - } else if (Ver0 == 12 && Ver1 == 1 && Ver2 == 14) { // ver == 12.1.14 - // In this case, Solution0 and Solution1 must be applied - if ((Solution0.IsValid() && Solution1.IsValid()) == false) { - puts("[-] Patch abort. None of PatchSolutions will be applied."); - puts(" Are you sure your Navicat has not been patched before?"); - return -1; - } - } else { // ver > 12.1.14 - // In this case, Solution0 and Solution2 must be applied - if ((Solution0.IsValid() && Solution2.IsValid()) == false) { - puts("[-] Patch abort. None of PatchSolutions will be applied."); - puts(" Are you sure your Navicat has not been patched before?"); - return -1; - } - } - // - // End strategies by different Navicat versions - // - LoadKey(RsaCipher, argc == 3 ? argv[2] : nullptr, Solution0, Solution1, Solution2); + main_interpreter.TakeOver( + new nkg::X64ImageInterpreter(nkg::X64ImageInterpreter::Parse(main_mmap, main_stat->st_size)) + ); - if (Solution0.IsValid()) { - Solution0->MakePatch(RsaCipher); + lpSolution0.TakeOver( + new nkg::PatchSolution0(*main_interpreter.Get()) + ); + lpSolution1.TakeOver( + new nkg::PatchSolution1(*main_interpreter.Get()) + ); + lpSolution2.TakeOver( + new nkg::PatchSolution2(*main_interpreter.Get()) + ); } - if (Solution1.IsValid()) { - Solution1->MakePatch(RsaCipher); - } - if (Solution2.IsValid()) { - Solution2->MakePatch(RsaCipher); - } - - if (Solution0.IsValid()) - puts("[+] PatchSolution0 has been applied."); - if (Solution1.IsValid()) - puts("[+] PatchSolution1 has been applied."); - if (Solution2.IsValid()) - puts("[+] PatchSolution2 has been applied."); puts(""); - puts("**************************************************************"); - puts("* Patch has been done successfully. Have fun and enjoy~~ *"); - puts("* DO NOT FORGET TO SIGN NAVICAT BY YOUR CERTIFICATE!!! *"); - puts("**************************************************************"); + + if (lpSolution0.IsValid() && lpSolution0->FindPatchOffset() == false) { + lpSolution0.Release(); + } + if (lpSolution1.IsValid() && lpSolution1->FindPatchOffset() == false) { + lpSolution1.Release(); + } + if (lpSolution2.IsValid() && lpSolution2->FindPatchOffset() == false) { + lpSolution2.Release(); + } + + if (int Ver0, Ver1, Ver2; sscanf(GetNavicatVersion(szInstallPath).c_str(), "%d.%d.%d", &Ver0, &Ver1, &Ver2) == 3) { + printf("\n"); + printf("[*] Your Navicat version: %d.%d.%d\n", Ver0, Ver1, Ver2); + printf("\n"); + + // + // Begin strategies by different Navicat versions + // + if (Ver0 < 12) { // ver < 12.0.0 + throw ARL::SystemError(__FILE__, __LINE__, errno, "Unsupported version of Navicat."); + } else if (Ver0 == 12 && Ver1 == 0 && Ver2 < 24) { // ver < 12.0.24 + printf("[*] Your Navicat version is < 12.0.24. So there would be nothing patched.\n"); + printf(" Just use `openssl` to generate `RegPrivateKey.pem` and `rpk` file:\n"); + printf(" openssl genrsa -out RegPrivateKey.pem 2048\n"); + printf(" openssl rsa -in RegPrivateKey.pem -pubout -out rpk\n"); + printf(" and replace `%s/Contents/Resources/rpk` with the `rpk` file you just generated.\n", szInstallPath.c_str()); + printf("\n"); + return 0; + } else if (Ver0 == 12 && (Ver1 == 0 || (Ver1 == 1 && Ver2 < 14))) { // 12.0.24 <= ver && ver < 12.1.14 + // In this case, Solution0 must be applied + if (lpSolution0.IsValid() == false) { + puts("[-] Patch abort. None of PatchSolutions will be applied."); + puts(" Are you sure your Navicat has not been patched/modified before?"); + return -1; + } + } else if (Ver0 == 12 && Ver1 == 1 && Ver2 == 14) { // ver == 12.1.14 + // In this case, Solution0 and Solution1 must be applied + if ((lpSolution0.IsValid() && lpSolution1.IsValid()) == false) { + puts("[-] Patch abort. None of PatchSolutions will be applied."); + puts(" Are you sure your Navicat has not been patched/modified before?"); + return -1; + } + } else { // ver > 12.1.14 + // In this case, Solution0 and Solution2 must be applied + if ((lpSolution0.IsValid() && lpSolution2.IsValid()) == false) { + puts("[-] Patch abort. None of PatchSolutions will be applied."); + puts(" Are you sure your Navicat has not been patched/modified before?"); + return -1; + } + } + // + // End strategies by different Navicat versions + // + } else { + throw ARL::SystemError(__FILE__, __LINE__, errno, "Failed to get version of Navicat."); + } + + // + // Make sure that there is one patch solution at least existing. + // + if (lpSolution0.IsValid() == false && lpSolution1.IsValid() == false && lpSolution2.IsValid() == false) { + throw ARL::Exception(__FILE__, __LINE__, "No patch applied. Patch abort!") + .PushHint("Are you sure your Navicat has not been patched/modified before?"); + } + + LoadKey(Cipher, szKeyFilePath, lpSolution0, lpSolution1, lpSolution2); + + if (bDryrun == false) { + // + // Save private key if not given + // + if (szKeyFilePath.empty()) { + Cipher.ExportKeyToFile("RegPrivateKey.pem"); + } + + // + // Making patch. No way to go back here :-) + // + if (lpSolution0.IsValid()) { + lpSolution0->MakePatch(Cipher); + } + if (lpSolution1.IsValid()) { + lpSolution1->MakePatch(Cipher); + } + if (lpSolution2.IsValid()) { + lpSolution2->MakePatch(Cipher); + } + + if (lpSolution0.IsValid()) { + puts("[+] PatchSolution0 has been applied."); + } + if (lpSolution1.IsValid()) { + puts("[+] PatchSolution1 has been applied."); + } + if (lpSolution2.IsValid()) { + puts("[+] PatchSolution2 has been applied."); + } + + if (szKeyFilePath.empty()) { + printf("[*] New RSA-2048 private key has been saved to\n"); + printf(" %s/RegPrivateKey.pem\n", nkg::Misc::FsCurrentWorkingDirectory().c_str()); + printf("\n"); + } + + puts(""); + puts("**************************************************************"); + puts("* Patch has been done successfully. Have fun and enjoy~~ *"); + puts("* DO NOT FORGET TO SIGN NAVICAT BY YOUR CERTIFICATE!!! *"); + puts("**************************************************************"); + } else { + puts("**************************************************************"); + puts("* DRY-RUN MODE ENABLE! *"); + puts("* NO PATCH WILL BE APPLIED! *"); + puts("**************************************************************"); + } return 0; - } catch (nkg::Exception& e) { - printf("[-] %s:%zu -> \n", e.File(), e.Line()); - printf(" %s\n", e.Message()); + } catch (ARL::Exception& e) { + printf("[-] %s:%zu ->\n", e.ExceptionFile(), e.ExceptionLine()); + printf(" %s\n", e.ExceptionMessage()); + if (e.HasErrorCode()) { - printf(" %s\n", e.ErrorString()); + printf(" %s (0x%zx)\n", e.ErrorString(), e.ErrorCode()); } + + for (const auto& Hint : e.Hints()) { + printf(" Hints: %s\n", Hint.c_str()); + } + + return -1; + } catch (std::exception& e) { + printf("[-] %s\n", e.what()); return -1; } }