├── .gitignore ├── CMakeLists.txt ├── Dockerfile ├── LICENSE ├── README.md ├── common ├── exception.hpp ├── exceptions │ ├── index_exception.hpp │ ├── key_exception.hpp │ ├── operation_canceled_exception.hpp │ └── unix_exception.hpp ├── resource_traits │ ├── cxx_dynamic_array_traits.hpp │ ├── cxx_object_traits.hpp │ ├── keystone │ │ ├── keystone_alloc.hpp │ │ └── keystone_handle.hpp │ ├── openssl │ │ ├── bignum.hpp │ │ ├── bio.hpp │ │ ├── bio_chain.hpp │ │ ├── decoder_ctx.hpp │ │ ├── encoder_ctx.hpp │ │ ├── evp_cipher_ctx.hpp │ │ ├── evp_pkey.hpp │ │ ├── evp_pkey_ctx.hpp │ │ └── rsa.hpp │ ├── unicorn │ │ ├── unicorn_alloc.hpp │ │ └── unicorn_handle.hpp │ └── unix_os │ │ ├── file_descriptor.hpp │ │ └── map_view.hpp ├── resource_wrapper.hpp ├── rsa_cipher.cpp └── rsa_cipher.hpp ├── doc ├── how-to-build.md ├── how-to-build.zh-CN.md ├── how-to-use.md └── how-to-use.zh-CN.md ├── docker-compose.yaml ├── navicat-keygen ├── CollectInformation.cpp ├── GenerateLicense.cpp ├── base32_rfc4648.cpp ├── base32_rfc4648.hpp ├── base64_rfc4648.cpp ├── base64_rfc4648.hpp ├── main.cpp ├── navicat_serial_generator.cpp └── navicat_serial_generator.hpp ├── navicat-patcher ├── amd64_emulator.cpp ├── amd64_emulator.hpp ├── elf64_interpreter.cpp ├── elf64_interpreter.hpp ├── keystone_assembler.cpp ├── keystone_assembler.hpp ├── main.cpp ├── memory_utility.hpp ├── patch_solution.hpp ├── patch_solution_since.hpp ├── patch_solution_since_16.0.7.0.cpp └── patch_solution_since_16.0.7.0.hpp └── start.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | cmake-build-* 4 | */.DS_Store 5 | bin/* 6 | navicat-patcher/Elf64Crafter.hpp 7 | navicat-patcher/Elf64Crafter.cpp 8 | navicat-patcher/Elf64InterpreterAmd64.hpp 9 | navicat-patcher/Elf64InterpreterAmd64.cpp 10 | RegPrivateKey.pem 11 | build 12 | *.AppImage 13 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.18) 2 | project(navicat-keygen) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 6 | 7 | find_package(fmt REQUIRED) 8 | find_package(RapidJSON REQUIRED) 9 | find_package(OpenSSL REQUIRED) 10 | 11 | include(FetchContent) 12 | set(KEYSTONE_BUILD_STATIC_RUNTIME ON CACHE BOOL "" FORCE) 13 | set(BUILD_LIBS_ONLY ON CACHE BOOL "" FORCE) 14 | set(UNICORN_ARCH "x86" CACHE STRING "" FORCE) 15 | set(UNICORN_INSTALL OFF CACHE BOOL "" FORCE) 16 | set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) 17 | set(UNICORN_BUILD_TESTS OFF CACHE BOOL "" FORCE) 18 | FetchContent_Declare( 19 | keystone 20 | GIT_REPOSITORY "https://github.com/keystone-engine/keystone.git" 21 | GIT_TAG "1475885daa7e566c064ae9754706e1a0ba24be3b" 22 | ) 23 | FetchContent_Declare( 24 | unicorn 25 | URL "https://github.com/unicorn-engine/unicorn/archive/refs/tags/2.0.0.tar.gz" 26 | URL_HASH "SHA256=67b445c760e2bbac663e8c8bc410e43311c7fc92df4dfa8d90e06a021d07f634" 27 | ) 28 | FetchContent_MakeAvailable(keystone unicorn) 29 | 30 | set( 31 | NKG_COMMON_SOURCE 32 | ./common/exception.hpp 33 | ./common/exceptions/index_exception.hpp 34 | ./common/exceptions/key_exception.hpp 35 | ./common/exceptions/operation_canceled_exception.hpp 36 | ./common/exceptions/unix_exception.hpp 37 | ./common/resource_wrapper.hpp 38 | ./common/resource_traits/cxx_object_traits.hpp 39 | ./common/resource_traits/cxx_dynamic_array_traits.hpp 40 | ./common/resource_traits/keystone/keystone_alloc.hpp 41 | ./common/resource_traits/keystone/keystone_handle.hpp 42 | ./common/resource_traits/openssl/bignum.hpp 43 | ./common/resource_traits/openssl/bio.hpp 44 | ./common/resource_traits/openssl/bio_chain.hpp 45 | ./common/resource_traits/openssl/rsa.hpp 46 | ./common/resource_traits/openssl/evp_pkey.hpp 47 | ./common/resource_traits/openssl/evp_pkey_ctx.hpp 48 | ./common/resource_traits/openssl/evp_cipher_ctx.hpp 49 | ./common/resource_traits/openssl/encoder_ctx.hpp 50 | ./common/resource_traits/openssl/decoder_ctx.hpp 51 | ./common/resource_traits/unicorn/unicorn_handle.hpp 52 | ./common/resource_traits/unicorn/unicorn_alloc.hpp 53 | ./common/resource_traits/unix_os/file_descriptor.hpp 54 | ./common/resource_traits/unix_os/map_view.hpp 55 | ./common/rsa_cipher.hpp 56 | ./common/rsa_cipher.cpp 57 | ) 58 | 59 | set( 60 | NKG_KEYGEN_SOURCE 61 | ./navicat-keygen/base32_rfc4648.hpp 62 | ./navicat-keygen/base32_rfc4648.cpp 63 | ./navicat-keygen/base64_rfc4648.hpp 64 | ./navicat-keygen/base64_rfc4648.cpp 65 | ./navicat-keygen/navicat_serial_generator.hpp 66 | ./navicat-keygen/navicat_serial_generator.cpp 67 | ./navicat-keygen/CollectInformation.cpp 68 | ./navicat-keygen/GenerateLicense.cpp 69 | ./navicat-keygen/main.cpp 70 | ) 71 | 72 | set( 73 | NKG_PATCHER_SOURCE 74 | ./navicat-patcher/amd64_emulator.hpp 75 | ./navicat-patcher/amd64_emulator.cpp 76 | ./navicat-patcher/keystone_assembler.hpp 77 | ./navicat-patcher/keystone_assembler.cpp 78 | ./navicat-patcher/elf64_interpreter.hpp 79 | ./navicat-patcher/elf64_interpreter.cpp 80 | ./navicat-patcher/patch_solution.hpp 81 | ./navicat-patcher/patch_solution_since.hpp 82 | ./navicat-patcher/patch_solution_since_16.0.7.0.hpp 83 | ./navicat-patcher/patch_solution_since_16.0.7.0.cpp 84 | ./navicat-patcher/memory_utility.hpp 85 | ./navicat-patcher/main.cpp 86 | ) 87 | 88 | add_executable(navicat-keygen ${NKG_COMMON_SOURCE} ${NKG_KEYGEN_SOURCE}) 89 | target_include_directories(navicat-keygen PRIVATE ./common ${RAPIDJSON_INCLUDE_DIRS}) 90 | target_link_libraries(navicat-keygen fmt::fmt OpenSSL::Crypto) 91 | 92 | add_executable(navicat-patcher ${NKG_COMMON_SOURCE} ${NKG_PATCHER_SOURCE}) 93 | target_include_directories(navicat-patcher PRIVATE ./common ${keystone_SOURCE_DIR}/include) 94 | target_link_libraries(navicat-patcher fmt::fmt OpenSSL::Crypto keystone unicorn pthread stdc++fs) 95 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | RUN apt update && apt install -y \ 4 | build-essential \ 5 | wget \ 6 | cmake \ 7 | git \ 8 | pkg-config \ 9 | python3 \ 10 | libfmt-dev \ 11 | libssl-dev \ 12 | libfuse2 \ 13 | appstream \ 14 | file \ 15 | rapidjson-dev 16 | 17 | WORKDIR /patcher 18 | 19 | COPY . . 20 | 21 | RUN chmod +x start.sh -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Navicat Patch & Keygen for Linux 2 | 3 | Support Navicat 16 and up to **16.1.8** (this patch will not work with the current latest version of Navicat 16). 4 | 5 | For infomation on older version of Navicat 16 or workaround for latest version of Navicat. See [#4](https://github.com/FuLygon/Navicat-Linux/issues/4) 6 | 7 | For Navicat 15, use branch [15](https://github.com/FuLygon/Navicat-Linux/tree/15) 8 | 9 | # Installation methods 10 | 11 | ## 1. Docker 12 | 13 | Make sure [Docker](https://docs.docker.com/engine/install) is installed and running. 14 | 15 | ### Setup the repository 16 | 17 | Clone the repository. 18 | 19 | ```shell 20 | git clone https://github.com/FuLygon/Navicat-Linux.git && cd Navicat-Linux 21 | ``` 22 | 23 | Copy Navicat `AppImage` inside this directory. 24 | 25 | ### Start the patching script 26 | 27 | ```shell 28 | docker compose run --build --rm patcher 29 | ``` 30 | 31 | Proceed with the patching script. 32 | 33 | ### Keygen and Activating Navicat 34 | 35 | The final step will be generating serial key and activating Navicat. **REMEMBER TO DISCONNECT INTERNET DURING THIS PROCESS**. 36 | 37 | ```shell 38 | Starting Keygen... (REMEMBER TO DISCONNECT INTERNET) 39 | Continue? [Y/n] y 40 | ``` 41 | 42 | Continue the final step as usual until you at `Input request code in Base64`. 43 | 44 | ```shell 45 | [*] Input major version number: 46 | (range: 11 ~ 16, default: 16)> 16 47 | 48 | [*] Serial number: 49 | XXXX-XXXX-XXXX-XXXX 50 | 51 | [*] Your name: FuLygon 52 | [*] Your organization: FGN 53 | 54 | [*] Input request code in Base64: (Double press ENTER to end) 55 | ``` 56 | 57 | **Do not close the terminal**. Go into `./build` directory, there should be a patched `AppImage` of Navicat. Execute this `AppImage` to open Navicat. Beginning the activation process. 58 | 59 | **ANOTHER REMINDER TO DISCONNECT INTERNET** 60 | 61 | Enter the `Serial number` provided by the script to Navicat and **Activate**. 62 | 63 | There should be a new prompt show up with **Manual Activation** option. If **Manual Activation** option not appear, double check if you have disconnected internet. 64 | 65 | A new **Manual Activation** window with 2 textbox **Request code** and **Activation Code** will appear. 66 | 67 | Copy the **Request code** to the patching script and **DOUBLE PRESS ENTER** to continue as mentioned by the `keygen`. 68 | 69 | The `keygen` will generate an `Activation Code`. Copy this value to **Activate Code** textbox of Navicat and **Activate** to complete the Manual Activation process. 70 | 71 | Your Navicat should be activated by now. 72 | 73 | ## 2. Manual 74 | 75 | Manual method documentation is unmaintained and might be outdated, or incompatible with some distros. 76 | 77 | See [docs](https://github.com/FuLygon/Navicat-Linux/tree/master/doc) for further detail 78 | 79 | # Credits 80 | 81 | [DoubleLabyrinth](https://github.com/DoubleLabyrinth) 82 | -------------------------------------------------------------------------------- /common/exception.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace nkg { 8 | 9 | class exception : public std::exception { 10 | private: 11 | int m_source_line; 12 | std::string m_source_file; 13 | std::string m_custom_message; 14 | std::vector m_hints; 15 | 16 | public: 17 | [[noreturn]] 18 | static void trap_then_terminate() { 19 | #if _MSC_VER 20 | __debugbreak(); 21 | #elif defined(__GNUC__) || defined(__GNUG__) || defined(__clang__) 22 | __builtin_trap(); 23 | 24 | #else 25 | #error "exception.hpp: unknown compiler is detected." 26 | #endif 27 | std::terminate(); 28 | } 29 | 30 | exception(std::string_view file, int line, std::string_view message) noexcept : 31 | std::exception(), m_source_line(line), m_source_file(file), m_custom_message(message) {} 32 | 33 | exception(const exception&) noexcept = default; 34 | exception(exception&&) noexcept = default; 35 | 36 | exception& operator=(const exception&) noexcept = default; 37 | exception& operator=(exception&&) noexcept = default; 38 | 39 | [[nodiscard]] 40 | int source_line() const noexcept { 41 | return m_source_line; 42 | } 43 | 44 | [[nodiscard]] 45 | const std::string& source_file() const noexcept { 46 | return m_source_file; 47 | } 48 | 49 | [[nodiscard]] 50 | const std::string& custom_message() const noexcept { 51 | return m_custom_message; 52 | } 53 | 54 | exception&& push_hint(std::string_view hint) noexcept { 55 | m_hints.emplace_back(hint); 56 | return std::move(*this); 57 | } 58 | 59 | exception&& pop_hint() noexcept { 60 | m_hints.pop_back(); 61 | return std::move(*this); 62 | } 63 | 64 | const std::vector& hints() const noexcept { 65 | return m_hints; 66 | } 67 | 68 | virtual const char* what() const noexcept override { 69 | return m_custom_message.c_str(); 70 | } 71 | 72 | [[nodiscard]] 73 | virtual bool error_code_exists() const noexcept { 74 | return false; 75 | } 76 | 77 | [[nodiscard]] 78 | virtual intptr_t error_code() const noexcept { 79 | trap_then_terminate(); 80 | } 81 | 82 | [[nodiscard]] 83 | virtual const std::string& error_string() const noexcept { 84 | trap_then_terminate(); 85 | } 86 | 87 | virtual ~exception() override = default; 88 | }; 89 | 90 | } 91 | -------------------------------------------------------------------------------- /common/exceptions/index_exception.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../exception.hpp" 3 | 4 | namespace nkg::exceptions { 5 | 6 | class index_exception : public ::nkg::exception { 7 | using ::nkg::exception::exception; 8 | }; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /common/exceptions/key_exception.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../exception.hpp" 3 | 4 | namespace nkg::exceptions { 5 | 6 | class key_exception : public ::nkg::exception { 7 | using ::nkg::exception::exception; 8 | }; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /common/exceptions/operation_canceled_exception.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../exception.hpp" 3 | 4 | namespace nkg::exceptions { 5 | 6 | class operation_canceled_exception : public ::nkg::exception { 7 | using ::nkg::exception::exception; 8 | }; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /common/exceptions/unix_exception.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "../exception.hpp" 5 | 6 | namespace nkg::exceptions { 7 | 8 | class unix_exception : public ::nkg::exception { 9 | public: 10 | using error_code_t = decltype(errno); 11 | 12 | private: 13 | error_code_t m_error_code; 14 | std::string m_error_string; 15 | 16 | public: 17 | unix_exception(std::string_view file, int line, error_code_t unix_errno, std::string_view message) noexcept : 18 | ::nkg::exception(file, line, message), m_error_code(unix_errno), m_error_string(strerror(unix_errno)) {} 19 | 20 | [[nodiscard]] 21 | virtual bool error_code_exists() const noexcept override { 22 | return true; 23 | } 24 | 25 | [[nodiscard]] 26 | virtual intptr_t error_code() const noexcept override { 27 | return m_error_code; 28 | } 29 | 30 | [[nodiscard]] 31 | virtual const std::string& error_string() const noexcept override { 32 | return m_error_string; 33 | } 34 | }; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /common/resource_traits/cxx_dynamic_array_traits.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace nkg::resource_traits { 4 | 5 | template 6 | struct cxx_dynamic_array_traits { 7 | using handle_t = element_t*; 8 | 9 | static constexpr handle_t invalid_value = nullptr; 10 | 11 | [[nodiscard]] 12 | static bool is_valid(const handle_t& handle) noexcept { 13 | return handle != invalid_value; 14 | } 15 | 16 | static void release(const handle_t& handle) { 17 | delete[] handle; 18 | } 19 | }; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /common/resource_traits/cxx_object_traits.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace nkg::resource_traits { 4 | 5 | template 6 | struct cxx_object_traits { 7 | using handle_t = object_t*; 8 | 9 | static constexpr handle_t invalid_value = nullptr; 10 | 11 | [[nodiscard]] 12 | static bool is_valid(const handle_t& handle) noexcept { 13 | return handle != invalid_value; 14 | } 15 | 16 | static void release(const handle_t& handle) { 17 | delete handle; 18 | } 19 | }; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /common/resource_traits/keystone/keystone_alloc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace nkg::resource_traits::keystone { 5 | 6 | struct keystone_alloc { 7 | using handle_t = unsigned char*; 8 | 9 | static constexpr handle_t invalid_value = nullptr; 10 | 11 | [[nodiscard]] 12 | static bool is_valid(const handle_t& handle) noexcept { 13 | return handle != invalid_value; 14 | } 15 | 16 | static void release(const handle_t& handle) noexcept { 17 | ks_free(handle); 18 | } 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /common/resource_traits/keystone/keystone_handle.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace nkg::resource_traits::keystone { 5 | 6 | struct keystone_handle { 7 | using handle_t = ks_engine*; 8 | 9 | static constexpr handle_t invalid_value = nullptr; 10 | 11 | [[nodiscard]] 12 | static bool is_valid(const handle_t& handle) noexcept { 13 | return handle != invalid_value; 14 | } 15 | 16 | static void release(const handle_t& handle) { 17 | ks_close(handle); 18 | } 19 | }; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /common/resource_traits/openssl/bignum.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace nkg::resource_traits::openssl { 5 | 6 | struct bignum { 7 | using handle_t = BIGNUM*; 8 | 9 | static constexpr handle_t invalid_value = nullptr; 10 | 11 | [[nodiscard]] 12 | static bool is_valid(const handle_t& handle) noexcept { 13 | return handle != invalid_value; 14 | } 15 | 16 | static void release(const handle_t& handle) noexcept { 17 | BN_free(handle); 18 | } 19 | }; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /common/resource_traits/openssl/bio.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace nkg::resource_traits::openssl { 5 | 6 | struct bio { 7 | using handle_t = BIO*; 8 | 9 | static constexpr handle_t invalid_value = nullptr; 10 | 11 | [[nodiscard]] 12 | static bool is_valid(const handle_t& handle) noexcept { 13 | return handle != invalid_value; 14 | } 15 | 16 | static void release(const handle_t& handle) noexcept { 17 | BIO_free(handle); 18 | } 19 | }; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /common/resource_traits/openssl/bio_chain.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace nkg::resource_traits::openssl { 5 | 6 | struct bio_chain { 7 | using handle_t = BIO*; 8 | 9 | static constexpr handle_t invalid_value = nullptr; 10 | 11 | [[nodiscard]] 12 | static bool is_valid(const handle_t& handle) noexcept { 13 | return handle != invalid_value; 14 | } 15 | 16 | static void release(const handle_t& handle) noexcept { 17 | BIO_free_all(handle); 18 | } 19 | }; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /common/resource_traits/openssl/decoder_ctx.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace nkg::resource_traits::openssl { 5 | 6 | struct decoder_ctx { 7 | using handle_t = OSSL_DECODER_CTX*; 8 | 9 | static constexpr handle_t invalid_value = nullptr; 10 | 11 | [[nodiscard]] 12 | static bool is_valid(const handle_t& handle) noexcept { 13 | return handle != invalid_value; 14 | } 15 | 16 | static void release(const handle_t& handle) noexcept { 17 | OSSL_DECODER_CTX_free(handle); 18 | } 19 | }; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /common/resource_traits/openssl/encoder_ctx.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace nkg::resource_traits::openssl { 5 | 6 | struct encoder_ctx { 7 | using handle_t = OSSL_ENCODER_CTX*; 8 | 9 | static constexpr handle_t invalid_value = nullptr; 10 | 11 | [[nodiscard]] 12 | static bool is_valid(const handle_t& handle) noexcept { 13 | return handle != invalid_value; 14 | } 15 | 16 | static void release(const handle_t& handle) noexcept { 17 | OSSL_ENCODER_CTX_free(handle); 18 | } 19 | }; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /common/resource_traits/openssl/evp_cipher_ctx.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace nkg::resource_traits::openssl { 5 | 6 | struct evp_cipher_ctx { 7 | using handle_t = EVP_CIPHER_CTX*; 8 | 9 | static constexpr handle_t invalid_value = nullptr; 10 | 11 | [[nodiscard]] 12 | static bool is_valid(const handle_t& handle) noexcept { 13 | return handle != invalid_value; 14 | } 15 | 16 | static void release(const handle_t& handle) noexcept { 17 | EVP_CIPHER_CTX_free(handle); 18 | } 19 | }; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /common/resource_traits/openssl/evp_pkey.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace nkg::resource_traits::openssl { 5 | 6 | struct evp_pkey { 7 | using handle_t = EVP_PKEY*; 8 | 9 | static constexpr handle_t invalid_value = nullptr; 10 | 11 | [[nodiscard]] 12 | static bool is_valid(const handle_t& handle) noexcept { 13 | return handle != invalid_value; 14 | } 15 | 16 | static void release(const handle_t& handle) noexcept { 17 | EVP_PKEY_free(handle); 18 | } 19 | }; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /common/resource_traits/openssl/evp_pkey_ctx.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace nkg::resource_traits::openssl { 5 | 6 | struct evp_pkey_ctx { 7 | using handle_t = EVP_PKEY_CTX*; 8 | 9 | static constexpr handle_t invalid_value = nullptr; 10 | 11 | [[nodiscard]] 12 | static bool is_valid(const handle_t& handle) noexcept { 13 | return handle != invalid_value; 14 | } 15 | 16 | static void release(const handle_t& handle) noexcept { 17 | EVP_PKEY_CTX_free(handle); 18 | } 19 | }; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /common/resource_traits/openssl/rsa.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace nkg::resource_traits::openssl { 5 | 6 | struct rsa { 7 | using handle_t = RSA*; 8 | 9 | static constexpr handle_t invalid_value = nullptr; 10 | 11 | [[nodiscard]] 12 | static bool is_valid(const handle_t& handle) noexcept { 13 | return handle != invalid_value; 14 | } 15 | 16 | static void release(const handle_t& handle) noexcept { 17 | RSA_free(handle); 18 | } 19 | }; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /common/resource_traits/unicorn/unicorn_alloc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace nkg::resource_traits::unicorn { 5 | 6 | struct unicorn_alloc { 7 | using handle_t = void*; 8 | 9 | static constexpr handle_t invalid_value = nullptr; 10 | 11 | [[nodiscard]] 12 | static bool is_valid(const handle_t& handle) noexcept { 13 | return handle != invalid_value; 14 | } 15 | 16 | static void release(const handle_t& handle) { 17 | uc_free(handle); 18 | } 19 | }; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /common/resource_traits/unicorn/unicorn_handle.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace nkg::resource_traits::unicorn { 5 | 6 | struct unicorn_handle { 7 | using handle_t = uc_engine*; 8 | 9 | static constexpr handle_t invalid_value = nullptr; 10 | 11 | [[nodiscard]] 12 | static bool is_valid(const handle_t& handle) noexcept { 13 | return handle != invalid_value; 14 | } 15 | 16 | static void release(const handle_t& handle) { 17 | uc_close(handle); 18 | } 19 | }; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /common/resource_traits/unix_os/file_descriptor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace nkg::resource_traits::unix_os { 5 | 6 | struct file_descriptor { 7 | using handle_t = int; 8 | 9 | static constexpr handle_t invalid_value = -1; 10 | 11 | [[nodiscard]] 12 | static bool is_valid(const handle_t& handle) noexcept { 13 | return handle != invalid_value; 14 | } 15 | 16 | static void release(const handle_t& handle) { 17 | close(handle); 18 | } 19 | }; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /common/resource_traits/unix_os/map_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace nkg::resource_traits::unix_os { 7 | 8 | struct map_view { 9 | using handle_t = void*; 10 | 11 | static inline const handle_t invalid_value = MAP_FAILED; 12 | 13 | [[nodiscard]] 14 | static bool is_valid(const handle_t& handle) noexcept { 15 | return handle != invalid_value; 16 | } 17 | }; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /common/resource_wrapper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace nkg { 6 | 7 | template 8 | class resource_wrapper { 9 | public: 10 | using handle_t = typename resource_traits_t::handle_t; 11 | static_assert(std::is_trivial_v && std::is_standard_layout_v, "`resource_wrapper` requires a handle with POD type."); 12 | 13 | private: 14 | handle_t m_handle; 15 | releaser_t m_releaser; 16 | 17 | public: 18 | template 19 | resource_wrapper(releaser_arg_t&& releaser) noexcept : 20 | m_handle(resource_traits_t::invalid_value), 21 | m_releaser(std::forward(releaser)) {} 22 | 23 | template 24 | resource_wrapper(const handle_t& handle, releaser_arg_t&& releaser) noexcept : 25 | m_handle(handle), 26 | m_releaser(std::forward(releaser)) {} 27 | 28 | template 29 | resource_wrapper(resource_traits_t, releaser_arg_t&& releaser) noexcept : 30 | m_handle(resource_traits_t::invalid_value), 31 | m_releaser(std::forward(releaser)) {} 32 | 33 | template 34 | resource_wrapper(resource_traits_t, const handle_t& handle, releaser_arg_t&& releaser) noexcept : 35 | m_handle(handle), 36 | m_releaser(std::forward(releaser)) {} 37 | 38 | // 39 | // `resource_wrapper` does not allow copy-construct 40 | // 41 | resource_wrapper(const resource_wrapper& other) = delete; 42 | 43 | // 44 | // `resource_wrapper` allows move-construct. 45 | // 46 | resource_wrapper(resource_wrapper&& other) noexcept : 47 | m_handle(other.m_handle), 48 | m_releaser(std::move(other.m_releaser)) 49 | { 50 | other.m_handle = resource_traits_t::invalid_value; 51 | } 52 | 53 | // 54 | // `resource_wrapper` does not allow to copy. 55 | // 56 | resource_wrapper& operator=(const resource_wrapper& other) = delete; 57 | 58 | // 59 | // `resource_wrapper` allows to move. 60 | // 61 | resource_wrapper& operator=(resource_wrapper&& other) noexcept { 62 | if (this != std::addressof(other)) { 63 | m_handle = other.m_handle; 64 | m_releaser = std::move(other.m_releaser); 65 | other.m_handle = resource_traits_t::invalid_value; 66 | } 67 | return *this; 68 | } 69 | 70 | template, ptr_t> = nullptr> 71 | [[nodiscard]] 72 | ptr_t operator->() const noexcept { 73 | return m_handle; 74 | } 75 | 76 | template 77 | [[nodiscard]] 78 | as_t as() const noexcept { 79 | return reinterpret_cast(m_handle); 80 | } 81 | 82 | [[nodiscard]] 83 | bool is_valid() const noexcept { 84 | return resource_traits_t::is_valid(m_handle); 85 | } 86 | 87 | [[nodiscard]] 88 | const handle_t& get() const noexcept { 89 | return m_handle; 90 | } 91 | 92 | template 93 | [[nodiscard]] 94 | as_t* unsafe_addressof() noexcept { 95 | return reinterpret_cast(std::addressof(m_handle)); 96 | } 97 | 98 | void set(const handle_t& handle) { 99 | if (is_valid()) { 100 | m_releaser(m_handle); 101 | } 102 | m_handle = handle; 103 | } 104 | 105 | void discard() noexcept { 106 | m_handle = resource_traits_t::invalid_value; 107 | } 108 | 109 | [[nodiscard]] 110 | handle_t transfer() noexcept { 111 | handle_t t = m_handle; 112 | m_handle = resource_traits_t::invalid_value; 113 | return t; 114 | } 115 | 116 | void release() { 117 | if (is_valid()) { 118 | m_releaser(m_handle); 119 | m_handle = resource_traits_t::invalid_value; 120 | } 121 | } 122 | 123 | ~resource_wrapper() { 124 | release(); 125 | } 126 | }; 127 | 128 | template 129 | class resource_wrapper { 130 | public: 131 | using handle_t = typename resource_traits_t::handle_t; 132 | static_assert(std::is_trivial_v&& std::is_standard_layout_v, "`resource_wrapper` requires a handle with POD type."); 133 | 134 | private: 135 | handle_t m_handle; 136 | 137 | public: 138 | resource_wrapper() noexcept : 139 | m_handle(resource_traits_t::invalid_value) {} 140 | 141 | resource_wrapper(const handle_t& handle) noexcept : 142 | m_handle(handle) {} 143 | 144 | resource_wrapper(resource_traits_t) noexcept : 145 | m_handle(resource_traits_t::invalid_value) {} 146 | 147 | resource_wrapper(resource_traits_t, const handle_t& handle) noexcept : 148 | m_handle(handle) {} 149 | 150 | resource_wrapper(const resource_wrapper& other) = delete; 151 | 152 | resource_wrapper(resource_wrapper&& other) noexcept : 153 | m_handle(other.m_handle) 154 | { 155 | other.m_handle = resource_traits_t::invalid_value; 156 | } 157 | 158 | resource_wrapper& operator=(const resource_wrapper& other) = delete; 159 | 160 | resource_wrapper& operator=(resource_wrapper&& other) noexcept { 161 | if (this != std::addressof(other)) { 162 | m_handle = other.m_handle; 163 | other.m_handle = resource_traits_t::invalid_value; 164 | } 165 | return *this; 166 | } 167 | 168 | template, ptr_t> = nullptr> 169 | [[nodiscard]] 170 | ptr_t operator->() const noexcept { 171 | return m_handle; 172 | } 173 | 174 | template 175 | [[nodiscard]] 176 | as_t as() const noexcept { 177 | return reinterpret_cast(m_handle); 178 | } 179 | 180 | [[nodiscard]] 181 | bool is_valid() const noexcept { 182 | return resource_traits_t::is_valid(m_handle); 183 | } 184 | 185 | [[nodiscard]] 186 | const handle_t& get() const noexcept { 187 | return m_handle; 188 | } 189 | 190 | template 191 | [[nodiscard]] 192 | as_t* unsafe_addressof() noexcept { 193 | return reinterpret_cast(std::addressof(m_handle)); 194 | } 195 | 196 | void set(const handle_t& handle) { 197 | if (is_valid()) { 198 | resource_traits_t::release(m_handle); 199 | } 200 | m_handle = handle; 201 | } 202 | 203 | void discard() noexcept { 204 | m_handle = resource_traits_t::invalid_value; 205 | } 206 | 207 | [[nodiscard]] 208 | handle_t transfer() noexcept { 209 | handle_t t = m_handle; 210 | m_handle = resource_traits_t::invalid_value; 211 | return t; 212 | } 213 | 214 | void release() { 215 | if (is_valid()) { 216 | resource_traits_t::release(m_handle); 217 | m_handle = resource_traits_t::invalid_value; 218 | } 219 | } 220 | 221 | ~resource_wrapper() { 222 | release(); 223 | } 224 | }; 225 | 226 | template 227 | resource_wrapper(resource_traits_t) -> 228 | resource_wrapper; 229 | 230 | template 231 | resource_wrapper(resource_traits_t, arg_t&&) -> 232 | resource_wrapper>, typename resource_traits_t::handle_t>, std::remove_reference_t, void>>; 233 | 234 | template 235 | resource_wrapper(resource_traits_t, const handle_t&, releaser_t&&) -> 236 | resource_wrapper>; 237 | 238 | } 239 | -------------------------------------------------------------------------------- /common/rsa_cipher.cpp: -------------------------------------------------------------------------------- 1 | #include "rsa_cipher.hpp" 2 | #include 3 | #include 4 | 5 | #include "resource_traits/openssl/bio.hpp" 6 | #include "resource_traits/openssl/bignum.hpp" 7 | 8 | #if (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x 9 | #include 10 | #include 11 | #include "resource_traits/openssl/encoder_ctx.hpp" 12 | #include "resource_traits/openssl/decoder_ctx.hpp" 13 | #endif 14 | 15 | #define NKG_CURRENT_SOURCE_FILE() u8".\\common\\rsa_cipher.cpp" 16 | #define NKG_CURRENT_SOURCE_LINE() __LINE__ 17 | 18 | namespace nkg { 19 | 20 | #if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0 21 | [[nodiscard]] 22 | RSA* rsa_cipher::_read_private_key_from_bio(BIO* p_bio) { 23 | resource_wrapper new_rsa 24 | { resource_traits::openssl::rsa{}, PEM_read_bio_RSAPrivateKey(p_bio, nullptr, nullptr, nullptr) }; 25 | 26 | if (new_rsa.is_valid()) { 27 | return new_rsa.transfer(); 28 | } else { 29 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_read_bio_RSAPrivateKey failed.") 30 | .push_hint(u8"Are you sure that you DO provide a valid RSA private key file?"); 31 | } 32 | } 33 | 34 | [[nodiscard]] 35 | RSA* rsa_cipher::_read_public_key_pem_from_bio(BIO* p_bio) { 36 | resource_wrapper new_rsa 37 | { resource_traits::openssl::rsa{}, PEM_read_bio_RSA_PUBKEY(p_bio, nullptr, nullptr, nullptr) }; 38 | 39 | if (new_rsa.is_valid()) { 40 | return new_rsa.transfer(); 41 | } else { 42 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_read_bio_RSA_PUBKEY failed.") 43 | .push_hint(u8"Are you sure that you DO provide a valid RSA public key file with PEM format?"); 44 | } 45 | } 46 | 47 | [[nodiscard]] 48 | RSA* rsa_cipher::_read_public_key_pkcs1_from_bio(BIO* p_bio) { 49 | resource_wrapper new_rsa 50 | { resource_traits::openssl::rsa{}, PEM_read_bio_RSAPublicKey(p_bio, nullptr, nullptr, nullptr) }; 51 | 52 | if (new_rsa.is_valid()) { 53 | return new_rsa.transfer(); 54 | } else { 55 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_read_bio_RSAPublicKey failed.") 56 | .push_hint(u8"Are you sure that you DO provide a valid RSA public key file with PKCS1 format?"); 57 | } 58 | } 59 | 60 | void rsa_cipher::_write_private_key_to_bio(RSA* p_rsa, BIO* p_bio) { 61 | auto r = PEM_write_bio_RSAPrivateKey(p_bio, p_rsa, nullptr, nullptr, 0, nullptr, nullptr); 62 | if (r == 0) { 63 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_write_bio_RSAPrivateKey failed."); 64 | }; 65 | } 66 | 67 | void rsa_cipher::_write_public_key_pem_to_bio(RSA* p_rsa, BIO* p_bio) { 68 | auto r = PEM_write_bio_RSA_PUBKEY(p_bio, p_rsa); 69 | if (r == 0) { 70 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_write_bio_RSA_PUBKEY failed."); 71 | } 72 | } 73 | 74 | void rsa_cipher::_write_public_key_pkcs1_to_bio(RSA* p_rsa, BIO* p_bio) { 75 | auto r = PEM_write_bio_RSAPublicKey(p_bio, p_rsa); 76 | if (r == 0) { 77 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_write_bio_RSAPublicKey failed."); 78 | } 79 | } 80 | 81 | rsa_cipher::rsa_cipher() : 82 | m_rsa(RSA_new()) 83 | { 84 | if (!m_rsa.is_valid()) { 85 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_new failed."); 86 | } 87 | } 88 | #elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x 89 | [[nodiscard]] 90 | EVP_PKEY* rsa_cipher::_read_private_key_from_bio(BIO* p_bio) { 91 | resource_wrapper new_rsa{ resource_traits::openssl::evp_pkey{} }; 92 | 93 | resource_wrapper decoder_context 94 | { resource_traits::openssl::decoder_ctx{}, OSSL_DECODER_CTX_new_for_pkey(new_rsa.unsafe_addressof(), "PEM", "pkcs1", "RSA", OSSL_KEYMGMT_SELECT_PRIVATE_KEY, nullptr, nullptr) }; 95 | 96 | if (!decoder_context.is_valid()) { 97 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_DECODER_CTX_new_for_pkey failed."); 98 | } 99 | 100 | if (!OSSL_DECODER_from_bio(decoder_context.get(), p_bio)) { // 1 on success, 0 on failure 101 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_DECODER_from_bio failed."); 102 | } 103 | 104 | return new_rsa.transfer(); 105 | } 106 | 107 | [[nodiscard]] 108 | EVP_PKEY* rsa_cipher::_read_public_key_pem_from_bio(BIO* p_bio) { 109 | resource_wrapper new_rsa{ resource_traits::openssl::evp_pkey{} }; 110 | 111 | resource_wrapper decoder_context 112 | { resource_traits::openssl::decoder_ctx{}, OSSL_DECODER_CTX_new_for_pkey(new_rsa.unsafe_addressof(), "PEM", "SubjectPublicKeyInfo", "RSA", OSSL_KEYMGMT_SELECT_PUBLIC_KEY, nullptr, nullptr) }; 113 | 114 | if (!decoder_context.is_valid()) { 115 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_DECODER_CTX_new_for_pkey failed."); 116 | } 117 | 118 | if (!OSSL_DECODER_from_bio(decoder_context.get(), p_bio)) { // 1 on success, 0 on failure 119 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_DECODER_from_bio failed."); 120 | } 121 | 122 | return new_rsa.transfer(); 123 | } 124 | 125 | [[nodiscard]] 126 | EVP_PKEY* rsa_cipher::_read_public_key_pkcs1_from_bio(BIO* p_bio) { 127 | resource_wrapper new_rsa{ resource_traits::openssl::evp_pkey{} }; 128 | 129 | resource_wrapper decoder_context 130 | { resource_traits::openssl::decoder_ctx{}, OSSL_DECODER_CTX_new_for_pkey(new_rsa.unsafe_addressof(), "PEM", "pkcs1", "RSA", OSSL_KEYMGMT_SELECT_PUBLIC_KEY, nullptr, nullptr) }; 131 | 132 | if (!decoder_context.is_valid()) { 133 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_DECODER_CTX_new_for_pkey failed."); 134 | } 135 | 136 | if (!OSSL_DECODER_from_bio(decoder_context.get(), p_bio)) { // 1 on success, 0 on failure 137 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_DECODER_from_bio failed."); 138 | } 139 | 140 | return new_rsa.transfer(); 141 | } 142 | 143 | void rsa_cipher::_write_private_key_to_bio(EVP_PKEY* p_rsa, BIO* p_bio) { 144 | resource_wrapper encoder_context 145 | { resource_traits::openssl::encoder_ctx{}, OSSL_ENCODER_CTX_new_for_pkey(p_rsa, OSSL_KEYMGMT_SELECT_PRIVATE_KEY, "PEM", "pkcs1", nullptr) }; 146 | 147 | if (!encoder_context.is_valid()) { 148 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_ENCODER_CTX_new_for_pkey failed."); 149 | } 150 | 151 | if (!OSSL_ENCODER_to_bio(encoder_context.get(), p_bio)) { // 1 on success, 0 on failure 152 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_ENCODER_to_bio failed."); 153 | } 154 | } 155 | 156 | void rsa_cipher::_write_public_key_pem_to_bio(EVP_PKEY* p_rsa, BIO* p_bio) { 157 | resource_wrapper encoder_context 158 | { resource_traits::openssl::encoder_ctx{}, OSSL_ENCODER_CTX_new_for_pkey(p_rsa, OSSL_KEYMGMT_SELECT_PUBLIC_KEY, "PEM", "SubjectPublicKeyInfo", nullptr) }; 159 | 160 | if (!encoder_context.is_valid()) { 161 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_ENCODER_CTX_new_for_pkey failed."); 162 | } 163 | 164 | if (!OSSL_ENCODER_to_bio(encoder_context.get(), p_bio)) { // 1 on success, 0 on failure 165 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_ENCODER_to_bio failed."); 166 | } 167 | } 168 | 169 | void rsa_cipher::_write_public_key_pkcs1_to_bio(EVP_PKEY* p_rsa, BIO* p_bio) { 170 | resource_wrapper encoder_context 171 | { resource_traits::openssl::encoder_ctx{}, OSSL_ENCODER_CTX_new_for_pkey(p_rsa, OSSL_KEYMGMT_SELECT_PUBLIC_KEY, "PEM", "pkcs1", nullptr) }; 172 | 173 | if (!encoder_context.is_valid()) { 174 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_ENCODER_CTX_new_for_pkey failed."); 175 | } 176 | 177 | if (!OSSL_ENCODER_to_bio(encoder_context.get(), p_bio)) { // 1 on success, 0 on failure 178 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_ENCODER_to_bio failed."); 179 | } 180 | } 181 | 182 | rsa_cipher::rsa_cipher() = default; 183 | #else 184 | #error "rsa_cipher.cpp: Unexpected OpenSSL version." 185 | #endif 186 | 187 | [[nodiscard]] 188 | size_t rsa_cipher::bits() const { 189 | #if (OPENSSL_VERSION_NUMBER & 0xfff00000) == 0x10000000 // openssl 1.0.x 190 | if (m_rsa->n) { 191 | return BN_num_bits(m_rsa->n); 192 | } else { 193 | throw no_key_assigned_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"RSA key has not been set yet."); 194 | } 195 | #elif (OPENSSL_VERSION_NUMBER & 0xfff00000) == 0x10100000 // openssl 1.1.x 196 | return RSA_bits(m_rsa.get()); 197 | #elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // openssl 3.x.x 198 | if (m_rsa.is_valid()) { 199 | return EVP_PKEY_get_bits(m_rsa.get()); 200 | } else { 201 | throw no_key_assigned_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"RSA key has not been set yet."); 202 | } 203 | #else 204 | #error "rsa_cipher.cpp: Unexpected OpenSSL version." 205 | #endif 206 | } 207 | 208 | void rsa_cipher::generate_key(int bits, unsigned int e) { 209 | resource_wrapper bn_e{ resource_traits::openssl::bignum{}, BN_new() }; 210 | 211 | if (!bn_e.is_valid()) { 212 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"BN_new failed."); 213 | } 214 | 215 | if (BN_set_word(bn_e.get(), e) == 0) { 216 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BN_set_word failed."); 217 | } 218 | 219 | #if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0 220 | if (RSA_generate_key_ex(m_rsa.get(), bits, bn_e.get(), nullptr) == 0) { 221 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_generate_key_ex failed."); 222 | } 223 | #elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x 224 | resource_wrapper evp_pkey_context{ resource_traits::openssl::evp_pkey_ctx{}, EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr) }; 225 | if (!evp_pkey_context.is_valid()) { 226 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_new_id failed."); 227 | } 228 | 229 | if (EVP_PKEY_keygen_init(evp_pkey_context.get()) <= 0) { // 1 for success, 0 or a negative value for failure 230 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_keygen_init failed."); 231 | } 232 | 233 | if (EVP_PKEY_CTX_set_rsa_keygen_bits(evp_pkey_context.get(), bits) <= 0) { // return a positive value for success and 0 or a negative value for failure 234 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_set_rsa_keygen_bits failed."); 235 | } 236 | 237 | if (EVP_PKEY_CTX_set1_rsa_keygen_pubexp(evp_pkey_context.get(), bn_e.get()) <= 0) { // return a positive value for success and 0 or a negative value for failure 238 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_set1_rsa_keygen_pubexp failed."); 239 | } 240 | 241 | resource_wrapper new_rsa{ resource_traits::openssl::evp_pkey{} }; 242 | 243 | if (EVP_PKEY_keygen(evp_pkey_context.get(), new_rsa.unsafe_addressof()) <= 0) { // 1 for success, 0 or a negative value for failure 244 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_keygen failed."); 245 | } 246 | 247 | m_rsa = std::move(new_rsa); 248 | #else 249 | #error "rsa_cipher.cpp: Unexpected OpenSSL version." 250 | #endif 251 | } 252 | 253 | void rsa_cipher::export_private_key_file(std::string_view file_path) const { 254 | resource_wrapper bio_file 255 | { resource_traits::openssl::bio{}, BIO_new_file(file_path.data(), "w")}; 256 | 257 | if (bio_file.is_valid()) { 258 | _write_private_key_to_bio(m_rsa.get(), bio_file.get()); 259 | } else { 260 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed."); 261 | } 262 | } 263 | 264 | void rsa_cipher::export_public_key_file_pem(std::string_view file_path) const { 265 | resource_wrapper bio_file 266 | { resource_traits::openssl::bio{}, BIO_new_file(file_path.data(), "w")}; 267 | 268 | if (bio_file.is_valid()) { 269 | _write_public_key_pem_to_bio(m_rsa.get(), bio_file.get()); 270 | } else { 271 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed."); 272 | } 273 | } 274 | 275 | void rsa_cipher::export_public_key_file_pkcs1(std::string_view file_path) const { 276 | resource_wrapper bio_file 277 | { resource_traits::openssl::bio{}, BIO_new_file(file_path.data(), "w")}; 278 | 279 | if (bio_file.is_valid()) { 280 | _write_public_key_pkcs1_to_bio(m_rsa.get(), bio_file.get()); 281 | } else { 282 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed."); 283 | } 284 | } 285 | 286 | void rsa_cipher::import_private_key_file(std::string_view file_path) { 287 | resource_wrapper bio_file 288 | { resource_traits::openssl::bio{}, BIO_new_file(file_path.data(), "r") }; 289 | 290 | if (bio_file.is_valid()) { 291 | m_rsa.set(_read_private_key_from_bio(bio_file.get())); 292 | } else { 293 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed."); 294 | } 295 | } 296 | 297 | void rsa_cipher::import_public_key_file_pem(std::string_view file_path) { 298 | resource_wrapper bio_file 299 | { resource_traits::openssl::bio{}, BIO_new_file(file_path.data(), "r") }; 300 | 301 | if (bio_file.is_valid()) { 302 | m_rsa.set(_read_public_key_pem_from_bio(bio_file.get())); 303 | } else { 304 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed."); 305 | } 306 | } 307 | 308 | void rsa_cipher::import_public_key_file_pkcs1(std::string_view file_path) { 309 | resource_wrapper bio_file 310 | { resource_traits::openssl::bio{}, BIO_new_file(file_path.data(), "r") }; 311 | 312 | if (bio_file.is_valid()) { 313 | m_rsa.set(_read_public_key_pkcs1_from_bio(bio_file.get())); 314 | } else { 315 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed."); 316 | } 317 | } 318 | 319 | [[nodiscard]] 320 | std::string rsa_cipher::export_private_key_string() const { 321 | resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) }; 322 | 323 | if (bio_memory.is_valid()) { 324 | _write_private_key_to_bio(m_rsa.get(), bio_memory.get()); 325 | 326 | const char* pch = nullptr; 327 | long lch = BIO_get_mem_data(bio_memory.get(), &pch); 328 | 329 | return std::string(pch, lch); 330 | } else { 331 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed."); 332 | } 333 | } 334 | 335 | [[nodiscard]] 336 | std::string rsa_cipher::export_public_key_string_pem() const { 337 | resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) }; 338 | 339 | if (bio_memory.is_valid()) { 340 | _write_public_key_pem_to_bio(m_rsa.get(), bio_memory.get()); 341 | 342 | const char* pch = nullptr; 343 | long lch = BIO_get_mem_data(bio_memory.get(), &pch); 344 | 345 | return std::string(pch, lch); 346 | } else { 347 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed."); 348 | } 349 | } 350 | 351 | [[nodiscard]] 352 | std::string rsa_cipher::export_public_key_string_pkcs1() const { 353 | resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) }; 354 | 355 | if (bio_memory.is_valid()) { 356 | _write_public_key_pkcs1_to_bio(m_rsa.get(), bio_memory.get()); 357 | 358 | const char* pch = nullptr; 359 | long lch = BIO_get_mem_data(bio_memory.get(), &pch); 360 | 361 | return std::string(pch, lch); 362 | } else { 363 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed."); 364 | } 365 | } 366 | 367 | void rsa_cipher::import_private_key_string(std::string_view key_string) { 368 | resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) }; 369 | 370 | if (!bio_memory.is_valid()) { 371 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed."); 372 | } 373 | 374 | if (BIO_puts(bio_memory.get(), key_string.data()) <= 0) { 375 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_puts failed."); 376 | } 377 | 378 | m_rsa.set(_read_private_key_from_bio(bio_memory.get())); 379 | } 380 | 381 | void rsa_cipher::import_public_key_string_pem(std::string_view key_string) { 382 | resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) }; 383 | 384 | if (!bio_memory.is_valid()) { 385 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed."); 386 | } 387 | 388 | if (BIO_puts(bio_memory.get(), key_string.data()) <= 0) { 389 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_puts failed."); 390 | } 391 | 392 | m_rsa.set(_read_public_key_pem_from_bio(bio_memory.get())); 393 | } 394 | 395 | void rsa_cipher::import_public_key_string_pkcs1(std::string_view key_string) { 396 | resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) }; 397 | 398 | if (!bio_memory.is_valid()) { 399 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed."); 400 | } 401 | 402 | if (BIO_puts(bio_memory.get(), key_string.data()) <= 0) { 403 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_puts failed."); 404 | } 405 | 406 | m_rsa.set(_read_public_key_pkcs1_from_bio(bio_memory.get())); 407 | } 408 | 409 | size_t rsa_cipher::public_encrypt(const void* plaintext, size_t plaintext_size, void* ciphertext, int padding) const { 410 | #if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0 411 | if (plaintext_size <= INT_MAX) { 412 | int bytes_written = 413 | RSA_public_encrypt(static_cast(plaintext_size), reinterpret_cast(plaintext), reinterpret_cast(ciphertext), m_rsa.get(), padding); 414 | 415 | if (bytes_written != -1) { 416 | return bytes_written; 417 | } else { 418 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_encrypt failed."); 419 | } 420 | } else { 421 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"plaintext_size > INT_MAX"); 422 | } 423 | #elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x 424 | resource_wrapper evp_pkey_context{ resource_traits::openssl::evp_pkey_ctx{}, EVP_PKEY_CTX_new(m_rsa.get(), nullptr) }; 425 | if (!evp_pkey_context.is_valid()) { 426 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_new failed."); 427 | } 428 | 429 | if (EVP_PKEY_encrypt_init(evp_pkey_context.get()) <= 0) { // return 1 for success, 0 or a negative value for failure 430 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_encrypt_init failed."); 431 | } 432 | 433 | if (EVP_PKEY_CTX_set_rsa_padding(evp_pkey_context.get(), padding) <= 0) { // return a positive value for success, 0 or a negative value for failure 434 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_set_rsa_padding failed."); 435 | } 436 | 437 | size_t ciphertext_size = 0; 438 | if (EVP_PKEY_encrypt(evp_pkey_context.get(), nullptr, &ciphertext_size, reinterpret_cast(plaintext), plaintext_size) <= 0) { // return 1 for success, 0 or a negative value for failure 439 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_encrypt failed."); 440 | } 441 | 442 | if (EVP_PKEY_encrypt(evp_pkey_context.get(), reinterpret_cast(ciphertext), &ciphertext_size, reinterpret_cast(plaintext), plaintext_size) <= 0) { // return 1 for success, 0 or a negative value for failure 443 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_encrypt failed."); 444 | } 445 | 446 | return ciphertext_size; 447 | #else 448 | #error "rsa_cipher.cpp: Unexpected OpenSSL version." 449 | #endif 450 | } 451 | 452 | size_t rsa_cipher::private_encrypt(const void* plaintext, size_t plaintext_size, void* ciphertext, int padding) const { 453 | #if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0 454 | if (plaintext_size <= INT_MAX) { 455 | int bytes_written = 456 | RSA_private_encrypt(static_cast(plaintext_size), reinterpret_cast(plaintext), reinterpret_cast(ciphertext), m_rsa.get(), padding); 457 | 458 | if (bytes_written != -1) { 459 | return bytes_written; 460 | } else { 461 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_encrypt failed."); 462 | } 463 | } else { 464 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"plaintext_size > INT_MAX"); 465 | } 466 | #elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x 467 | resource_wrapper evp_pkey_context{ resource_traits::openssl::evp_pkey_ctx{}, EVP_PKEY_CTX_new(m_rsa.get(), nullptr) }; 468 | if (!evp_pkey_context.is_valid()) { 469 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_new failed."); 470 | } 471 | 472 | if (EVP_PKEY_sign_init(evp_pkey_context.get()) <= 0) { // return 1 for success, 0 or a negative value for failure 473 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_sign_init failed."); 474 | } 475 | 476 | if (EVP_PKEY_CTX_set_rsa_padding(evp_pkey_context.get(), padding) <= 0) { // return a positive value for success, 0 or a negative value for failure 477 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_set_rsa_padding failed."); 478 | } 479 | 480 | size_t ciphertext_size = 0; 481 | if (EVP_PKEY_sign(evp_pkey_context.get(), nullptr, &ciphertext_size, reinterpret_cast(plaintext), plaintext_size) <= 0) { // return 1 for success, 0 or a negative value for failure 482 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_sign failed."); 483 | } 484 | 485 | if (EVP_PKEY_sign(evp_pkey_context.get(), reinterpret_cast(ciphertext), &ciphertext_size, reinterpret_cast(plaintext), plaintext_size) <= 0) { // return 1 for success, 0 or a negative value for failure 486 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_sign failed."); 487 | } 488 | 489 | return ciphertext_size; 490 | #else 491 | #error "rsa_cipher.cpp: Unexpected OpenSSL version." 492 | #endif 493 | } 494 | 495 | size_t rsa_cipher::public_decrypt(const void* ciphertext, size_t ciphertext_size, void* plaintext, int padding) const { 496 | #if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0 497 | if (ciphertext_size <= INT_MAX) { 498 | int bytes_written = 499 | RSA_public_decrypt(static_cast(ciphertext_size), reinterpret_cast(ciphertext), reinterpret_cast(plaintext), m_rsa.get(), padding); 500 | 501 | if (bytes_written != -1) { 502 | return bytes_written; 503 | } else { 504 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_decrypt failed.") 505 | .push_hint(u8"Are your sure you DO provide a correct public key?"); 506 | } 507 | } else { 508 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"ciphertext_size > INT_MAX"); 509 | } 510 | #elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x 511 | resource_wrapper evp_pkey_context{ resource_traits::openssl::evp_pkey_ctx{}, EVP_PKEY_CTX_new(m_rsa.get(), nullptr) }; 512 | if (!evp_pkey_context.is_valid()) { 513 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_new failed."); 514 | } 515 | 516 | if (EVP_PKEY_verify_recover_init(evp_pkey_context.get())) { // return 1 for success, 0 or a negative value for failure 517 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_verify_recover_init failed."); 518 | } 519 | 520 | if (EVP_PKEY_CTX_set_rsa_padding(evp_pkey_context.get(), padding) <= 0) { // return a positive value for success, 0 or a negative value for failure 521 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_set_rsa_padding failed."); 522 | } 523 | 524 | size_t plaintext_size = 0; 525 | if (EVP_PKEY_verify_recover(evp_pkey_context.get(), nullptr, &plaintext_size, reinterpret_cast(ciphertext), ciphertext_size) <= 0) { // return 1 for success, 0 or a negative value for failure 526 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_verify_recover failed."); 527 | } 528 | 529 | if (EVP_PKEY_verify_recover(evp_pkey_context.get(), reinterpret_cast(plaintext), &plaintext_size, reinterpret_cast(ciphertext), ciphertext_size) <= 0) { // return 1 for success, 0 or a negative value for failure 530 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_verify_recover failed."); 531 | } 532 | 533 | return plaintext_size; 534 | #else 535 | #error "rsa_cipher.cpp: Unexpected OpenSSL version." 536 | #endif 537 | } 538 | 539 | size_t rsa_cipher::private_decrypt(const void* ciphertext, size_t ciphertext_size, void* plaintext, int padding) const { 540 | #if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0 541 | if (ciphertext_size <= INT_MAX) { 542 | int bytes_written = 543 | RSA_private_decrypt(static_cast(ciphertext_size), reinterpret_cast(ciphertext), reinterpret_cast(plaintext), m_rsa.get(), padding); 544 | 545 | if (bytes_written != -1) { 546 | return bytes_written; 547 | } else { 548 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_decrypt failed.") 549 | .push_hint(u8"Are your sure you DO provide a correct private key?"); 550 | } 551 | } else { 552 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"ciphertext_size > INT_MAX"); 553 | } 554 | #elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x 555 | resource_wrapper evp_pkey_context{ resource_traits::openssl::evp_pkey_ctx{}, EVP_PKEY_CTX_new(m_rsa.get(), nullptr) }; 556 | if (!evp_pkey_context.is_valid()) { 557 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_new failed."); 558 | } 559 | 560 | if (EVP_PKEY_decrypt_init(evp_pkey_context.get()) <= 0) { // return 1 for success, 0 or a negative value for failure 561 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_decrypt_init failed."); 562 | } 563 | 564 | if (EVP_PKEY_CTX_set_rsa_padding(evp_pkey_context.get(), padding) <= 0) { // return a positive value for success, 0 or a negative value for failure 565 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_CTX_set_rsa_padding failed."); 566 | } 567 | 568 | size_t plaintext_size = 0; 569 | if (EVP_PKEY_decrypt(evp_pkey_context.get(), nullptr, &plaintext_size, reinterpret_cast(ciphertext), ciphertext_size) <= 0) { // return 1 for success, 0 or a negative value for failure 570 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_decrypt failed."); 571 | } 572 | 573 | if (EVP_PKEY_decrypt(evp_pkey_context.get(), reinterpret_cast(plaintext), &plaintext_size, reinterpret_cast(ciphertext), ciphertext_size) <= 0) { // return 1 for success, 0 or a negative value for failure 574 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_PKEY_decrypt failed."); 575 | } 576 | 577 | return plaintext_size; 578 | #else 579 | #error "rsa_cipher.cpp: Unexpected OpenSSL version." 580 | #endif 581 | } 582 | 583 | } 584 | 585 | #undef NKG_CURRENT_SOURCE_FILE 586 | #undef NKG_CURRENT_SOURCE_LINE 587 | -------------------------------------------------------------------------------- /common/rsa_cipher.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include "resource_wrapper.hpp" 9 | #if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0 10 | #include "resource_traits/openssl/rsa.hpp" 11 | #elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x 12 | #include "resource_traits/openssl/evp_pkey_ctx.hpp" 13 | #include "resource_traits/openssl/evp_pkey.hpp" 14 | #else 15 | #error "rsa_cipher.hpp: Unexpected OpenSSL version." 16 | #endif 17 | 18 | #include "exception.hpp" 19 | 20 | #define NKG_CURRENT_SOURCE_FILE() u8".\\common\\rsa_cipher.hpp" 21 | #define NKG_CURRENT_SOURCE_LINE() __LINE__ 22 | 23 | namespace nkg { 24 | 25 | class rsa_cipher { 26 | public: 27 | class backend_error; 28 | class no_key_assigned_error; 29 | 30 | private: 31 | #if (OPENSSL_VERSION_NUMBER & 0xf0000000) < 0x30000000 // for openssl < 3.0.0 32 | resource_wrapper m_rsa; 33 | 34 | [[nodiscard]] 35 | static RSA* _read_private_key_from_bio(BIO* p_bio); 36 | 37 | [[nodiscard]] 38 | static RSA* _read_public_key_pem_from_bio(BIO* p_bio); 39 | 40 | [[nodiscard]] 41 | static RSA* _read_public_key_pkcs1_from_bio(BIO* p_bio); 42 | 43 | static void _write_private_key_to_bio(RSA* p_rsa, BIO* p_bio); 44 | 45 | static void _write_public_key_pem_to_bio(RSA* p_rsa, BIO* p_bio); 46 | 47 | static void _write_public_key_pkcs1_to_bio(RSA* p_rsa, BIO* p_bio); 48 | #elif (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x 49 | resource_wrapper m_rsa; 50 | 51 | [[nodiscard]] 52 | static EVP_PKEY* _read_private_key_from_bio(BIO* p_bio); 53 | 54 | [[nodiscard]] 55 | static EVP_PKEY* _read_public_key_pem_from_bio(BIO* p_bio); 56 | 57 | [[nodiscard]] 58 | static EVP_PKEY* _read_public_key_pkcs1_from_bio(BIO* p_bio); 59 | 60 | static void _write_private_key_to_bio(EVP_PKEY* p_rsa, BIO* p_bio); 61 | 62 | static void _write_public_key_pem_to_bio(EVP_PKEY* p_rsa, BIO* p_bio); 63 | 64 | static void _write_public_key_pkcs1_to_bio(EVP_PKEY* p_rsa, BIO* p_bio); 65 | #else 66 | #error "rsa_cipher.hpp: Unexpected OpenSSL version." 67 | #endif 68 | 69 | public: 70 | rsa_cipher(); 71 | 72 | [[nodiscard]] 73 | size_t bits() const; 74 | 75 | void generate_key(int bits, unsigned int e = RSA_F4); 76 | 77 | void export_private_key_file(std::string_view file_path) const; 78 | 79 | void export_public_key_file_pem(std::string_view file_path) const; 80 | 81 | void export_public_key_file_pkcs1(std::string_view file_path) const; 82 | 83 | void import_private_key_file(std::string_view file_path); 84 | 85 | void import_public_key_file_pem(std::string_view file_path); 86 | 87 | void import_public_key_file_pkcs1(std::string_view file_path); 88 | 89 | [[nodiscard]] 90 | std::string export_private_key_string() const; 91 | 92 | [[nodiscard]] 93 | std::string export_public_key_string_pem() const; 94 | 95 | [[nodiscard]] 96 | std::string export_public_key_string_pkcs1() const; 97 | 98 | void import_private_key_string(std::string_view key_string); 99 | 100 | void import_public_key_string_pem(std::string_view key_string); 101 | 102 | void import_public_key_string_pkcs1(std::string_view key_string); 103 | 104 | size_t public_encrypt(const void* plaintext, size_t plaintext_size, void* ciphertext, int padding) const; 105 | 106 | size_t private_encrypt(const void* plaintext, size_t plaintext_size, void* ciphertext, int padding) const; 107 | 108 | size_t public_decrypt(const void* ciphertext, size_t ciphertext_size, void* plaintext, int padding) const; 109 | 110 | size_t private_decrypt(const void* ciphertext, size_t ciphertext_size, void* plaintext, int padding) const; 111 | }; 112 | 113 | class rsa_cipher::backend_error : public ::nkg::exception { 114 | public: 115 | using error_code_t = decltype(ERR_get_error()); 116 | 117 | private: 118 | std::optional m_error_code; 119 | std::string m_error_string; 120 | 121 | public: 122 | backend_error(std::string_view file, int line, std::string_view message) noexcept : 123 | ::nkg::exception(file, line, message) {} 124 | 125 | backend_error(std::string_view file, int line, error_code_t openssl_errno, std::string_view message) noexcept : 126 | ::nkg::exception(file, line, message), m_error_code(openssl_errno) {} 127 | 128 | [[nodiscard]] 129 | virtual bool error_code_exists() const noexcept override { 130 | return m_error_code.has_value(); 131 | } 132 | 133 | [[nodiscard]] 134 | virtual intptr_t error_code() const noexcept override { 135 | if (error_code_exists()) { return m_error_code.value(); } else { trap_then_terminate(); } 136 | } 137 | 138 | [[nodiscard]] 139 | virtual const std::string& error_string() const noexcept override { 140 | if (error_code_exists()) { return m_error_string; } else { trap_then_terminate(); } 141 | } 142 | }; 143 | 144 | class rsa_cipher::no_key_assigned_error : public ::nkg::exception { 145 | using ::nkg::exception::exception; 146 | }; 147 | 148 | } 149 | 150 | #undef NKG_CURRENT_SOURCE_FILE 151 | #undef NKG_CURRENT_SOURCE_LINE 152 | -------------------------------------------------------------------------------- /doc/how-to-build.md: -------------------------------------------------------------------------------- 1 | # navicat-keygen for linux - How to build? 2 | 3 | [中文版](how-to-build.zh-CN.md) 4 | 5 | ## 1. Prerequisites 6 | 7 | 1. Install latest `CMake`: 8 | 9 | ```bash 10 | $ sudo apt-get install cmake 11 | ``` 12 | 13 | 2. Install `fmt`, `OpenSSL` and `rapidjson`: 14 | 15 | ```bash 16 | $ sudo apt-get install libfmt-dev libssl-dev rapidjson-dev 17 | ``` 18 | 19 | ## 2. Build 20 | 21 | 1. Clone: 22 | 23 | ```bash 24 | $ git clone https://github.com/FuLygon/Navicat-Linux.git 25 | $ cd Navicat-Linux 26 | ``` 27 | 28 | 2. Build: 29 | 30 | ```bash 31 | $ mkdir build 32 | $ cd build 33 | $ cmake -DCMAKE_BUILD_TYPE=Release .. 34 | $ cmake --build . -- -j4 35 | ``` 36 | 37 | Then you will see two executable files, `navicat-keygen` and `navicat-patcher`, in `build` directory. 38 | -------------------------------------------------------------------------------- /doc/how-to-build.zh-CN.md: -------------------------------------------------------------------------------- 1 | # navicat-keygen for linux - 如何编译? 2 | 3 | ## 1. 前提条件 4 | 5 | 1. 安装最新的 `CMake`: 6 | 7 | ```bash 8 | $ sudo apt-get install cmake 9 | ``` 10 | 11 | 2. 安装 `fmt`、`OpenSSL` 和 `rapidjson`: 12 | 13 | ```bash 14 | $ sudo apt-get install libfmt-dev libssl-dev rapidjson-dev 15 | ``` 16 | 17 | ## 2. 编译 18 | 19 | 1. clone 仓库: 20 | 21 | ```bash 22 | $ git clone https://github.com/FuLygon/Navicat-Linux.git 23 | $ cd Navicat-Linux 24 | ``` 25 | 26 | 2. 编译: 27 | 28 | ```bash 29 | $ mkdir build 30 | $ cd build 31 | $ cmake -DCMAKE_BUILD_TYPE=Release .. 32 | $ cmake --build . -- -j4 33 | ``` 34 | 35 | 编译完后你会在 build 文件夹里看到两个可执行文件 `navicat-keygen` 和 `navicat-patcher`。 36 | -------------------------------------------------------------------------------- /doc/how-to-use.md: -------------------------------------------------------------------------------- 1 | # navicat-keygen for linux - How to use? 2 | 3 | [中文版](how-to-use.zh-CN.md) 4 | 5 | 1. Download navicat from official website and you will get an AppImage file. For example, a file named `navicat16-premium-en.AppImage`. I assume that the AppImage file is located in `~/` folder. 6 | 7 | 2. Extract all files inside `navicat16-premium-en.AppImage` to a directory, e.g. `~/navicat16-premium-en-patched`: 8 | 9 | ```bash 10 | $ mkdir ~/navicat16-premium-en 11 | $ sudo mount -o loop ~/navicat16-premium-en.AppImage ~/navicat16-premium-en 12 | $ cp -r ~/navicat16-premium-en ~/navicat16-premium-en-patched 13 | $ sudo umount ~/navicat16-premium-en 14 | $ rm -rf ~/navicat16-premium-en 15 | ``` 16 | 17 | 3. [Build keygen and patcher.](how-to-build.md) 18 | 19 | 4. Use `navicat-patcher` to replace the official public key. 20 | 21 | ``` 22 | Usage: 23 | navicat-patcher [OPTION...] [RSA-2048 private key file] 24 | 25 | --dry-run Run patcher without applying any patches 26 | -h, --help Print help 27 | ``` 28 | 29 | **Example:** 30 | 31 | ```bash 32 | $ ./navicat-patcher ~/navicat16-premium-en-patched 33 | ``` 34 | 35 | It has been tested on **Navicat Premium 16.0.7 English For Linux** version. The following is an example of output: 36 | 37 | ``` 38 | *************************************************** 39 | * navicat-patcher by @DoubleLabyrinth * 40 | * version: 16.0.7.0 * 41 | *************************************************** 42 | 43 | [+] Try to open libcc.dll ... OK! 44 | 45 | [*] patch_solution_since<16, 0, 7, 0>: m_va_CSRegistrationInfoFetcher_LINUX_vtable = 0x0000000002fbf038 46 | [*] patch_solution_since<16, 0, 7, 0>: m_va_CSRegistrationInfoFetcher_LINUX_GenerateRegistrationKey = 0x0000000001420530 47 | [*] patch_solution_since<16, 0, 7, 0>: m_va_pltgot_std_string_append = 0x000000000306a608 48 | [+] patch_solution_since<16, 0, 7, 0>: official encoded key is found. 49 | 50 | [*] Generating new RSA private key, it may take a long time... 51 | [*] Your RSA private key: 52 | -----BEGIN RSA PRIVATE KEY----- 53 | MIIEogIBAAKCAQEAnqeNFqE0n6k+Ys58R+IzIULlZ9oxasJns46vaVcnd9e9mTj4 54 | hf/ArQohP5bex0UD+NGCLfQWKohnQxy9IFjQxZ6wUJnKOaA1UfdRr0ck7LZz5YV2 55 | 5CkF4VZ7UWGEp/LEQiJnIBAtp7Zq5PNviqI03PZv2MfZt1/6YstCvi8s0rmpAyTw 56 | V5pteDipsI3lTapysLLsL+kQuJ0Z64GGk5D7rM8UPBt/Wjpe/qb/OwJKJ3Vi65/Z 57 | RRLVt46euwdiW8ORmNt552zOdvQPgYLEP38dMCpoeqIwv7IFWWJHxt0JgOTmxZ5c 58 | hjMq9ns3wyyEFjvSGSpM6sGL5eR1TMDQt8PSIwIDAQABAoIBAAp+jL+VdDSnbj/8 59 | 5o2escEeeqwu65vjNhbTdljicfLka18qPI4oh6cqh158bUoDD6syuIivn5O6qBHx 60 | YbU1CsI3p/P86Dp9lWlRka6lZxTdULc6581ZxVDNdqTAbZTqYv745ZdiWpLAZzuz 61 | uooSBqsjBezx8z3E9Hv6c/S+jBl4IcKx7vEgKbC47CAAnFZFGonRPBj88fXn7VmP 62 | KEa7EVIE8yNG/xO4H+hlpio8XsTfbXB4Tx/Mw4f0ZCifFFE9aC503usbTQ9HCRn/ 63 | 13VgBeFzC+uxluEvpm7r3efcI1vFLDHiKlAzO85vDhTOef3jZv29PTUwbkNCP8AM 64 | CHP9abECgYEAzKWmGRcMVhwKL2RIc0VW1mSW82VTnjuTe9wnonYOGHmxCxQBZpB2 65 | j8mjfPBNzg3pjhRryrmfA2V/1Rxk7/hnw9I+OnRJA2p+USOKVlXi5YA3CMtPcevS 66 | DJaawPIkvjnnApSQ0CNYYKCgB43l33GJQ+btvYQiB/pmPwCiPhdsdYkCgYEAxndh 67 | GVSRd9yHez9LggsTfS4uo1OlZReHrtyX933eEG4YtiwM90Mb/9ZCA6CY5/o8YpOp 68 | AhPu7he3f67cQ7dotjilFHWqi4f+53d8NhfweQhM0azA9zzXjKhsao9jPjtTo+DW 69 | BptWVNZOqv1v0Np4/BZ3rTKtP42vSR4/Ql7Mi0sCgYATzoiH7yIjh2047wTQG0rv 70 | TycJAaqZKvz4RPOVFsYAem63OsVz7tF60zI+mmd9ZP1Q4gsYwORyCLXZo3jlfO5W 71 | FpgtQin66ai2I7F077UZL1KkSEE1LnTTARSTThxeSO5h4o0th+460/EJKiOwf6Wg 72 | a85gxFQi34pb2KzbQ5scuQKBgGLHA9LBnm0Tm3Kh/AjLTnXdSGUNuqHn5iYHsLMD 73 | OEThJvd0UTe3dPYOQ2jew3uhtfAyIcng9egWccPg2cvyOvGGm9LlBW7QzvORKocZ 74 | vxveH62z1461/2oIYX1fxDsy99v2iU9cfMlYqGq+HKrMMa7117aiJEwfToCLx1xX 75 | JmKlAoGAI8aOpSjyQ/xVUS1niXmwbKPkOJP7jI3yQ6YpUEnho/wpFw2/OjuVdXw2 76 | tcYFSg1GjBvTigDeh385TsjDa862SYAslgqZBPN4jXP20xw1kRr9kAQq6bvadC+b 77 | WpwZ8C5vNxwX5ccbxz3WwJhHtZvIQt0J8hOf3+BgcedHxa+bcTQ= 78 | -----END RSA PRIVATE KEY----- 79 | 80 | [*] patch_solution_since<16, 0, 7, 0>: Patch has been done. 81 | [*] New RSA-2048 private key has been saved to 82 | /home/doublesine/RegPrivateKey.pem 83 | 84 | ******************************************************* 85 | * PATCH HAS BEEN DONE SUCCESSFULLY! * 86 | * HAVE FUN AND ENJOY~ * 87 | ******************************************************* 88 | ``` 89 | 90 | 5. Repack files into a new AppImage: 91 | 92 | **Example:** 93 | 94 | ```bash 95 | $ wget 'https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage' 96 | $ chmod +x appimagetool-x86_64.AppImage 97 | $ ./appimagetool-x86_64.AppImage ~/navicat16-premium-en-patched ~/navicat16-premium-en-patched.AppImage 98 | ``` 99 | 100 | 6. Run the newly-generated AppImage: 101 | 102 | ```bash 103 | $ chmod +x ~/navicat16-premium-en-patched.AppImage 104 | $ ~/navicat16-premium-en-patched.AppImage 105 | ``` 106 | 107 | 7. Use `navicat-keygen` to generate **snKey** and **Activation Code**. 108 | 109 | ``` 110 | Usage: 111 | navicat-keygen <--bin|-text> [--adv] 112 | 113 | <--bin|--text> Specify "--bin" to generate "license_file" used by Navicat 11. 114 | Specify "--text" to generate base64-encoded activation code. 115 | This parameter is mandatory. 116 | 117 | [--adv] Enable advance mode. 118 | This parameter is optional. 119 | 120 | A path to an RSA-2048 private key file. 121 | This parameter is mandatory. 122 | ``` 123 | 124 | **Example:** 125 | 126 | ```bash 127 | $ ./navicat-keygen --text ./RegPrivateKey.pem 128 | ``` 129 | 130 | You will be asked to select Navicat language and give major version number. After that an randomly generated **snKey** will be given. 131 | 132 | ``` 133 | *************************************************** 134 | * navicat-keygen by @DoubleLabyrinth * 135 | * version: 16.0.7.0 * 136 | *************************************************** 137 | 138 | [*] Select Navicat product: 139 | 0. DataModeler 140 | 1. Premium 141 | 2. MySQL 142 | 3. PostgreSQL 143 | 4. Oracle 144 | 5. SQLServer 145 | 6. SQLite 146 | 7. MariaDB 147 | 8. MongoDB 148 | 9. ReportViewer 149 | 150 | (Input index)> 1 151 | 152 | [*] Select product language: 153 | 0. English 154 | 1. Simplified Chinese 155 | 2. Traditional Chinese 156 | 3. Japanese 157 | 4. Polish 158 | 5. Spanish 159 | 6. French 160 | 7. German 161 | 8. Korean 162 | 9. Russian 163 | 10. Portuguese 164 | 165 | (Input index)> 0 166 | 167 | [*] Input major version number: 168 | (range: 11 ~ 16, default: 16)> 16 169 | 170 | [*] Serial number: 171 | NAVB-EZF4-7T7X-9MPG 172 | 173 | [*] Your name: 174 | ``` 175 | 176 | You can use this **snKey** to activate your Navicat preliminarily. 177 | 178 | Then you will be asked to input `Your name` and `Your organization`. Just set them whatever you want, but not too long. 179 | 180 | ``` 181 | [*] Your name: Double Sine 182 | [*] Your organization: PremiumSoft CyberTech Ltd. 183 | 184 | [*] Input request code in Base64: (Double press ENTER to end) 185 | ``` 186 | 187 | After that, you will be asked to input request code. Now **DO NOT CLOSE KEYGEN**. 188 | 189 | 8. **Disconnect your network**. Find and click `Registration`. 190 | 191 | Fill license key by **Serial number** that the keygen gave and click `Activate`. 192 | 193 | 9. Generally online activation will fail and Navicat will ask you do `Manual Activation`, just choose it. 194 | 195 | 10. Copy your request code and paste it in the keygen. Input empty line to tell the keygen that your input ends. 196 | 197 | ``` 198 | [*] Input request code in Base64: (Double press ENTER to end) 199 | ds7CnjEnNL+8Rme9Q5iD+3t9Tfuq9W6FzVN/3UZwC5zzecmM9EwyHJuZSovKJNSBTzL6AiGyxliTuKPWmLqAdwiKGLuD+mSaZ0syk0jTakVbXmbAk9maFkTz8SK5jMwnQVM/WBZcI0z2Jg1GnOCZVClu/Lo3/WF+XncS+alc2gshG9dUaI44Cqfvp/u1/EYso5fX/bjeBXaFW1/zj+uuRjVv5l0gt7JsTh9byGVxSDTO4zI64Iz9+58QYCbI9zKM+3G9Gou0UlNKjDYw4gN5+4dpiWAjitVTcL3oQzvflgAXjGlT/P6MA+8Xb5PEPJrEdxsErJObxBhO4cTH52wKoQ== 200 | 201 | [*] Request Info: 202 | {"K":"NAVBEZF47T7X9MPG", "DI":"AFCFB038A240942D8776", "P":"linux"} 203 | 204 | [*] Response Info: 205 | {"K":"NAVBEZF47T7X9MPG","DI":"AFCFB038A240942D8776","N":"Double Sine","O":"PremiumSoft CyberTech Ltd.","T":1644837835} 206 | 207 | [*] Activation Code: 208 | OY8Ib0brsepeS99it4s4WTDPQuKgu93WembLJ0bzr6M30Wh24reH1/ocaZ2Ek1bRBi5lqu2xBv/MpAcFUlstJANtavArkFnXYv0ZZiF3VF70De5GMe/VjkreNhjCGtTZcQKr8fabBTPjJuN0P+Hi1xWwMs9zJMuH+MJTmCQpbM4gu86YrFK/EDcdHtA4ZFgUI0SgYW8lwFausLFHp7C4uIQNbjtv4KP3XolDUrAx4lqg6bklgZ9C8ZjUpg28VVR9Ym37b1Fup7Y7C8OjmmMiAp8N5z8m6cA/EjcSLfLOMGf8jsAK0GHz5/AGUqAXWifv9h9cxPA35UgytqI9F2IH/Q== 209 | ``` 210 | 211 | 11. Finally, you will get **Activation Code** which looks like a Base64 string. 212 | 213 | Just copy it and paste it in Navicat `Manual Activation` window, then click `Activate`. 214 | 215 | If nothing wrong, activation should be done successfully. 216 | 217 | 12. Clean up: 218 | 219 | ```bash 220 | $ rm ~/navicat16-premium-en.AppImage 221 | $ rm -rf ~/navicat16-premium-en-patched 222 | $ mv ~/navicat16-premium-en-patched.AppImage ~/navicat16-premium-en.AppImage 223 | ``` 224 | -------------------------------------------------------------------------------- /doc/how-to-use.zh-CN.md: -------------------------------------------------------------------------------- 1 | # navicat-keygen for linux - 如何使用? 2 | 3 | 1. 从官网下载 Navicat,你应该会得到一个 AppImage 文件,例如 `navicat16-premium-en.AppImage`。 4 | 5 | 我假定这个 AppImage 文件在 `~/` 文件夹下。 6 | 7 | 2. 提取 `navicat16-premium-en.AppImage` 里的所有文件到一个文件夹,例如 `~/navicat16-premium-en-patched`: 8 | 9 | ```bash 10 | $ mkdir ~/navicat16-premium-en 11 | $ sudo mount -o loop ~/navicat16-premium-en.AppImage ~/navicat16-premium-en 12 | $ cp -r ~/navicat16-premium-en ~/navicat16-premium-en-patched 13 | $ sudo umount ~/navicat16-premium-en 14 | $ rm -rf ~/navicat16-premium-en 15 | ``` 16 | 17 | 3. [编译 patcher 和 keygen](how-to-build.zh-CN.md)。 18 | 19 | 4. 使用 `navicat-patcher` 替换官方公钥。 20 | 21 | ``` 22 | Usage: 23 | navicat-patcher [OPTION...] [RSA-2048 private key file] 24 | 25 | --dry-run Run patcher without applying any patches 26 | -h, --help Print help 27 | ``` 28 | 29 | **例如:** 30 | 31 | ```bash 32 | $ ./navicat-patcher ~/navicat16-premium-en-patched 33 | ``` 34 | 35 | **Navicat Premium 16.0.7 英文版 for Linux** 已经通过测试,下面是一份样例输出: 36 | 37 | ``` 38 | *************************************************** 39 | * navicat-patcher by @DoubleLabyrinth * 40 | * version: 16.0.7.0 * 41 | *************************************************** 42 | 43 | [+] Try to open libcc.dll ... OK! 44 | 45 | [*] patch_solution_since<16, 0, 7, 0>: m_va_CSRegistrationInfoFetcher_LINUX_vtable = 0x0000000002fbf038 46 | [*] patch_solution_since<16, 0, 7, 0>: m_va_CSRegistrationInfoFetcher_LINUX_GenerateRegistrationKey = 0x0000000001420530 47 | [*] patch_solution_since<16, 0, 7, 0>: m_va_pltgot_std_string_append = 0x000000000306a608 48 | [+] patch_solution_since<16, 0, 7, 0>: official encoded key is found. 49 | 50 | [*] Generating new RSA private key, it may take a long time... 51 | [*] Your RSA private key: 52 | -----BEGIN RSA PRIVATE KEY----- 53 | MIIEogIBAAKCAQEAnqeNFqE0n6k+Ys58R+IzIULlZ9oxasJns46vaVcnd9e9mTj4 54 | hf/ArQohP5bex0UD+NGCLfQWKohnQxy9IFjQxZ6wUJnKOaA1UfdRr0ck7LZz5YV2 55 | 5CkF4VZ7UWGEp/LEQiJnIBAtp7Zq5PNviqI03PZv2MfZt1/6YstCvi8s0rmpAyTw 56 | V5pteDipsI3lTapysLLsL+kQuJ0Z64GGk5D7rM8UPBt/Wjpe/qb/OwJKJ3Vi65/Z 57 | RRLVt46euwdiW8ORmNt552zOdvQPgYLEP38dMCpoeqIwv7IFWWJHxt0JgOTmxZ5c 58 | hjMq9ns3wyyEFjvSGSpM6sGL5eR1TMDQt8PSIwIDAQABAoIBAAp+jL+VdDSnbj/8 59 | 5o2escEeeqwu65vjNhbTdljicfLka18qPI4oh6cqh158bUoDD6syuIivn5O6qBHx 60 | YbU1CsI3p/P86Dp9lWlRka6lZxTdULc6581ZxVDNdqTAbZTqYv745ZdiWpLAZzuz 61 | uooSBqsjBezx8z3E9Hv6c/S+jBl4IcKx7vEgKbC47CAAnFZFGonRPBj88fXn7VmP 62 | KEa7EVIE8yNG/xO4H+hlpio8XsTfbXB4Tx/Mw4f0ZCifFFE9aC503usbTQ9HCRn/ 63 | 13VgBeFzC+uxluEvpm7r3efcI1vFLDHiKlAzO85vDhTOef3jZv29PTUwbkNCP8AM 64 | CHP9abECgYEAzKWmGRcMVhwKL2RIc0VW1mSW82VTnjuTe9wnonYOGHmxCxQBZpB2 65 | j8mjfPBNzg3pjhRryrmfA2V/1Rxk7/hnw9I+OnRJA2p+USOKVlXi5YA3CMtPcevS 66 | DJaawPIkvjnnApSQ0CNYYKCgB43l33GJQ+btvYQiB/pmPwCiPhdsdYkCgYEAxndh 67 | GVSRd9yHez9LggsTfS4uo1OlZReHrtyX933eEG4YtiwM90Mb/9ZCA6CY5/o8YpOp 68 | AhPu7he3f67cQ7dotjilFHWqi4f+53d8NhfweQhM0azA9zzXjKhsao9jPjtTo+DW 69 | BptWVNZOqv1v0Np4/BZ3rTKtP42vSR4/Ql7Mi0sCgYATzoiH7yIjh2047wTQG0rv 70 | TycJAaqZKvz4RPOVFsYAem63OsVz7tF60zI+mmd9ZP1Q4gsYwORyCLXZo3jlfO5W 71 | FpgtQin66ai2I7F077UZL1KkSEE1LnTTARSTThxeSO5h4o0th+460/EJKiOwf6Wg 72 | a85gxFQi34pb2KzbQ5scuQKBgGLHA9LBnm0Tm3Kh/AjLTnXdSGUNuqHn5iYHsLMD 73 | OEThJvd0UTe3dPYOQ2jew3uhtfAyIcng9egWccPg2cvyOvGGm9LlBW7QzvORKocZ 74 | vxveH62z1461/2oIYX1fxDsy99v2iU9cfMlYqGq+HKrMMa7117aiJEwfToCLx1xX 75 | JmKlAoGAI8aOpSjyQ/xVUS1niXmwbKPkOJP7jI3yQ6YpUEnho/wpFw2/OjuVdXw2 76 | tcYFSg1GjBvTigDeh385TsjDa862SYAslgqZBPN4jXP20xw1kRr9kAQq6bvadC+b 77 | WpwZ8C5vNxwX5ccbxz3WwJhHtZvIQt0J8hOf3+BgcedHxa+bcTQ= 78 | -----END RSA PRIVATE KEY----- 79 | 80 | [*] patch_solution_since<16, 0, 7, 0>: Patch has been done. 81 | [*] New RSA-2048 private key has been saved to 82 | /home/doublesine/RegPrivateKey.pem 83 | 84 | ******************************************************* 85 | * PATCH HAS BEEN DONE SUCCESSFULLY! * 86 | * HAVE FUN AND ENJOY~ * 87 | ******************************************************* 88 | ``` 89 | 90 | 5. 将文件重新打包成 AppImage: 91 | 92 | **例如:** 93 | 94 | ```bash 95 | $ wget 'https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage' 96 | $ chmod +x appimagetool-x86_64.AppImage 97 | $ ./appimagetool-x86_64.AppImage ~/navicat16-premium-en-patched ~/navicat16-premium-en-patched.AppImage 98 | ``` 99 | 100 | 6. 运行刚生成的 AppImage: 101 | 102 | ```bash 103 | $ chmod +x ~/navicat16-premium-en-patched.AppImage 104 | $ ~/navicat16-premium-en-patched.AppImage 105 | ``` 106 | 107 | 7. 使用 `navicat-keygen` 来生成 **序列号** 和 **激活码**。 108 | 109 | ``` 110 | Usage: 111 | navicat-keygen <--bin|-text> [--adv] 112 | 113 | <--bin|--text> Specify "--bin" to generate "license_file" used by Navicat 11. 114 | Specify "--text" to generate base64-encoded activation code. 115 | This parameter is mandatory. 116 | 117 | [--adv] Enable advance mode. 118 | This parameter is optional. 119 | 120 | A path to an RSA-2048 private key file. 121 | This parameter is mandatory. 122 | ``` 123 | 124 | **例如:** 125 | 126 | ```bash 127 | $ ./navicat-keygen --text ./RegPrivateKey.pem 128 | ``` 129 | 130 | 你会被要求选择 Navicat 产品类别、Navicat 语言版本和填写主版本号。之后一个随机生成的 **序列号** 将会给出。 131 | 132 | ``` 133 | *************************************************** 134 | * navicat-keygen by @DoubleLabyrinth * 135 | * version: 16.0.7.0 * 136 | *************************************************** 137 | 138 | [*] Select Navicat product: 139 | 0. DataModeler 140 | 1. Premium 141 | 2. MySQL 142 | 3. PostgreSQL 143 | 4. Oracle 144 | 5. SQLServer 145 | 6. SQLite 146 | 7. MariaDB 147 | 8. MongoDB 148 | 9. ReportViewer 149 | 150 | (Input index)> 1 151 | 152 | [*] Select product language: 153 | 0. English 154 | 1. Simplified Chinese 155 | 2. Traditional Chinese 156 | 3. Japanese 157 | 4. Polish 158 | 5. Spanish 159 | 6. French 160 | 7. German 161 | 8. Korean 162 | 9. Russian 163 | 10. Portuguese 164 | 165 | (Input index)> 0 166 | 167 | [*] Input major version number: 168 | (range: 11 ~ 16, default: 16)> 16 169 | 170 | [*] Serial number: 171 | NAVB-EZF4-7T7X-9MPG 172 | 173 | [*] Your name: 174 | ``` 175 | 176 | 你可以使用这个 **序列号** 来暂时激活 Navicat。 177 | 178 | 之后你会被要求填写 **用户名** 和 **组织名**。你可以随意填写,但别太长。 179 | 180 | ``` 181 | [*] Your name: Double Sine 182 | [*] Your organization: PremiumSoft CyberTech Ltd. 183 | 184 | [*] Input request code in Base64: (Double press ENTER to end) 185 | ``` 186 | 187 | 之后你会被要求填写请求码。**注意不要关闭 keygen。** 188 | 189 | 8. **断开网络**. 找到注册窗口,填写 keygen 给你的 **序列号**,然后点击 `激活`。 190 | 191 | 9. 通常在线激活会失败,所以在弹出的提示中选择 `手动激活`。 192 | 193 | 10. 复制 **请求码** 到 keygen,连按两次回车结束。 194 | 195 | ``` 196 | [*] Input request code in Base64: (Double press ENTER to end) 197 | ds7CnjEnNL+8Rme9Q5iD+3t9Tfuq9W6FzVN/3UZwC5zzecmM9EwyHJuZSovKJNSBTzL6AiGyxliTuKPWmLqAdwiKGLuD+mSaZ0syk0jTakVbXmbAk9maFkTz8SK5jMwnQVM/WBZcI0z2Jg1GnOCZVClu/Lo3/WF+XncS+alc2gshG9dUaI44Cqfvp/u1/EYso5fX/bjeBXaFW1/zj+uuRjVv5l0gt7JsTh9byGVxSDTO4zI64Iz9+58QYCbI9zKM+3G9Gou0UlNKjDYw4gN5+4dpiWAjitVTcL3oQzvflgAXjGlT/P6MA+8Xb5PEPJrEdxsErJObxBhO4cTH52wKoQ== 198 | 199 | [*] Request Info: 200 | {"K":"NAVBEZF47T7X9MPG", "DI":"AFCFB038A240942D8776", "P":"linux"} 201 | 202 | [*] Response Info: 203 | {"K":"NAVBEZF47T7X9MPG","DI":"AFCFB038A240942D8776","N":"Double Sine","O":"PremiumSoft CyberTech Ltd.","T":1644837835} 204 | 205 | [*] Activation Code: 206 | OY8Ib0brsepeS99it4s4WTDPQuKgu93WembLJ0bzr6M30Wh24reH1/ocaZ2Ek1bRBi5lqu2xBv/MpAcFUlstJANtavArkFnXYv0ZZiF3VF70De5GMe/VjkreNhjCGtTZcQKr8fabBTPjJuN0P+Hi1xWwMs9zJMuH+MJTmCQpbM4gu86YrFK/EDcdHtA4ZFgUI0SgYW8lwFausLFHp7C4uIQNbjtv4KP3XolDUrAx4lqg6bklgZ9C8ZjUpg28VVR9Ym37b1Fup7Y7C8OjmmMiAp8N5z8m6cA/EjcSLfLOMGf8jsAK0GHz5/AGUqAXWifv9h9cxPA35UgytqI9F2IH/Q== 207 | ``` 208 | 209 | 11. 最终你会得到一个 base64 编码的 **激活码**。 210 | 211 | 将之复制到 `手动激活` 的窗口,然后点击 `激活`。 212 | 213 | 如果没有什么意外,应该可以成功激活。 214 | 215 | 12. 最后清理: 216 | 217 | ```bash 218 | $ rm ~/navicat16-premium-en.AppImage 219 | $ rm -rf ~/navicat16-premium-en-patched 220 | $ mv ~/navicat16-premium-en-patched.AppImage ~/navicat16-premium-en.AppImage 221 | ``` 222 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | patcher: 3 | build: 4 | context: . 5 | privileged: true 6 | command: /bin/bash ./start.sh 7 | tty: true 8 | stdin_open: true 9 | volumes: 10 | - ./build:/patcher/bin 11 | -------------------------------------------------------------------------------- /navicat-keygen/CollectInformation.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "navicat_serial_generator.hpp" 3 | #include "exceptions/operation_canceled_exception.hpp" 4 | 5 | #define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-keygen\\CollectInformation.cpp" 6 | #define NKG_CURRENT_SOURCE_LINE() __LINE__ 7 | 8 | namespace nkg { 9 | 10 | [[nodiscard]] 11 | static int read_int(int min_val, int max_val, std::string_view prompt, std::string_view error_msg) { 12 | int val; 13 | 14 | for (std::string s;;) { 15 | std::cout << prompt; 16 | if (!std::getline(std::cin, s)) { 17 | throw exceptions::operation_canceled_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Operation is canceled by user."); 18 | } 19 | 20 | if (s.empty()) 21 | continue; 22 | 23 | try { 24 | val = std::stoi(s, nullptr, 0); 25 | if (min_val <= val && val <= max_val) { 26 | return val; 27 | } else { 28 | throw std::invalid_argument("Out of range."); 29 | } 30 | } catch (std::invalid_argument&) { 31 | std::cout << error_msg << std::endl; 32 | } 33 | } 34 | } 35 | 36 | [[nodiscard]] 37 | static int read_int(int min_val, int max_val, int default_val, std::string_view prompt, std::string_view error_msg) { 38 | int val; 39 | 40 | for (std::string s;;) { 41 | std::cout << prompt; 42 | if (!std::getline(std::cin, s)) { 43 | throw exceptions::operation_canceled_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Operation is canceled by user."); 44 | } 45 | 46 | if (s.empty()) { 47 | return default_val; 48 | } 49 | 50 | try { 51 | val = std::stoi(s, nullptr, 0); 52 | if (min_val <= val && val <= max_val) { 53 | return val; 54 | } else { 55 | throw std::invalid_argument("Out of range."); 56 | } 57 | } catch (std::invalid_argument&) { 58 | std::cout << error_msg << std::endl; 59 | } 60 | } 61 | } 62 | 63 | [[nodiscard]] 64 | navicat_serial_generator CollectInformationNormal() { 65 | navicat_serial_generator sn_generator; 66 | 67 | std::cout << "[*] Select Navicat product:" << std::endl; 68 | std::cout << " 0. DataModeler" << std::endl; 69 | std::cout << " 1. Premium" << std::endl; 70 | std::cout << " 2. MySQL" << std::endl; 71 | std::cout << " 3. PostgreSQL" << std::endl; 72 | std::cout << " 4. Oracle" << std::endl; 73 | std::cout << " 5. SQLServer" << std::endl; 74 | std::cout << " 6. SQLite" << std::endl; 75 | std::cout << " 7. MariaDB" << std::endl; 76 | std::cout << " 8. MongoDB" << std::endl; 77 | std::cout << " 9. ReportViewer" << std::endl; 78 | std::cout << " 10. ChartsCreator" << std::endl; 79 | std::cout << " 11. ChartsViewer" << std::endl; 80 | std::cout << std::endl; 81 | sn_generator.set_software_type(static_cast(read_int(0, 11, "(Input index)> ", "Invalid index."))); 82 | 83 | std::cout << std::endl; 84 | std::cout << "[*] Select product language:" << std::endl; 85 | std::cout << " 0. English" << std::endl; 86 | std::cout << " 1. Simplified Chinese" << std::endl; 87 | std::cout << " 2. Traditional Chinese" << std::endl; 88 | std::cout << " 3. Japanese" << std::endl; 89 | std::cout << " 4. Polish" << std::endl; 90 | std::cout << " 5. Spanish" << std::endl; 91 | std::cout << " 6. French" << std::endl; 92 | std::cout << " 7. German" << std::endl; 93 | std::cout << " 8. Korean" << std::endl; 94 | std::cout << " 9. Russian" << std::endl; 95 | std::cout << " 10. Portuguese" << std::endl; 96 | std::cout << std::endl; 97 | sn_generator.set_software_language(static_cast(read_int(0, 10, "(Input index)> ", "Invalid index."))); 98 | 99 | std::cout << std::endl; 100 | std::cout << "[*] Input major version number:" << std::endl; 101 | sn_generator.set_software_version(read_int(1, 16, 16, "(range: 1 ~ 16, default: 16)> ", "Invalid number.")); 102 | 103 | std::cout << std::endl; 104 | return sn_generator; 105 | } 106 | 107 | [[nodiscard]] 108 | navicat_serial_generator CollectInformationAdvanced() { 109 | navicat_serial_generator sn_generator; 110 | 111 | std::cout << "[*] Navicat Product Signature:" << std::endl; 112 | sn_generator.set_software_type(static_cast(read_int(0x00, 0xff, "(range: 0x00 ~ 0xFF)> ", "Invalid number."))); 113 | 114 | std::cout << std::endl; 115 | std::cout << "[*] Navicat Language Signature 0:" << std::endl; 116 | auto s1 = static_cast(read_int(0x00, 0xff, "(range: 0x00 ~ 0xFF)> ", "Invalid number.")); 117 | 118 | std::cout << std::endl; 119 | std::cout << "[*] Navicat Language Signature 1:" << std::endl; 120 | auto s2 = static_cast(read_int(0x00, 0xff, "(range: 0x00 ~ 0xFF)> ", "Invalid number.")); 121 | 122 | sn_generator.set_software_language(s1, s2); 123 | 124 | std::cout << std::endl; 125 | std::cout << "[*] Input major version number:" << std::endl; 126 | sn_generator.set_software_version(read_int(1, 16, 16, "(range: 1 ~ 16, default: 16)> ", "Invalid number.")); 127 | 128 | std::cout << std::endl; 129 | return sn_generator; 130 | } 131 | } 132 | 133 | #undef NKG_CURRENT_SOURCE_FILE 134 | #undef NKG_CURRENT_SOURCE_LINE 135 | -------------------------------------------------------------------------------- /navicat-keygen/GenerateLicense.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "exceptions/operation_canceled_exception.hpp" 7 | #include "exceptions/unix_exception.hpp" 8 | 9 | #include "resource_wrapper.hpp" 10 | #include "resource_traits/unix_os/file_descriptor.hpp" 11 | 12 | #include "rsa_cipher.hpp" 13 | #include "navicat_serial_generator.hpp" 14 | #include "base64_rfc4648.hpp" 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-keygen\\GenerateLicense.cpp" 21 | #define NKG_CURRENT_SOURCE_LINE() __LINE__ 22 | 23 | namespace nkg { 24 | 25 | void GenerateLicenseText(const rsa_cipher& cipher, const navicat_serial_generator& sn_generator) { 26 | std::string u8_username; 27 | std::string u8_organization; 28 | 29 | std::string b64_request_code; 30 | std::vector request_code; 31 | std::string u8_request_info; 32 | std::string u8_response_info; 33 | std::vector response_code; 34 | std::string b64_response_code; 35 | 36 | std::cout << "[*] Your name: "; 37 | if (!std::getline(std::cin, u8_username)) { 38 | throw exceptions::operation_canceled_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Operation is canceled by user."); 39 | } 40 | 41 | std::cout << "[*] Your organization: "; 42 | if (!std::getline(std::cin, u8_organization)) { 43 | throw exceptions::operation_canceled_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Operation is canceled by user."); 44 | } 45 | 46 | std::cout << std::endl; 47 | 48 | std::cout << "[*] Input request code in Base64: (Double press ENTER to end)" << std::endl; 49 | while (true) { 50 | std::string temp; 51 | if (!std::getline(std::cin, temp)) { 52 | throw exceptions::operation_canceled_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Operation is canceled by user."); 53 | } 54 | 55 | if (temp.empty()) { 56 | break; 57 | } 58 | 59 | b64_request_code.append(temp); 60 | } 61 | 62 | request_code = base64_rfc4648::decode(b64_request_code); 63 | if (request_code.size() != 256) { 64 | throw ::nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Broken request code. %zu", request_code.size())); 65 | } 66 | 67 | u8_request_info.resize((cipher.bits() + 7) / 8); 68 | u8_request_info.resize(cipher.private_decrypt(request_code.data(), request_code.size(), u8_request_info.data(), RSA_PKCS1_PADDING)); 69 | 70 | std::cout << "[*] Request Info:" << std::endl; 71 | std::cout << u8_request_info << std::endl; 72 | std::cout << std::endl; 73 | 74 | rapidjson::Document json; 75 | rapidjson::Value N_Key; 76 | rapidjson::Value N_Value; 77 | rapidjson::Value O_Key; 78 | rapidjson::Value O_Value; 79 | rapidjson::Value T_Key; 80 | rapidjson::Value T_Value; 81 | rapidjson::StringBuffer buffer; 82 | rapidjson::Writer writer(buffer); 83 | 84 | // 85 | // Begin to parse 86 | // 87 | json.Parse(u8_request_info.c_str()); 88 | // 89 | // Remove "Platform" info 90 | // 91 | json.RemoveMember("P"); 92 | // 93 | // Set "Name" info 94 | // 95 | N_Key.SetString("N", 1); 96 | N_Value.SetString(u8_username.c_str(), static_cast(u8_username.length())); 97 | // 98 | // Set "Organization" info 99 | // 100 | O_Key.SetString("O", 1); 101 | O_Value.SetString(u8_organization.c_str(), static_cast(u8_organization.length())); 102 | // 103 | // Set "Time" info 104 | // 105 | T_Key.SetString("T", 1); 106 | T_Value.SetUint(static_cast(std::time(nullptr))); 107 | // 108 | // Add "Name", "Organization" and "Time" 109 | // 110 | json.AddMember(N_Key, N_Value, json.GetAllocator()); 111 | json.AddMember(O_Key, O_Value, json.GetAllocator()); 112 | json.AddMember(T_Key, T_Value, json.GetAllocator()); 113 | 114 | json.Accept(writer); 115 | if (buffer.GetSize() > 240) { 116 | throw ::nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Response Info is too long."); 117 | } 118 | 119 | u8_response_info.assign(buffer.GetString(), buffer.GetSize()); 120 | 121 | std::cout << "[*] Response Info:" << std::endl; 122 | std::cout << u8_response_info << std::endl; 123 | std::cout << std::endl; 124 | 125 | response_code.resize((cipher.bits() + 7) / 8); 126 | response_code.resize(cipher.private_encrypt(u8_response_info.data(), u8_response_info.size(), response_code.data(), RSA_PKCS1_PADDING)); 127 | 128 | b64_response_code = base64_rfc4648::encode(response_code); 129 | 130 | std::cout << "[*] Activation Code:" << std::endl; 131 | std::cout << b64_response_code << std::endl; 132 | std::cout << std::endl; 133 | } 134 | 135 | void GenerateLicenseBinary(const rsa_cipher& cipher, const navicat_serial_generator& sn_generator) { 136 | std::string u8_serial_number = sn_generator.serial_number(); 137 | 138 | std::string u8_username; 139 | std::string u8_organization; 140 | 141 | std::string u8_response_info; 142 | std::vector response_code; 143 | 144 | std::cout << "[*] Your name: "; 145 | if (!std::getline(std::cin, u8_username)) { 146 | throw exceptions::operation_canceled_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Operation is canceled by user."); 147 | } 148 | 149 | std::cout << "[*] Your organization: "; 150 | if (!std::getline(std::cin, u8_organization)) { 151 | throw exceptions::operation_canceled_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Operation is canceled by user."); 152 | } 153 | 154 | std::cout << std::endl; 155 | 156 | rapidjson::Document json; 157 | rapidjson::Value N_Key; 158 | rapidjson::Value N_Value; 159 | rapidjson::Value O_Key; 160 | rapidjson::Value O_Value; 161 | rapidjson::Value T_Key; 162 | rapidjson::Value T_Value; 163 | rapidjson::Value K_Key; 164 | rapidjson::Value K_Value; 165 | rapidjson::StringBuffer buffer; 166 | rapidjson::Writer writer(buffer); 167 | 168 | json.Parse("{}"); 169 | K_Key.SetString("K", 1); 170 | K_Value.SetString(u8_serial_number.c_str(), static_cast(u8_serial_number.length())); 171 | N_Key.SetString("N", 1); 172 | N_Value.SetString(u8_username.c_str(), static_cast(u8_username.length())); 173 | O_Key.SetString("O", 1); 174 | O_Value.SetString(u8_organization.c_str(), static_cast(u8_organization.length())); 175 | T_Key.SetString("T", 1); 176 | T_Value.SetUint(static_cast(std::time(nullptr))); 177 | 178 | json.AddMember(K_Key, K_Value, json.GetAllocator()); 179 | json.AddMember(N_Key, N_Value, json.GetAllocator()); 180 | json.AddMember(O_Key, O_Value, json.GetAllocator()); 181 | json.AddMember(T_Key, T_Value, json.GetAllocator()); 182 | 183 | json.Accept(writer); 184 | if (buffer.GetSize() > 240) { 185 | throw ::nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Response Info is too long."); 186 | } 187 | 188 | u8_response_info.assign(buffer.GetString(), buffer.GetSize()); 189 | 190 | std::cout << "[*] Response Info:" << std::endl; 191 | std::cout << u8_response_info << std::endl; 192 | std::cout << std::endl; 193 | 194 | response_code.resize((cipher.bits() + 7) / 8); 195 | response_code.resize(cipher.private_encrypt(u8_response_info.data(), u8_response_info.size(), response_code.data(), RSA_PKCS1_PADDING)); 196 | 197 | resource_wrapper license_file{ resource_traits::unix_os::file_descriptor{}, open("license_file", O_WRONLY | O_CREAT | O_TRUNC, 0666) }; 198 | if (!license_file.is_valid()) { 199 | throw exceptions::unix_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), errno, u8"open failed."); 200 | } 201 | 202 | if (write(license_file.get(), response_code.data(), response_code.size()) < 0) { 203 | throw exceptions::unix_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), errno, u8"write failed."); 204 | } 205 | 206 | std::cout << "[+] license_file has been generated." << std::endl; 207 | } 208 | } 209 | 210 | #undef NKG_CURRENT_SOURCE_FILE 211 | #undef NKG_CURRENT_SOURCE_LINE 212 | -------------------------------------------------------------------------------- /navicat-keygen/base32_rfc4648.cpp: -------------------------------------------------------------------------------- 1 | #include "base32_rfc4648.hpp" 2 | #include 3 | #include 4 | 5 | #define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-keygen\\base32_rfc4648.cpp" 6 | #define NKG_CURRENT_SOURCE_LINE() __LINE__ 7 | 8 | namespace nkg { 9 | 10 | char base32_rfc4648::symbol(alphabet_index_t idx) { 11 | return alphabet[idx]; 12 | } 13 | 14 | base32_rfc4648::alphabet_index_t base32_rfc4648::reverse_symbol(char c) { 15 | if ('A' <= c && c <= 'Z') { 16 | return c - 'A'; 17 | } else if ('2' <= c && c <= '7') { 18 | return c - '2' + 26; 19 | } else { 20 | throw decoding_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Non-base32 digit is found."); 21 | } 22 | } 23 | 24 | std::string base32_rfc4648::encode(const std::vector& data) { 25 | return encode(data.data(), data.size()); 26 | } 27 | 28 | std::string base32_rfc4648::encode(const void* data_ptr, size_t data_size) { 29 | std::string retval; 30 | 31 | if (data_size) { 32 | retval.reserve((data_size * 8 + 4) / 5); 33 | 34 | auto p = reinterpret_cast(data_ptr); 35 | alphabet_index_t left_bits = 0; 36 | alphabet_index_t bit_buffer = 0; 37 | for (size_t i = 0; i < data_size; ++i) { 38 | bit_buffer = (bit_buffer << 8) | p[i]; 39 | left_bits += 8; 40 | 41 | while (left_bits >= 5) { 42 | alphabet_index_t idx = (bit_buffer >> (left_bits - 5)) & 0x1f; 43 | retval.push_back(symbol(idx)); 44 | left_bits -= 5; 45 | } 46 | } 47 | 48 | if (left_bits > 0) { 49 | alphabet_index_t idx = (bit_buffer << (5 - left_bits)) & 0x1f; 50 | retval.push_back(symbol(idx)); 51 | } 52 | 53 | switch (data_size % 5) { 54 | case 0: 55 | break; 56 | case 1: 57 | retval.append(6, padding_character); 58 | break; 59 | case 2: 60 | retval.append(4, padding_character); 61 | break; 62 | case 3: 63 | retval.append(3, padding_character); 64 | break; 65 | case 4: 66 | retval.append(1, padding_character); 67 | break; 68 | default: 69 | __builtin_unreachable(); 70 | } 71 | } 72 | 73 | return retval; 74 | } 75 | 76 | std::vector base32_rfc4648::decode(std::string_view b32_string) { 77 | if (b32_string.length() % 8 == 0) { 78 | std::vector retval; 79 | 80 | size_t count_of_padding = std::distance(b32_string.crbegin(), std::find_if_not(b32_string.crbegin(), b32_string.crend(), [](char c) -> bool { return c == padding_character; })); 81 | switch (count_of_padding) { 82 | case 1: 83 | retval.reserve(b32_string.length() / 8 * 5 - (5 - 4)); 84 | break; 85 | case 3: 86 | retval.reserve(b32_string.length() / 8 * 5 - (5 - 3)); 87 | break; 88 | case 4: 89 | retval.reserve(b32_string.length() / 8 * 5 - (5 - 2)); 90 | break; 91 | case 6: 92 | retval.reserve(b32_string.length() / 8 * 5 - (5 - 1)); 93 | break; 94 | default: 95 | throw decoding_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Incorrect padding"); 96 | } 97 | 98 | size_t count_of_encoded = b32_string.length() - count_of_padding; 99 | 100 | alphabet_index_t left_bits = 0; 101 | alphabet_index_t bit_buffer = 0; 102 | for (size_t i = 0; i < count_of_encoded; ++i) { 103 | bit_buffer = (bit_buffer << 5) | reverse_symbol(b32_string[i]); 104 | left_bits += 5; 105 | 106 | while (left_bits >= 8) { 107 | auto val = static_cast((bit_buffer >> (left_bits - 8)) & 0xff); 108 | retval.push_back(val); 109 | left_bits -= 8; 110 | } 111 | } 112 | 113 | return retval; 114 | } else { 115 | throw decoding_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Incorrect padding"); 116 | } 117 | } 118 | 119 | } 120 | 121 | #undef NKG_CURRENT_SOURCE_LINE 122 | #undef NKG_CURRENT_SOURCE_FILE 123 | -------------------------------------------------------------------------------- /navicat-keygen/base32_rfc4648.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "exception.hpp" 5 | 6 | namespace nkg { 7 | 8 | struct base32_rfc4648 { 9 | using alphabet_index_t = size_t; 10 | 11 | static constexpr const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; 12 | static constexpr const char padding_character = '='; 13 | 14 | class decoding_error; 15 | 16 | static char symbol(alphabet_index_t idx); 17 | 18 | static alphabet_index_t reverse_symbol(char c); 19 | 20 | static std::string encode(const std::vector& data); 21 | 22 | static std::string encode(const void* data_ptr, size_t data_size); 23 | 24 | static std::vector decode(std::string_view b32_string); 25 | }; 26 | 27 | class base32_rfc4648::decoding_error : public ::nkg::exception { 28 | using ::nkg::exception::exception; 29 | }; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /navicat-keygen/base64_rfc4648.cpp: -------------------------------------------------------------------------------- 1 | #include "base64_rfc4648.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "resource_wrapper.hpp" 7 | #include "resource_traits/openssl/bio.hpp" 8 | #include "resource_traits/openssl/bio_chain.hpp" 9 | 10 | #define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-keygen\\base64_rfc4648.cpp" 11 | #define NKG_CURRENT_SOURCE_LINE() __LINE__ 12 | 13 | namespace nkg { 14 | 15 | std::string base64_rfc4648::encode(const std::vector& data) { 16 | resource_wrapper bio_b64{ resource_traits::openssl::bio_chain{}, BIO_new(BIO_f_base64()) }; 17 | if (!bio_b64.is_valid()) { 18 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed."); 19 | } 20 | 21 | BIO_set_flags(bio_b64.get(), BIO_FLAGS_BASE64_NO_NL); 22 | 23 | resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) }; 24 | if (!bio_memory.is_valid()) { 25 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed."); 26 | } 27 | 28 | BIO_push(bio_b64.get(), bio_memory.get()); 29 | 30 | for (size_t written_size = 0, left_size = data.size(); left_size != 0;) { 31 | int size_to_write = static_cast(std::min(left_size, static_cast(INT_MAX))); 32 | 33 | int r = BIO_write(bio_b64.get(), data.data() + written_size, size_to_write); 34 | if (r > 0) { 35 | written_size += r; 36 | left_size -= r; 37 | } else { 38 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_write failed."); 39 | } 40 | } 41 | 42 | BIO_flush(bio_b64.get()); 43 | 44 | const char* pch = nullptr; 45 | long lch = BIO_get_mem_data(bio_memory.get(), &pch); 46 | 47 | bio_memory.discard(); // the bio_chain `bio_b64` will free it 48 | 49 | return std::string(pch, lch); 50 | } 51 | 52 | std::vector base64_rfc4648::decode(std::string_view b64_string) { 53 | resource_wrapper bio_b64{ resource_traits::openssl::bio_chain{}, BIO_new(BIO_f_base64()) }; 54 | if (!bio_b64.is_valid()) { 55 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed."); 56 | } 57 | 58 | BIO_set_flags(bio_b64.get(), BIO_FLAGS_BASE64_NO_NL); 59 | 60 | resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) }; 61 | if (!bio_memory.is_valid()) { 62 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed."); 63 | } 64 | 65 | BIO_push(bio_b64.get(), bio_memory.get()); 66 | 67 | for (size_t written_length = 0, left_length = b64_string.length(); left_length != 0;) { 68 | int length_to_write = static_cast(std::min(left_length, static_cast(INT_MAX))); 69 | 70 | int r = BIO_write(bio_memory.get(), b64_string.data() + written_length, length_to_write); 71 | if (r > 0) { 72 | written_length += r; 73 | left_length -= r; 74 | } else { 75 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_write failed."); 76 | } 77 | } 78 | 79 | std::vector retval; 80 | retval.reserve(b64_string.length() * 3 / 4 + 1); 81 | 82 | for (uint8_t buf[256];;) { 83 | auto len = BIO_read(bio_b64.get(), buf, sizeof(buf)); 84 | if (len > 0) { 85 | retval.insert(retval.end(), buf, buf + len); 86 | } else { 87 | break; 88 | } 89 | } 90 | 91 | bio_memory.discard(); // the bio_chain `bio_b64` will free it 92 | 93 | return retval; 94 | } 95 | 96 | } 97 | 98 | #undef NKG_CURRENT_SOURCE_FILE 99 | #undef NKG_CURRENT_SOURCE_LINE 100 | -------------------------------------------------------------------------------- /navicat-keygen/base64_rfc4648.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "exception.hpp" 5 | 6 | namespace nkg { 7 | 8 | struct base64_rfc4648 { 9 | class backend_error; 10 | 11 | static std::string encode(const std::vector& data); 12 | 13 | static std::vector decode(std::string_view str_b64); 14 | }; 15 | 16 | class base64_rfc4648::backend_error : public ::nkg::exception { 17 | using ::nkg::exception::exception; 18 | }; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /navicat-keygen/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "exception.hpp" 6 | #include "exceptions/operation_canceled_exception.hpp" 7 | 8 | #include "base64_rfc4648.hpp" 9 | #include "navicat_serial_generator.hpp" 10 | #include "rsa_cipher.hpp" 11 | 12 | #define NKG_CURRENT_SOURCE_FILE() ".\\navicat-keygen\\wmain.cpp" 13 | #define NKG_CURRENT_SOURCE_LINE() __LINE__ 14 | 15 | namespace nkg { 16 | using fnCollectInformation = std::function; 17 | using fnGenerateLicense = std::function; 18 | 19 | navicat_serial_generator CollectInformationNormal(); 20 | navicat_serial_generator CollectInformationAdvanced(); 21 | void GenerateLicenseText(const rsa_cipher& cipher, const navicat_serial_generator& sn_generator); 22 | void GenerateLicenseBinary(const rsa_cipher& cipher, const navicat_serial_generator& sn_generator); 23 | } 24 | 25 | void welcome() { 26 | puts("***************************************************"); 27 | puts("* navicat-keygen by @DoubleLabyrinth *"); 28 | puts("* version: 16.0.7.0-3 *"); 29 | puts("***************************************************"); 30 | puts(""); 31 | } 32 | 33 | void help() { 34 | puts("Usage:"); 35 | puts(" navicat-keygen <--bin|-text> [--adv] "); 36 | puts(""); 37 | puts(" <--bin|--text> Specify \"--bin\" to generate \"license_file\" used by Navicat 11."); 38 | puts(" Specify \"--text\" to generate base64-encoded activation code."); 39 | puts(" This parameter is mandatory."); 40 | puts(""); 41 | puts(" [--adv] Enable advance mode."); 42 | puts(" This parameter is optional."); 43 | puts(""); 44 | puts(" A path to an RSA-2048 private key file."); 45 | puts(" This parameter is mandatory."); 46 | puts(""); 47 | puts("Example:"); 48 | puts(" navicat-keygen --text ./RegPrivateKey.pem"); 49 | } 50 | 51 | int main(int argc, char* argv[]) { 52 | welcome(); 53 | 54 | if (argc == 3 || argc == 4) { 55 | nkg::fnCollectInformation lpfnCollectInformation; 56 | nkg::fnGenerateLicense lpfnGenerateLicense; 57 | 58 | if (strcmp(argv[1], "--bin") == 0) { 59 | lpfnGenerateLicense = nkg::GenerateLicenseBinary; 60 | } else if (strcmp(argv[1], "--text") == 0) { 61 | lpfnGenerateLicense = nkg::GenerateLicenseText; 62 | } else { 63 | help(); 64 | return -1; 65 | } 66 | 67 | if (argc == 3) { 68 | lpfnCollectInformation = nkg::CollectInformationNormal; 69 | } else if (argc == 4 && strcmp(argv[2], "--adv") == 0) { 70 | lpfnCollectInformation = nkg::CollectInformationAdvanced; 71 | } else { 72 | help(); 73 | return -1; 74 | } 75 | 76 | try { 77 | nkg::rsa_cipher cipher; 78 | 79 | cipher.import_private_key_file(argv[argc - 1]); 80 | if (cipher.bits() != 2048) { 81 | throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "RSA key length != 2048 bits.") 82 | .push_hint("You must provide an RSA key whose modulus length is 2048 bits."); 83 | } 84 | 85 | auto sn_generator = lpfnCollectInformation(); 86 | sn_generator.generate(); 87 | 88 | puts("[*] Serial number:"); 89 | puts(sn_generator.serial_number_formatted().c_str()); 90 | puts(""); 91 | 92 | lpfnGenerateLicense(cipher, sn_generator); 93 | 94 | return 0; 95 | } catch (nkg::exceptions::operation_canceled_exception&) { 96 | return -1; 97 | } catch (nkg::exception& e) { 98 | printf("[-] %s:%d ->\n", e.source_file().c_str(), e.source_line()); 99 | printf(" %s\n", e.custom_message().c_str()); 100 | 101 | if (e.error_code_exists()) { 102 | printf(" %s (0x%zx)\n", e.error_string().c_str(), e.error_code()); 103 | } 104 | 105 | for (auto& hint : e.hints()) { 106 | printf(" Hints: %s\n", hint.c_str()); 107 | } 108 | 109 | return -1; 110 | } catch (std::exception& e) { 111 | printf("[-] %s\n", e.what()); 112 | return -1; 113 | } 114 | } else { 115 | help(); 116 | return -1; 117 | } 118 | } 119 | 120 | #undef NKG_CURRENT_SOURCE_FILE 121 | #undef NKG_CURRENT_SOURCE_LINE 122 | -------------------------------------------------------------------------------- /navicat-keygen/navicat_serial_generator.cpp: -------------------------------------------------------------------------------- 1 | #include "navicat_serial_generator.hpp" 2 | #include 3 | 4 | #include 5 | #include 6 | #if (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x 7 | #include 8 | #endif 9 | 10 | #include "resource_wrapper.hpp" 11 | #include "resource_traits/openssl/evp_cipher_ctx.hpp" 12 | 13 | #include 14 | #include "base32_rfc4648.hpp" 15 | 16 | #define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-keygen\\navicat_serial_generator.cpp" 17 | #define NKG_CURRENT_SOURCE_LINE() __LINE__ 18 | 19 | namespace nkg { 20 | 21 | char navicat_serial_generator::_replace_confusing_chars(char c) { 22 | if (c == 'I') { 23 | return '8'; 24 | } else if (c == 'O') { 25 | return '9'; 26 | } else { 27 | return c; 28 | } 29 | }; 30 | 31 | navicat_serial_generator::navicat_serial_generator() noexcept : 32 | m_data{ 0x68 , 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32 }, m_des_key{} {} 33 | 34 | void navicat_serial_generator::set_software_language(navicat_software_language lang) noexcept { 35 | switch (lang) { 36 | case navicat_software_language::English: 37 | m_data[5] = 0xAC; // Must be 0xAC for English version. 38 | m_data[6] = 0x88; // Must be 0x88 for English version. 39 | break; 40 | case navicat_software_language::SimplifiedChinese: 41 | m_data[5] = 0xCE; // Must be 0xCE for Simplified Chinese version. 42 | m_data[6] = 0x32; // Must be 0x32 for Simplified Chinese version. 43 | break; 44 | case navicat_software_language::TraditionalChinese: 45 | m_data[5] = 0xAA; // Must be 0xAA for Traditional Chinese version. 46 | m_data[6] = 0x99; // Must be 0x99 for Traditional Chinese version. 47 | break; 48 | case navicat_software_language::Japanese: 49 | m_data[5] = 0xAD; // Must be 0xAD for Japanese version. Discoverer: @dragonflylee 50 | m_data[6] = 0x82; // Must be 0x82 for Japanese version. Discoverer: @dragonflylee 51 | break; 52 | case navicat_software_language::Polish: 53 | m_data[5] = 0xBB; // Must be 0xBB for Polish version. Discoverer: @dragonflylee 54 | m_data[6] = 0x55; // Must be 0x55 for Polish version. Discoverer: @dragonflylee 55 | break; 56 | case navicat_software_language::Spanish: 57 | m_data[5] = 0xAE; // Must be 0xAE for Spanish version. Discoverer: @dragonflylee 58 | m_data[6] = 0x10; // Must be 0x10 for Spanish version. Discoverer: @dragonflylee 59 | break; 60 | case navicat_software_language::French: 61 | m_data[5] = 0xFA; // Must be 0xFA for French version. Discoverer: @Deltafox79 62 | m_data[6] = 0x20; // Must be 0x20 for French version. Discoverer: @Deltafox79 63 | break; 64 | case navicat_software_language::German: 65 | m_data[5] = 0xB1; // Must be 0xB1 for German version. Discoverer: @dragonflylee 66 | m_data[6] = 0x60; // Must be 0x60 for German version. Discoverer: @dragonflylee 67 | break; 68 | case navicat_software_language::Korean: 69 | m_data[5] = 0xB5; // Must be 0xB5 for Korean version. Discoverer: @dragonflylee 70 | m_data[6] = 0x60; // Must be 0x60 for Korean version. Discoverer: @dragonflylee 71 | break; 72 | case navicat_software_language::Russian: 73 | m_data[5] = 0xEE; // Must be 0xB5 for Russian version. Discoverer: @dragonflylee 74 | m_data[6] = 0x16; // Must be 0x60 for Russian version. Discoverer: @dragonflylee 75 | break; 76 | case navicat_software_language::Portuguese: 77 | m_data[5] = 0xCD; // Must be 0xCD for Portuguese version. Discoverer: @dragonflylee 78 | m_data[6] = 0x49; // Must be 0x49 for Portuguese version. Discoverer: @dragonflylee 79 | break; 80 | default: 81 | __builtin_unreachable(); 82 | } 83 | } 84 | 85 | void navicat_serial_generator::set_software_language(uint8_t lang_sig0, uint8_t lang_sig1) noexcept { 86 | m_data[5] = lang_sig0; 87 | m_data[6] = lang_sig1; 88 | } 89 | 90 | void navicat_serial_generator::set_software_type(navicat_software_type software_type) noexcept { 91 | switch (software_type) { 92 | case navicat_software_type::DataModeler: 93 | m_data[7] = 0x84; 94 | break; 95 | case navicat_software_type::Premium: 96 | m_data[7] = 0x65; 97 | break; 98 | case navicat_software_type::MySQL: 99 | m_data[7] = 0x68; 100 | break; 101 | case navicat_software_type::PostgreSQL: 102 | m_data[7] = 0x6C; 103 | break; 104 | case navicat_software_type::Oracle: 105 | m_data[7] = 0x70; 106 | break; 107 | case navicat_software_type::SQLServer: 108 | m_data[7] = 0x74; 109 | break; 110 | case navicat_software_type::SQLite: 111 | m_data[7] = 0x78; 112 | break; 113 | case navicat_software_type::MariaDB: 114 | m_data[7] = 0x7C; 115 | break; 116 | case navicat_software_type::MongoDB: 117 | m_data[7] = 0x80; 118 | break; 119 | case navicat_software_type::ReportViewer: 120 | m_data[7] = 0xb; 121 | break; 122 | case navicat_software_type::ChartsCreator: 123 | m_data[7] = 0x86; 124 | break; 125 | case navicat_software_type::ChartsViewer: 126 | m_data[7] = 0x88; 127 | break; 128 | default: 129 | __builtin_unreachable(); 130 | } 131 | } 132 | 133 | void navicat_serial_generator::set_software_type(uint8_t software_type_sig) noexcept { 134 | m_data[7] = software_type_sig; 135 | } 136 | 137 | void navicat_serial_generator::set_software_version(int ver) { 138 | if (1 <= ver && ver < 16) { 139 | static_assert(sizeof(m_des_key) == sizeof(s_des_key0)); 140 | 141 | m_data[8] = static_cast((ver << 4) | (m_data[8] & 0x0f)); 142 | memcpy(m_des_key, s_des_key0, sizeof(s_des_key0)); 143 | } else if (16 <= ver && ver < 32) { 144 | static_assert(sizeof(m_des_key) == sizeof(s_des_key1)); 145 | 146 | m_data[8] = static_cast(((ver - 16) << 4) | (m_data[8] & 0x0f)); 147 | memcpy(m_des_key, s_des_key1, sizeof(s_des_key1)); 148 | } else { 149 | throw version_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Invalid navicat version."); 150 | } 151 | } 152 | 153 | void navicat_serial_generator::generate() { 154 | RAND_bytes(m_data + 2, 3); 155 | 156 | #if (OPENSSL_VERSION_NUMBER & 0xf0000000) == 0x30000000 // for openssl 3.x.x 157 | if (!OSSL_PROVIDER_available(nullptr, "legacy")) { 158 | if (OSSL_PROVIDER_load(nullptr, "legacy") == nullptr) { 159 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"OSSL_PROVIDER_load failed."); 160 | } 161 | } 162 | #endif 163 | 164 | resource_wrapper evp_cipher_context{ resource_traits::openssl::evp_cipher_ctx{}, EVP_CIPHER_CTX_new() }; 165 | if (!evp_cipher_context.is_valid()) { 166 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_CIPHER_CTX_new failed."); 167 | } 168 | 169 | if (EVP_EncryptInit_ex(evp_cipher_context.get(), EVP_des_ecb(), nullptr, m_des_key, nullptr) <= 0) { // return 1 for success and 0 for failure 170 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_EncryptInit failed."); 171 | } 172 | 173 | if (int _; EVP_EncryptUpdate(evp_cipher_context.get(), m_data + 2, &_, m_data + 2, 8) <= 0) { // return 1 for success and 0 for failure 174 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"EVP_EncryptUpdate failed."); 175 | } 176 | 177 | m_serial_number = base32_rfc4648::encode(m_data, sizeof(m_data)); 178 | std::transform(m_serial_number.begin(), m_serial_number.end(), m_serial_number.begin(), _replace_confusing_chars); 179 | 180 | std::string_view sn = m_serial_number; 181 | m_serial_number_formatted = fmt::format("{}-{}-{}-{}", sn.substr(0, 4), sn.substr(4, 4), sn.substr(8, 4), sn.substr(12, 4)); 182 | } 183 | 184 | [[nodiscard]] 185 | const std::string& navicat_serial_generator::serial_number() const noexcept { 186 | return m_serial_number; 187 | } 188 | 189 | [[nodiscard]] 190 | const std::string& navicat_serial_generator::serial_number_formatted() const noexcept { 191 | return m_serial_number_formatted; 192 | } 193 | } 194 | 195 | #undef NKG_CURRENT_SOURCE_LINE 196 | #undef NKG_CURRENT_SOURCE_FILE 197 | -------------------------------------------------------------------------------- /navicat-keygen/navicat_serial_generator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "exception.hpp" 5 | 6 | namespace nkg { 7 | 8 | enum class navicat_software_language { 9 | English, 10 | SimplifiedChinese, 11 | TraditionalChinese, 12 | Japanese, 13 | Polish, 14 | Spanish, 15 | French, 16 | German, 17 | Korean, 18 | Russian, 19 | Portuguese 20 | }; 21 | 22 | enum class navicat_software_type { 23 | DataModeler, 24 | Premium, 25 | MySQL, 26 | PostgreSQL, 27 | Oracle, 28 | SQLServer, 29 | SQLite, 30 | MariaDB, 31 | MongoDB, 32 | ReportViewer, 33 | ChartsCreator, 34 | ChartsViewer 35 | }; 36 | 37 | class navicat_serial_generator { 38 | public: 39 | class version_error; 40 | class backend_error; 41 | 42 | private: 43 | static inline const uint8_t s_des_key0[8] = { 0x64, 0xAD, 0xF3, 0x2F, 0xAE, 0xF2, 0x1A, 0x27 }; 44 | static inline const uint8_t s_des_key1[8] = { 0xE9, 0x7F, 0xB0, 0x60, 0x77, 0x45, 0x90, 0xAE }; 45 | 46 | uint8_t m_data[10]; 47 | uint8_t m_des_key[8]; 48 | std::string m_serial_number; 49 | std::string m_serial_number_formatted; 50 | 51 | static char _replace_confusing_chars(char c); 52 | 53 | public: 54 | navicat_serial_generator() noexcept; 55 | 56 | void set_software_language(navicat_software_language lang) noexcept; 57 | void set_software_language(uint8_t lang_sig0, uint8_t lang_sig1) noexcept; 58 | 59 | void set_software_type(navicat_software_type software_type) noexcept; 60 | void set_software_type(uint8_t software_type_sig) noexcept; 61 | 62 | void set_software_version(int Version); 63 | 64 | void generate(); 65 | 66 | [[nodiscard]] 67 | const std::string& serial_number() const noexcept; 68 | 69 | [[nodiscard]] 70 | const std::string& serial_number_formatted() const noexcept; 71 | }; 72 | 73 | class navicat_serial_generator::version_error : public ::nkg::exception { 74 | using ::nkg::exception::exception; 75 | }; 76 | 77 | class navicat_serial_generator::backend_error : public ::nkg::exception { 78 | using ::nkg::exception::exception; 79 | }; 80 | 81 | } 82 | -------------------------------------------------------------------------------- /navicat-patcher/amd64_emulator.cpp: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | #include "amd64_emulator.hpp" 3 | #include "exceptions/key_exception.hpp" 4 | #include "resource_traits/unicorn/unicorn_alloc.hpp" 5 | 6 | #define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-patcher\\amd64_emulator.cpp" 7 | #define NKG_CURRENT_SOURCE_LINE() __LINE__ 8 | 9 | namespace nkg { 10 | 11 | void amd64_emulator::_unicorn_hookcode_cb_stub(uc_engine* uc, uint64_t address, uint32_t size, void* user_data) { 12 | auto hook_stub_ctx = reinterpret_cast(user_data); 13 | auto& hook_callback = std::any_cast&>(hook_stub_ctx->self->m_unicorn_hook_callbacks[hook_stub_ctx->unicorn_hook_handle]); 14 | hook_callback(address, size); 15 | } 16 | 17 | void amd64_emulator::_unicorn_hookmem_cb_stub(uc_engine* uc, uc_mem_type type, uint64_t address, int size, int64_t value, void* user_data) { 18 | auto hook_stub_ctx = reinterpret_cast(user_data); 19 | auto& hook_callback = std::any_cast&>(hook_stub_ctx->self->m_unicorn_hook_callbacks[hook_stub_ctx->unicorn_hook_handle]); 20 | hook_callback(type, address, static_cast(size), value); 21 | } 22 | 23 | bool amd64_emulator::_unicorn_eventmem_cb_stub(uc_engine* uc, uc_mem_type type, uint64_t address, int size, int64_t value, void* user_data) { 24 | auto hook_stub_ctx = reinterpret_cast(user_data); 25 | auto& hook_callback = std::any_cast&>(hook_stub_ctx->self->m_unicorn_hook_callbacks[hook_stub_ctx->unicorn_hook_handle]); 26 | return hook_callback(type, address, static_cast(size), value); 27 | } 28 | 29 | amd64_emulator::amd64_emulator() { 30 | auto err = uc_open(UC_ARCH_X86, UC_MODE_64, m_unicorn_engine.unsafe_addressof()); 31 | if (err != UC_ERR_OK) { 32 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_open failed."); 33 | } 34 | } 35 | 36 | void amd64_emulator::reg_read(int regid, void* value) { 37 | auto err = uc_reg_read(m_unicorn_engine.get(), regid, value); 38 | if (err != UC_ERR_OK) { 39 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_reg_read failed."); 40 | } 41 | } 42 | 43 | void amd64_emulator::reg_write(int regid, const void* value) { 44 | auto err = uc_reg_write(m_unicorn_engine.get(), regid, value); 45 | if (err != UC_ERR_OK) { 46 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_reg_write failed."); 47 | } 48 | } 49 | 50 | uint64_t amd64_emulator::msr_read(uint32_t rid) { 51 | uc_x86_msr msr; 52 | msr.rid = rid; 53 | 54 | auto err = uc_reg_read(m_unicorn_engine.get(), UC_X86_REG_MSR, &msr); 55 | if (err != UC_ERR_OK) { 56 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_reg_write failed."); 57 | } 58 | 59 | return msr.value; 60 | } 61 | 62 | void amd64_emulator::msr_write(uint32_t rid, uint64_t value) { 63 | uc_x86_msr msr; 64 | msr.rid = rid; 65 | msr.value = value; 66 | 67 | auto err = uc_reg_write(m_unicorn_engine.get(), UC_X86_REG_MSR, &msr); 68 | if (err != UC_ERR_OK) { 69 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_reg_write failed."); 70 | } 71 | } 72 | 73 | void amd64_emulator::mem_map(uint64_t address, size_t size, uint32_t perms) { 74 | auto err = uc_mem_map(m_unicorn_engine.get(), address, size, perms); 75 | if (err != UC_ERR_OK) { 76 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_mem_map failed."); 77 | } 78 | } 79 | 80 | void amd64_emulator::mem_unmap(uint64_t address, size_t size) { 81 | auto err = uc_mem_unmap(m_unicorn_engine.get(), address, size); 82 | if (err != UC_ERR_OK) { 83 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_mem_unmap failed."); 84 | } 85 | } 86 | 87 | void amd64_emulator::mem_read(uint64_t address, void* buf, size_t size) { 88 | auto err = uc_mem_read(m_unicorn_engine.get(), address, buf, size); 89 | if (err != UC_ERR_OK) { 90 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_mem_read failed."); 91 | } 92 | } 93 | 94 | std::vector amd64_emulator::mem_read(uint64_t address, size_t size) { 95 | std::vector ret_buf(size); 96 | 97 | auto err = uc_mem_read(m_unicorn_engine.get(), address, ret_buf.data(), ret_buf.size()); 98 | if (err != UC_ERR_OK) { 99 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_mem_read failed."); 100 | } 101 | 102 | return ret_buf; 103 | } 104 | 105 | void amd64_emulator::mem_write(uint64_t address, const void* buf, size_t size) { 106 | auto err = uc_mem_write(m_unicorn_engine.get(), address, buf, size); 107 | if (err != UC_ERR_OK) { 108 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_mem_write failed."); 109 | } 110 | } 111 | 112 | void amd64_emulator::mem_write(uint64_t address, const std::vector& buf) { 113 | mem_write(address, buf.data(), buf.size()); 114 | } 115 | 116 | bool amd64_emulator::is_address_mapped(uint64_t address) { 117 | resource_wrapper mapped_regions{ resource_traits::unicorn::unicorn_alloc{} }; 118 | uint32_t mapped_regions_num; 119 | 120 | auto err = uc_mem_regions(m_unicorn_engine.get(), mapped_regions.unsafe_addressof(), &mapped_regions_num); 121 | if (err != UC_ERR_OK) { 122 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_mem_regions failed."); 123 | } 124 | 125 | for (size_t i = 0; i < mapped_regions_num; ++i) { 126 | auto& region = mapped_regions.as()[i]; 127 | if (region.begin <= address && address <= region.end) { 128 | return true; 129 | } 130 | } 131 | 132 | return false; 133 | } 134 | 135 | void amd64_emulator::hook_del(uc_hook hook_handle) { 136 | auto iter_of_hook_stub_ctxs = m_unicorn_hook_stub_ctxs.find(hook_handle); 137 | if (iter_of_hook_stub_ctxs == m_unicorn_hook_stub_ctxs.end()) { 138 | throw exceptions::key_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Target hook is not found."); 139 | } 140 | 141 | auto iter_of_hook_callbacks = m_unicorn_hook_callbacks.find(hook_handle); 142 | if (iter_of_hook_callbacks != m_unicorn_hook_callbacks.end()) { 143 | auto err = uc_hook_del(m_unicorn_engine.get(), hook_handle); 144 | if (err) { 145 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"hook_del failed."); 146 | } 147 | 148 | m_unicorn_hook_callbacks.erase(iter_of_hook_callbacks); 149 | m_unicorn_hook_stub_ctxs.erase(iter_of_hook_stub_ctxs); 150 | return; 151 | } 152 | 153 | __builtin_unreachable(); 154 | } 155 | 156 | void amd64_emulator::emu_start(uint64_t begin_address, uint64_t end_address, uint64_t timeout, size_t count) { 157 | auto err = uc_emu_start(m_unicorn_engine.get(), begin_address, end_address, timeout, count); 158 | if (err != UC_ERR_OK) { 159 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"emu_start failed."); 160 | } 161 | } 162 | 163 | void amd64_emulator::emu_stop() { 164 | auto err = uc_emu_stop(m_unicorn_engine.get()); 165 | if (err != UC_ERR_OK) { 166 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_emu_stop failed."); 167 | } 168 | } 169 | 170 | //void amd64_emulator::create_gdt_entry(uint64_t gdt_entry_address, uint32_t base, uint32_t limit, uint8_t access_byte, uint8_t flags) { 171 | // struct { 172 | // uint16_t limit0; 173 | // uint16_t base0; 174 | // uint8_t base1; 175 | // uint8_t access_byte; 176 | // uint8_t limit1 : 4; 177 | // uint8_t flags : 4; 178 | // uint8_t base2; 179 | // } segment_descriptor; 180 | 181 | // static_assert(sizeof(segment_descriptor) == 8); 182 | 183 | // segment_descriptor.limit0 = limit & 0xffff; 184 | // segment_descriptor.base0 = base & 0xffff; 185 | // segment_descriptor.base1 = (base >> 16) & 0xff; 186 | // segment_descriptor.access_byte = access_byte; 187 | // segment_descriptor.limit1 = (limit >> 16) & 0xf; 188 | // segment_descriptor.flags = flags & 0xf; 189 | // segment_descriptor.base2 = (base >> 24) & 0xff; 190 | 191 | // auto err = uc_mem_write(m_unicorn_engine.get(), gdt_entry_address, &segment_descriptor, sizeof(segment_descriptor)); 192 | // if (err != UC_ERR_OK) { 193 | // throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_mem_write failed."); 194 | // } 195 | //} 196 | } 197 | 198 | #undef NKG_CURRENT_SOURCE_LINE 199 | #undef NKG_CURRENT_SOURCE_FILE 200 | -------------------------------------------------------------------------------- /navicat-patcher/amd64_emulator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "resource_wrapper.hpp" 9 | #include "resource_traits/unicorn/unicorn_handle.hpp" 10 | #include "exception.hpp" 11 | 12 | #define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-patcher\\amd64_emulator.hpp" 13 | #define NKG_CURRENT_SOURCE_LINE() __LINE__ 14 | 15 | namespace nkg { 16 | 17 | class amd64_emulator { 18 | public: 19 | class backend_error : public ::nkg::exception { 20 | public: 21 | using error_code_t = uc_err; 22 | 23 | private: 24 | error_code_t m_error_code; 25 | std::string m_error_string; 26 | 27 | public: 28 | backend_error(std::string_view file, int line, error_code_t unicorn_err, std::string_view message) noexcept : 29 | ::nkg::exception(file, line, message), m_error_code(unicorn_err), m_error_string(uc_strerror(unicorn_err)) {} 30 | 31 | [[nodiscard]] 32 | virtual bool error_code_exists() const noexcept override { 33 | return true; 34 | } 35 | 36 | [[nodiscard]] 37 | virtual intptr_t error_code() const noexcept override { 38 | return m_error_code; 39 | } 40 | 41 | [[nodiscard]] 42 | virtual const std::string& error_string() const noexcept override { 43 | return m_error_string; 44 | } 45 | }; 46 | 47 | using hookcode_cb_t = void(uint64_t address, size_t size); 48 | using hookmem_cb_t = void(uc_mem_type type, uint64_t address, size_t size, int64_t value); 49 | using eventmem_cb_t = bool(uc_mem_type type, uint64_t address, size_t size, int64_t value); 50 | 51 | private: 52 | struct hook_stub_context_t { 53 | amd64_emulator* self; 54 | uc_hook unicorn_hook_handle; 55 | }; 56 | 57 | resource_wrapper m_unicorn_engine; 58 | std::unordered_map m_unicorn_user_ctx; 59 | std::unordered_map> m_unicorn_hook_stub_ctxs; 60 | std::unordered_map m_unicorn_hook_callbacks; 61 | 62 | static void _unicorn_hookcode_cb_stub(uc_engine* uc, uint64_t address, uint32_t size, void* user_data); 63 | static void _unicorn_hookmem_cb_stub(uc_engine* uc, uc_mem_type type, uint64_t address, int size, int64_t value, void* user_data); 64 | static bool _unicorn_eventmem_cb_stub(uc_engine* uc, uc_mem_type type, uint64_t address, int size, int64_t value, void* user_data); 65 | 66 | public: 67 | amd64_emulator(); 68 | 69 | void reg_read(int regid, void* buf); 70 | 71 | void reg_write(int regid, const void* buf); 72 | 73 | uint64_t msr_read(uint32_t rid); 74 | 75 | void msr_write(uint32_t rid, uint64_t value); 76 | 77 | void mem_map(uint64_t address, size_t size, uint32_t perms); 78 | 79 | void mem_unmap(uint64_t address, size_t size); 80 | 81 | void mem_read(uint64_t address, void* buf, size_t size); 82 | 83 | std::vector mem_read(uint64_t address, size_t size); 84 | 85 | void mem_write(uint64_t address, const void* buf, size_t size); 86 | 87 | void mem_write(uint64_t address, const std::vector& buf); 88 | 89 | bool is_address_mapped(uint64_t address); 90 | 91 | template 92 | uc_hook hook_add(callable_t&& hook_callback, uint64_t begin_address = 1, uint64_t end_address = 0) { 93 | uc_err err; 94 | 95 | auto hook_stub_ctx = std::make_unique(); 96 | hook_stub_ctx->self = this; 97 | hook_stub_ctx->unicorn_hook_handle = 0; 98 | 99 | if constexpr (hook_type == UC_HOOK_CODE) { 100 | err = uc_hook_add(m_unicorn_engine.get(), &hook_stub_ctx->unicorn_hook_handle, hook_type, reinterpret_cast(_unicorn_hookcode_cb_stub), hook_stub_ctx.get(), begin_address, end_address); 101 | if (err != UC_ERR_OK) { 102 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_hook_add failed."); 103 | } 104 | 105 | m_unicorn_hook_callbacks.emplace(std::make_pair(hook_stub_ctx->unicorn_hook_handle, std::function{ std::forward(hook_callback) })); 106 | } else if constexpr ((hook_type & ~UC_HOOK_MEM_VALID) == 0) { 107 | err = uc_hook_add(m_unicorn_engine.get(), &hook_stub_ctx->unicorn_hook_handle, hook_type, reinterpret_cast(_unicorn_hookmem_cb_stub), hook_stub_ctx.get(), begin_address, end_address); 108 | if (err != UC_ERR_OK) { 109 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_hook_add failed."); 110 | } 111 | 112 | m_unicorn_hook_callbacks.emplace(std::make_pair(hook_stub_ctx->unicorn_hook_handle, std::function{ std::forward(hook_callback) })); 113 | } else if constexpr ((hook_type & ~UC_HOOK_MEM_UNMAPPED) == 0 || (hook_type & ~UC_HOOK_MEM_PROT) == 0) { 114 | err = uc_hook_add(m_unicorn_engine.get(), &hook_stub_ctx->unicorn_hook_handle, hook_type, reinterpret_cast(_unicorn_eventmem_cb_stub), hook_stub_ctx.get(), begin_address, end_address); 115 | if (err != UC_ERR_OK) { 116 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_hook_add failed."); 117 | } 118 | 119 | m_unicorn_hook_callbacks.emplace(std::make_pair(hook_stub_ctx->unicorn_hook_handle, std::function{ std::forward(hook_callback) })); 120 | } else { 121 | static_assert( 122 | hook_type == UC_HOOK_CODE || 123 | (hook_type & ~UC_HOOK_MEM_VALID) == 0 || 124 | (hook_type & ~UC_HOOK_MEM_UNMAPPED) == 0 || (hook_type & ~UC_HOOK_MEM_PROT) == 0, "Unsupported hook type."); 125 | } 126 | 127 | return m_unicorn_hook_stub_ctxs.emplace(std::make_pair(hook_stub_ctx->unicorn_hook_handle, std::move(hook_stub_ctx))).first->first; 128 | } 129 | 130 | void hook_del(uc_hook hook_handle); 131 | 132 | void emu_start(uint64_t begin_address, uint64_t end_address = 0, uint64_t timeout = 0, size_t count = 0); 133 | 134 | void emu_stop(); 135 | 136 | // void create_gdt_entry(uint64_t gdt_entry_address, uint32_t base, uint32_t limit, uint8_t access_byte, uint8_t flags); 137 | 138 | template 139 | void context_set(const std::string& name, val_t&& value) { 140 | m_unicorn_user_ctx[name] = std::forward(value); 141 | } 142 | 143 | template 144 | val_t context_get(const std::string& name) { 145 | return std::any_cast(m_unicorn_user_ctx[name]); 146 | } 147 | }; 148 | 149 | } 150 | 151 | #undef NKG_CURRENT_SOURCE_LINE 152 | #undef NKG_CURRENT_SOURCE_FILE 153 | -------------------------------------------------------------------------------- /navicat-patcher/elf64_interpreter.cpp: -------------------------------------------------------------------------------- 1 | #include "elf64_interpreter.hpp" 2 | #include "exceptions/index_exception.hpp" 3 | #include "exceptions/key_exception.hpp" 4 | #include 5 | #include 6 | #include 7 | 8 | #define NKG_CURRENT_SOURCE_FILE() ".\\navicat-patcher\\elf64_interpreter.cpp" 9 | #define NKG_CURRENT_SOURCE_LINE() __LINE__ 10 | 11 | namespace nkg { 12 | 13 | elf64_interpreter::elf64_interpreter() : 14 | m_elf_size(0), 15 | m_elf_header(nullptr), 16 | m_elf_program_headers(nullptr), 17 | m_elf_section_headers(nullptr), 18 | m_dynamic_rela(nullptr), 19 | m_dynamic_relasz(nullptr), 20 | m_dynamic_rel(nullptr), 21 | m_dynamic_relsz(nullptr), 22 | m_dynamic_pltgot(nullptr), 23 | m_dynamic_jmprel(nullptr), 24 | m_dynamic_pltrel(nullptr), 25 | m_dynamic_pltrelsz(nullptr), 26 | m_dynamic_symtab(nullptr), 27 | m_dynamic_strtab(nullptr) {} 28 | 29 | [[nodiscard]] 30 | elf64_interpreter elf64_interpreter::parse(void* image_ptr, size_t image_size) { 31 | elf64_interpreter new_image; 32 | 33 | // check ELF header 34 | new_image.m_elf_size = image_size; 35 | new_image.m_elf_header = reinterpret_cast(image_ptr); 36 | if (is_address_in_range(new_image.m_elf_header, sizeof(Elf64_Ehdr), image_ptr, image_size) == false) { 37 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: image is corrupted."); 38 | } 39 | 40 | if (memcmp(new_image.m_elf_header->e_ident, ELFMAG, SELFMAG) != 0) { 41 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: header magic check failed."); 42 | } 43 | 44 | if (new_image.m_elf_header->e_ident[EI_CLASS] != ELFCLASS64) { 45 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Unsupported ELF file: not ELF64 image."); 46 | } 47 | 48 | if (new_image.m_elf_header->e_ident[EI_DATA] == ELFDATA2LSB && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) { 49 | ; // pass 50 | } else if (new_image.m_elf_header->e_ident[EI_DATA] == ELFDATA2MSB && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) { 51 | ; // pass 52 | } else { 53 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Unsupported ELF file: unsupported endian."); 54 | } 55 | 56 | if (new_image.m_elf_header->e_ident[EI_VERSION] != EV_CURRENT) { 57 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_ident[EI_VERSION] check failed."); 58 | } 59 | 60 | // new_image.m_elf_header->e_ident[EI_OSABI] 61 | // new_image.m_elf_header->e_ident[EI_ABIVERSION] 62 | 63 | for (int i = EI_PAD; i < sizeof(new_image.m_elf_header->e_ident); ++i) { 64 | if (new_image.m_elf_header->e_ident[i] != 0) { 65 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_ident padding contains non-zero byte(s)."); 66 | } 67 | } 68 | 69 | if (new_image.m_elf_header->e_version != EV_CURRENT) { 70 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_version check failed."); 71 | } 72 | 73 | if (new_image.m_elf_header->e_ehsize != sizeof(Elf64_Ehdr)) { 74 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_ehsize check failed."); 75 | } 76 | 77 | if (new_image.m_elf_header->e_phoff && new_image.m_elf_header->e_phentsize && new_image.m_elf_header->e_phnum) { 78 | if (new_image.m_elf_header->e_phentsize != sizeof(Elf64_Phdr)) { 79 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_phentsize check failed."); 80 | } 81 | 82 | new_image.m_elf_program_headers = address_offset_cast(image_ptr, new_image.m_elf_header->e_phoff); 83 | 84 | if (is_address_in_range(new_image.m_elf_program_headers, new_image.m_elf_header->e_phnum * sizeof(Elf64_Phdr), image_ptr, image_size) == false) { 85 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: image is corrupted."); 86 | } 87 | } else if (new_image.m_elf_header->e_phoff == 0 && new_image.m_elf_header->e_phentsize == 0 && new_image.m_elf_header->e_phnum == 0) { 88 | new_image.m_elf_program_headers = nullptr; 89 | } else { 90 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_ph* check failed."); 91 | } 92 | 93 | if (new_image.m_elf_header->e_shoff && new_image.m_elf_header->e_shentsize && new_image.m_elf_header->e_shnum) { 94 | if (new_image.m_elf_header->e_shentsize != sizeof(Elf64_Shdr)) { 95 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_shentsize check failed."); 96 | } 97 | 98 | new_image.m_elf_section_headers = address_offset_cast(image_ptr, new_image.m_elf_header->e_shoff); 99 | 100 | if (is_address_in_range(new_image.m_elf_section_headers, new_image.m_elf_header->e_shnum * sizeof(Elf64_Shdr), image_ptr, image_size) == false) { 101 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: image is corrupted."); 102 | } 103 | } else if (new_image.m_elf_header->e_shoff == 0 && new_image.m_elf_header->e_shentsize == 0 && new_image.m_elf_header->e_shnum == 0) { 104 | new_image.m_elf_section_headers = nullptr; 105 | } else { 106 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_sh* check failed."); 107 | } 108 | 109 | if (new_image.m_elf_header->e_shstrndx != SHN_UNDEF) { 110 | if (new_image.m_elf_header->e_shstrndx >= new_image.m_elf_header->e_shnum) { 111 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_shstrndx is out of range."); 112 | } 113 | } 114 | 115 | // check program header table and section header table are not overlapped 116 | if (new_image.m_elf_program_headers && new_image.m_elf_section_headers) { 117 | auto a1 = new_image.m_elf_program_headers; 118 | auto a2 = new_image.m_elf_program_headers + new_image.m_elf_header->e_phnum; 119 | auto b1 = new_image.m_elf_section_headers; 120 | auto b2 = new_image.m_elf_section_headers + new_image.m_elf_header->e_shnum; 121 | bool not_overlapped = address_delta(a1, b1) < 0 && address_delta(a2, b1) <= 0 || address_delta(b1, a1) < 0 && address_delta(b2, a1) <= 0; 122 | if (!not_overlapped) { 123 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: program header table and section header table overlapped."); 124 | } 125 | } 126 | 127 | // parse program header 128 | for (size_t i = 0; i < new_image.m_elf_header->e_phnum; ++i) { 129 | auto& prog_hdr = new_image.m_elf_program_headers[i]; 130 | if (!is_address_in_range(address_offset(image_ptr, static_cast(prog_hdr.p_offset)), prog_hdr.p_filesz, image_ptr, image_size)) { 131 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: image is corrupted."); 132 | } 133 | 134 | auto prog_hdr_align = prog_hdr.p_align; 135 | if (prog_hdr_align) { 136 | // align must be a power of 2 137 | if ((prog_hdr_align & (prog_hdr_align - 1)) != 0) { 138 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Phdr[{}]: p_align is not a power of 2.", i)); 139 | } 140 | 141 | if (prog_hdr.p_offset % prog_hdr_align != prog_hdr.p_vaddr % prog_hdr_align) { 142 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Phdr[{}]: p_offset !== p_vaddr (mod prog_hdr_align).", i)); 143 | } 144 | } 145 | 146 | if (prog_hdr.p_type == PT_LOAD) { 147 | new_image.m_segment_va_lookup_table.emplace(std::make_pair(prog_hdr.p_vaddr, &prog_hdr)); 148 | new_image.m_segment_fo_lookup_table.emplace(std::make_pair(prog_hdr.p_offset, &prog_hdr)); 149 | } 150 | } 151 | 152 | // parse section header 153 | if (new_image.m_elf_header->e_shstrndx != SHN_UNDEF) { 154 | auto sect_hdr_strtab = &new_image.m_elf_section_headers[new_image.m_elf_header->e_shstrndx]; 155 | auto sect_view_strtab = address_offset_cast(image_ptr, sect_hdr_strtab->sh_offset); 156 | 157 | if (sect_hdr_strtab->sh_type != SHT_STRTAB) { 158 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: sect_hdr_strtab->sh_type != SHT_STRTAB."); 159 | } 160 | 161 | if (!is_address_in_range(sect_view_strtab, sect_hdr_strtab->sh_size, image_ptr, image_size)) { 162 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: image is corrupted."); 163 | } 164 | 165 | for (size_t i = 0; i < new_image.m_elf_header->e_shnum; ++i) { 166 | new_image.m_section_name_lookup_table 167 | .emplace(std::make_pair(std::string_view(address_offset(sect_view_strtab, new_image.m_elf_section_headers[i].sh_name)), &new_image.m_elf_section_headers[i])); 168 | } 169 | } 170 | 171 | for (int i = 0; i < new_image.m_elf_header->e_shnum; ++i) { 172 | auto& sect_hdr = new_image.m_elf_section_headers[i]; 173 | switch (sect_hdr.sh_type) { 174 | case SHT_SYMTAB: 175 | if (sect_hdr.sh_entsize != sizeof(Elf64_Sym)) { 176 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_entsize != sizeof(Elf64_Sym).", i)); 177 | } 178 | 179 | // check sh_link 180 | if (sect_hdr.sh_link == SHN_UNDEF) { 181 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link == SHN_UNDEF.", i)); 182 | } 183 | 184 | if (sect_hdr.sh_link >= new_image.m_elf_header->e_shnum) { 185 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link is out of range.", i)); 186 | } 187 | 188 | if (new_image.m_elf_section_headers[sect_hdr.sh_link].sh_type != SHT_STRTAB) { 189 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: bad value of sh_link.", i)); 190 | } 191 | 192 | // todo: check sh_info 193 | break; 194 | case SHT_RELA: 195 | if (sect_hdr.sh_entsize != sizeof(Elf64_Rela)) { 196 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_entsize != sizeof(Elf64_Rela).", i)); 197 | } 198 | 199 | // check sh_link 200 | if (sect_hdr.sh_link == SHN_UNDEF) { 201 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link == SHN_UNDEF.", i)); 202 | } 203 | 204 | if (sect_hdr.sh_link >= new_image.m_elf_header->e_shnum) { 205 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link is out of range.", i)); 206 | } 207 | 208 | if (new_image.m_elf_section_headers[sect_hdr.sh_link].sh_type != SHT_SYMTAB && new_image.m_elf_section_headers[sect_hdr.sh_link].sh_type != SHT_DYNSYM) { 209 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[%u]: bad value of sh_link.", i)); 210 | } 211 | 212 | // check sh_info 213 | if (sect_hdr.sh_flags & SHF_INFO_LINK) { 214 | if (sect_hdr.sh_info == SHN_UNDEF) { 215 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info == SHN_UNDEF.", i)); 216 | } 217 | 218 | if (sect_hdr.sh_info >= new_image.m_elf_header->e_shnum) { 219 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info is out of range.", i)); 220 | } 221 | } else { 222 | if (sect_hdr.sh_info != 0) { 223 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info != 0.", i)); 224 | } 225 | } 226 | break; 227 | case SHT_HASH: 228 | if (sect_hdr.sh_link == SHN_UNDEF) { 229 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link == SHN_UNDEF.", i)); 230 | } 231 | 232 | if (sect_hdr.sh_link >= new_image.m_elf_header->e_shnum) { 233 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link is out of range.", i)); 234 | } 235 | 236 | if (new_image.m_elf_section_headers[sect_hdr.sh_link].sh_type != SHT_SYMTAB && new_image.m_elf_section_headers[sect_hdr.sh_link].sh_type != SHT_DYNSYM) { 237 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: bad value of sh_link.", i)); 238 | } 239 | 240 | if (sect_hdr.sh_info != 0) { 241 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info != 0.", i)); 242 | } 243 | break; 244 | case SHT_DYNAMIC: 245 | if (sect_hdr.sh_entsize != sizeof(Elf64_Dyn)) { 246 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_entsize != sizeof(Elf64_Dyn).", i)); 247 | } 248 | 249 | // check sh_link 250 | if (sect_hdr.sh_link == SHN_UNDEF) { 251 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link == SHN_UNDEF.", i)); 252 | } 253 | 254 | if (sect_hdr.sh_link >= new_image.m_elf_header->e_shnum) { 255 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link is out of range.", i)); 256 | } 257 | 258 | if (new_image.m_elf_section_headers[sect_hdr.sh_link].sh_type != SHT_STRTAB) { 259 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: bad value of sh_link.", i)); 260 | } 261 | 262 | // check sh_info 263 | if (sect_hdr.sh_info != 0) { 264 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info != 0.", i)); 265 | } 266 | break; 267 | case SHT_REL: 268 | if (sect_hdr.sh_entsize != sizeof(Elf64_Rel)) { 269 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_entsize != sizeof(Elf64_Rel).", i)); 270 | } 271 | 272 | // check sh_link 273 | if (sect_hdr.sh_link == SHN_UNDEF) { 274 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link == SHN_UNDEF.", i)); 275 | } 276 | 277 | if (sect_hdr.sh_link >= new_image.m_elf_header->e_shnum) { 278 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link is out of range.", i)); 279 | } 280 | 281 | if (new_image.m_elf_section_headers[sect_hdr.sh_link].sh_type != SHT_SYMTAB && new_image.m_elf_section_headers[sect_hdr.sh_link].sh_type != SHT_DYNSYM) { 282 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[%u]: bad value of sh_link.", i)); 283 | } 284 | 285 | // check sh_info 286 | if (sect_hdr.sh_flags & SHF_INFO_LINK) { 287 | if (sect_hdr.sh_info == SHN_UNDEF) { 288 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info == SHN_UNDEF.", i)); 289 | } 290 | 291 | if (sect_hdr.sh_info >= new_image.m_elf_header->e_shnum) { 292 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info is out of range.", i)); 293 | } 294 | } else { 295 | if (sect_hdr.sh_info != 0) { 296 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info != 0.", i)); 297 | } 298 | } 299 | break; 300 | case SHT_DYNSYM: 301 | if (sect_hdr.sh_entsize != sizeof(Elf64_Sym)) { 302 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_entsize != sizeof(Elf64_Dyn).", i)); 303 | } 304 | 305 | // check sh_link 306 | if (sect_hdr.sh_link == SHN_UNDEF) { 307 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link == SHN_UNDEF.", i)); 308 | } 309 | 310 | if (sect_hdr.sh_link >= new_image.m_elf_header->e_shnum) { 311 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link is out of range.", i)); 312 | } 313 | 314 | if (new_image.m_elf_section_headers[sect_hdr.sh_link].sh_type != SHT_STRTAB) { 315 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: bad value of sh_link.", i)); 316 | } 317 | 318 | // todo: check sh_info 319 | break; 320 | default: 321 | break; 322 | } 323 | 324 | if (sect_hdr.sh_type != SHT_NOBITS) { 325 | if (is_address_in_range(address_offset(image_ptr, sect_hdr.sh_offset), sect_hdr.sh_size, image_ptr, image_size) == false) { 326 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: image is corrupted.", i)); 327 | } 328 | new_image.m_section_fo_lookup_table.emplace(std::make_pair(sect_hdr.sh_offset, new_image.m_elf_section_headers + i)); 329 | } 330 | 331 | if (sect_hdr.sh_addr) { 332 | if (sect_hdr.sh_addralign && sect_hdr.sh_addr % sect_hdr.sh_addralign != 0) { 333 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_addr is not aligned to sh_addralign.", i)); 334 | } 335 | new_image.m_section_va_lookup_table.emplace(std::make_pair(sect_hdr.sh_addr, &new_image.m_elf_section_headers[i])); 336 | } 337 | } 338 | 339 | // parse program header, second parse 340 | for (size_t i = 0; i < new_image.m_elf_header->e_phnum; ++i) { 341 | auto& prog_hdr = new_image.m_elf_program_headers[i]; 342 | if (prog_hdr.p_type == PT_DYNAMIC) { 343 | auto seg_dynamic_base = address_offset_cast(image_ptr, prog_hdr.p_offset); 344 | auto seg_dyncmic_size = prog_hdr.p_filesz; 345 | 346 | // first parse 347 | for (size_t j = 0; j * sizeof(Elf64_Dyn) < seg_dyncmic_size && seg_dynamic_base[j].d_tag != DT_NULL; ++j) { 348 | auto& dyn_entry = seg_dynamic_base[j]; 349 | switch (dyn_entry.d_tag) { 350 | case DT_RELA: 351 | new_image.m_dynamic_rela = &seg_dynamic_base[j]; 352 | break; 353 | case DT_RELAENT: 354 | if (seg_dynamic_base[j].d_un.d_val != sizeof(Elf64_Rela)) { 355 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: the value of DT_RELAENT.dval != sizeof(Elf64_Rela)."); 356 | } 357 | break; 358 | case DT_RELASZ: 359 | new_image.m_dynamic_relasz = &seg_dynamic_base[j]; 360 | break; 361 | case DT_REL: 362 | new_image.m_dynamic_rel = &seg_dynamic_base[j]; 363 | break; 364 | case DT_RELENT: 365 | if (seg_dynamic_base[j].d_un.d_val != sizeof(Elf64_Rel)) { 366 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: the value of DT_RELENT.dval != sizeof(Elf64_Rel)."); 367 | } 368 | break; 369 | case DT_RELSZ: 370 | new_image.m_dynamic_relsz = &seg_dynamic_base[j]; 371 | break; 372 | case DT_PLTGOT: 373 | new_image.m_dynamic_pltgot = &seg_dynamic_base[j]; 374 | break; 375 | case DT_JMPREL: 376 | new_image.m_dynamic_jmprel = &seg_dynamic_base[j]; 377 | break; 378 | case DT_PLTREL: 379 | if (seg_dynamic_base[j].d_un.d_val == DT_REL || seg_dynamic_base[j].d_un.d_val == DT_RELA) { 380 | new_image.m_dynamic_pltrel = &seg_dynamic_base[j]; 381 | } else { 382 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: the value of DT_PLTREL.dval is neither DT_REL nor DT_RELA."); 383 | } 384 | break; 385 | case DT_PLTRELSZ: 386 | new_image.m_dynamic_pltrelsz = &seg_dynamic_base[j]; 387 | break; 388 | case DT_STRTAB: 389 | new_image.m_dynamic_strtab = &seg_dynamic_base[j]; 390 | break; 391 | case DT_SYMTAB: 392 | new_image.m_dynamic_symtab = &seg_dynamic_base[j]; 393 | break; 394 | default: 395 | break; 396 | } 397 | } 398 | } 399 | } 400 | 401 | if (new_image.m_dynamic_rela && new_image.m_dynamic_relasz) { 402 | auto rela_base = new_image.convert_va_to_ptr(new_image.m_dynamic_rela->d_un.d_ptr); 403 | auto rela_size = new_image.m_dynamic_relasz->d_un.d_val; 404 | for (size_t i = 0; i * sizeof(Elf64_Rela) < rela_size; ++i) { 405 | auto reloc_va = rela_base[i].r_offset; 406 | auto reloc_type = ELF64_R_TYPE(rela_base[i].r_info); 407 | switch(reloc_type) { 408 | case R_X86_64_64: 409 | case R_X86_64_GLOB_DAT: 410 | case R_X86_64_RELATIVE: 411 | new_image.m_relocation_distribute.emplace(std::make_pair(reloc_va, 8)); 412 | break; 413 | default: 414 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Unsupported ELF file: unhandled relocation type({}).", reloc_type)); 415 | } 416 | } 417 | } 418 | 419 | if (new_image.m_dynamic_rel && new_image.m_dynamic_relsz) { 420 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Unsupported ELF file: DT_REL is not parsed."); 421 | } 422 | 423 | if (new_image.m_dynamic_jmprel && new_image.m_dynamic_pltrel && new_image.m_dynamic_pltrelsz) { 424 | if (new_image.m_dynamic_pltrel->d_un.d_val == DT_RELA) { 425 | auto jmprel_base = new_image.convert_va_to_ptr(new_image.m_dynamic_jmprel->d_un.d_ptr); 426 | auto jmprel_size = new_image.m_dynamic_pltrelsz->d_un.d_val; 427 | for (size_t i = 0; i * sizeof(Elf64_Rela) < jmprel_size; ++i) { 428 | auto reloc_va = jmprel_base[i].r_offset; 429 | auto reloc_type = ELF64_R_TYPE(jmprel_base[i].r_info); 430 | if (reloc_type == R_X86_64_JUMP_SLOT) { 431 | new_image.m_relocation_distribute.emplace(std::make_pair(reloc_va, 8)); 432 | } else { 433 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: bad relocation type({}) in JMPREL relocation table.", reloc_type)); 434 | } 435 | } 436 | } else { 437 | auto jmprel_base = new_image.convert_va_to_ptr(new_image.m_dynamic_jmprel->d_un.d_ptr); 438 | auto jmprel_size = new_image.m_dynamic_pltrelsz->d_un.d_val; 439 | for (size_t i = 0; i * sizeof(Elf64_Rela) < jmprel_size; ++i) { 440 | auto reloc_va = jmprel_base[i].r_offset; 441 | auto reloc_type = ELF64_R_TYPE(jmprel_base[i].r_info); 442 | if (reloc_type == R_X86_64_JUMP_SLOT) { 443 | new_image.m_relocation_distribute.emplace(std::make_pair(reloc_va, 8)); 444 | } else { 445 | throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: bad relocation type({}) in JMPREL relocation table.", reloc_type)); 446 | } 447 | } 448 | } 449 | } 450 | 451 | return new_image; 452 | } 453 | 454 | [[nodiscard]] 455 | size_t elf64_interpreter::elf_size() const noexcept { 456 | return m_elf_size; 457 | } 458 | 459 | [[nodiscard]] 460 | Elf64_Ehdr* elf64_interpreter::elf_header() const noexcept { 461 | return m_elf_header; 462 | } 463 | 464 | [[nodiscard]] 465 | Elf64_Phdr* elf64_interpreter::elf_program_header(size_t n) const { 466 | if (n < m_elf_header->e_phnum) { 467 | return m_elf_program_headers + n; 468 | } else { 469 | throw exceptions::index_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Out of range."); 470 | } 471 | } 472 | 473 | [[nodiscard]] 474 | Elf64_Phdr* elf64_interpreter::elf_program_header_from_fo(fo_t file_offset) const { 475 | auto it = m_segment_fo_lookup_table.upper_bound(file_offset); 476 | if (it != m_segment_fo_lookup_table.begin()) { 477 | --it; 478 | if (it->second->p_offset <= file_offset && file_offset < it->second->p_offset + it->second->p_filesz) { 479 | return it->second; 480 | } 481 | } 482 | throw bad_fo_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("File offset({:#x}) doesn't point to any segment.")); 483 | } 484 | 485 | [[nodiscard]] 486 | Elf64_Phdr* elf64_interpreter::elf_program_header_from_rva(rva_t rva) const { 487 | return elf_program_header_from_va(convert_rva_to_va(rva)); 488 | } 489 | 490 | [[nodiscard]] 491 | Elf64_Phdr* elf64_interpreter::elf_program_header_from_va(va_t va) const { 492 | auto it = m_segment_va_lookup_table.upper_bound(va); 493 | if (it != m_segment_va_lookup_table.begin()) { 494 | --it; 495 | if (it->second->p_vaddr <= va && va < it->second->p_vaddr + it->second->p_memsz) { 496 | return it->second; 497 | } 498 | } 499 | throw bad_va_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Invalid virtual address({:#016x}).", va)); 500 | } 501 | 502 | [[nodiscard]] 503 | size_t elf64_interpreter::elf_program_headers_num() const noexcept { 504 | return m_elf_header->e_shnum; 505 | } 506 | 507 | [[nodiscard]] 508 | Elf64_Shdr* elf64_interpreter::elf_section_header(size_t n) const { 509 | if (n < m_elf_header->e_shnum) { 510 | return m_elf_section_headers + n; 511 | } else { 512 | throw exceptions::index_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Out of range."); 513 | } 514 | } 515 | 516 | [[nodiscard]] 517 | Elf64_Shdr* elf64_interpreter::elf_section_header(std::string_view section_name) const { 518 | auto it = m_section_name_lookup_table.find(section_name); 519 | if (it != m_section_name_lookup_table.end()) { 520 | return it->second; 521 | } else { 522 | throw exceptions::key_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Section `{}` is not found.", section_name.data())); 523 | } 524 | } 525 | 526 | [[nodiscard]] 527 | size_t elf64_interpreter::elf_section_headers_num() const noexcept { 528 | return m_elf_header->e_shnum; 529 | } 530 | 531 | [[nodiscard]] 532 | elf64_interpreter::fo_t elf64_interpreter::convert_rva_to_fo(rva_t rva) const { 533 | return convert_va_to_fo(convert_rva_to_va(rva)); 534 | } 535 | 536 | [[nodiscard]] 537 | elf64_interpreter::fo_t elf64_interpreter::convert_va_to_fo(va_t va) const { 538 | auto it = m_segment_va_lookup_table.upper_bound(va); 539 | if (it != m_segment_va_lookup_table.begin()) { 540 | --it; 541 | if (it->second->p_vaddr <= va && va < it->second->p_vaddr + it->second->p_memsz) { 542 | if (va - it->second->p_vaddr < it->second->p_filesz) { 543 | return it->second->p_offset + (va - it->second->p_vaddr); 544 | } else { 545 | throw bad_va_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Virtual address({:#016x}) doesn't have corresponding file offset.", va)); 546 | } 547 | } 548 | } 549 | throw bad_va_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Invalid virtual address({:#016x})", va)); 550 | } 551 | 552 | [[nodiscard]] 553 | elf64_interpreter::rva_t elf64_interpreter::convert_fo_to_rva(fo_t file_offset) const { 554 | return convert_va_to_rva(convert_fo_to_va(file_offset)); 555 | } 556 | 557 | elf64_interpreter::rva_t elf64_interpreter::convert_va_to_rva(va_t va) const { 558 | return va - m_segment_va_lookup_table.begin()->first; 559 | } 560 | 561 | [[nodiscard]] 562 | elf64_interpreter::va_t elf64_interpreter::convert_fo_to_va(fo_t file_offset) const { 563 | auto it = m_segment_fo_lookup_table.upper_bound(file_offset); 564 | if (it != m_segment_fo_lookup_table.begin()) { 565 | --it; 566 | if (it->second->p_offset <= file_offset && file_offset < it->second->p_offset + it->second->p_filesz) { 567 | return it->second->p_vaddr + (file_offset - it->second->p_offset); 568 | } 569 | } 570 | throw bad_fo_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("File offset({:#x}) doesn't have corresponding virtual address.", file_offset)); 571 | } 572 | 573 | [[nodiscard]] 574 | elf64_interpreter::va_t elf64_interpreter::convert_rva_to_va(rva_t rva) const { 575 | return m_segment_va_lookup_table.begin()->first + rva; 576 | } 577 | 578 | [[nodiscard]] 579 | std::optional elf64_interpreter::elf_dynamic_rela() const { 580 | return m_dynamic_rela ? std::make_optional(m_dynamic_rela->d_un.d_ptr) : std::nullopt; 581 | } 582 | 583 | [[nodiscard]] 584 | std::optional elf64_interpreter::elf_dynamic_relasz() const { 585 | return m_dynamic_relasz ? std::make_optional(m_dynamic_relasz->d_un.d_ptr) : std::nullopt; 586 | } 587 | 588 | [[nodiscard]] 589 | std::optional elf64_interpreter::elf_dynamic_rel() const { 590 | return m_dynamic_rel ? std::make_optional(m_dynamic_rel->d_un.d_ptr) : std::nullopt; 591 | } 592 | 593 | [[nodiscard]] 594 | std::optional elf64_interpreter::elf_dynamic_relsz() const { 595 | return m_dynamic_relsz ? std::make_optional(m_dynamic_relsz->d_un.d_ptr) : std::nullopt; 596 | } 597 | 598 | [[nodiscard]] 599 | std::optional elf64_interpreter::elf_dynamic_pltgot() const { 600 | return m_dynamic_pltgot ? std::make_optional(m_dynamic_pltgot->d_un.d_ptr) : std::nullopt; 601 | } 602 | 603 | [[nodiscard]] 604 | std::optional elf64_interpreter::elf_dynamic_jmprel() const { 605 | return m_dynamic_jmprel ? std::make_optional(m_dynamic_jmprel->d_un.d_ptr) : std::nullopt; 606 | } 607 | 608 | [[nodiscard]] 609 | std::optional elf64_interpreter::elf_dynamic_pltrel() const { 610 | return m_dynamic_pltrel ? std::make_optional(m_dynamic_pltrel->d_un.d_val) : std::nullopt; 611 | } 612 | 613 | [[nodiscard]] 614 | std::optional elf64_interpreter::elf_dynamic_pltrelsz() const { 615 | return m_dynamic_pltrelsz ? std::make_optional(m_dynamic_pltrelsz->d_un.d_val) : std::nullopt; 616 | } 617 | 618 | [[nodiscard]] 619 | std::optional elf64_interpreter::elf_dynamic_symtab() const { 620 | return m_dynamic_symtab ? std::make_optional(m_dynamic_symtab->d_un.d_ptr) : std::nullopt; 621 | } 622 | 623 | [[nodiscard]] 624 | std::optional elf64_interpreter::elf_dynamic_strtab() const { 625 | return m_dynamic_strtab ? std::make_optional(m_dynamic_strtab->d_un.d_ptr) : std::nullopt; 626 | } 627 | 628 | [[nodiscard]] 629 | const std::map& elf64_interpreter::relocation_distribute() const { 630 | return m_relocation_distribute; 631 | } 632 | } 633 | 634 | #undef NKG_CURRENT_SOURCE_LINE 635 | #undef NKG_CURRENT_SOURCE_FILE 636 | -------------------------------------------------------------------------------- /navicat-patcher/elf64_interpreter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "exception.hpp" 11 | #include "memory_utility.hpp" 12 | 13 | namespace nkg { 14 | 15 | class elf64_interpreter { 16 | public: 17 | using fo_t = uintptr_t; 18 | using rva_t = uintptr_t; 19 | using va_t = uintptr_t; 20 | 21 | class parse_error; 22 | class bad_fo_exception; 23 | class bad_va_exception; 24 | 25 | private: 26 | size_t m_elf_size; 27 | Elf64_Ehdr* m_elf_header; 28 | Elf64_Phdr* m_elf_program_headers; 29 | Elf64_Shdr* m_elf_section_headers; 30 | 31 | std::map m_segment_va_lookup_table; 32 | std::map m_segment_fo_lookup_table; 33 | 34 | std::map m_section_va_lookup_table; 35 | std::map m_section_fo_lookup_table; 36 | std::map m_section_name_lookup_table; 37 | 38 | Elf64_Dyn* m_dynamic_rela; 39 | Elf64_Dyn* m_dynamic_relasz; 40 | Elf64_Dyn* m_dynamic_rel; 41 | Elf64_Dyn* m_dynamic_relsz; 42 | Elf64_Dyn* m_dynamic_pltgot; 43 | Elf64_Dyn* m_dynamic_jmprel; 44 | Elf64_Dyn* m_dynamic_pltrel; 45 | Elf64_Dyn* m_dynamic_pltrelsz; 46 | Elf64_Dyn* m_dynamic_symtab; 47 | Elf64_Dyn* m_dynamic_strtab; 48 | 49 | std::map m_relocation_distribute; 50 | 51 | elf64_interpreter(); 52 | 53 | public: 54 | [[nodiscard]] 55 | static elf64_interpreter parse(void* image_ptr, size_t image_size); 56 | 57 | [[nodiscard]] 58 | size_t elf_size() const noexcept; 59 | 60 | template 61 | [[nodiscard]] 62 | return_t elf_base() const noexcept { 63 | static_assert(std::is_pointer_v); 64 | return reinterpret_cast(m_elf_header); 65 | } 66 | 67 | template 68 | [[nodiscard]] 69 | return_t elf_offset(fo_t offset) const noexcept { 70 | static_assert(std::is_pointer_v); 71 | return address_offset_cast(m_elf_header, offset); 72 | } 73 | 74 | [[nodiscard]] 75 | Elf64_Ehdr* elf_header() const noexcept; 76 | 77 | [[nodiscard]] 78 | Elf64_Phdr* elf_program_header(size_t n) const; 79 | 80 | [[nodiscard]] 81 | Elf64_Phdr* elf_program_header_from_fo(fo_t file_offset) const; 82 | 83 | [[nodiscard]] 84 | Elf64_Phdr* elf_program_header_from_rva(rva_t rva) const; 85 | 86 | [[nodiscard]] 87 | Elf64_Phdr* elf_program_header_from_va(va_t va) const; 88 | 89 | [[nodiscard]] 90 | size_t elf_program_headers_num() const noexcept; 91 | 92 | [[nodiscard]] 93 | Elf64_Shdr* elf_section_header(size_t n) const; 94 | 95 | [[nodiscard]] 96 | Elf64_Shdr* elf_section_header(std::string_view section_name) const; 97 | 98 | [[nodiscard]] 99 | size_t elf_section_headers_num() const noexcept; 100 | 101 | template 102 | [[nodiscard]] 103 | return_t elf_section_view(size_t n, fo_t offset = 0) const { 104 | return elf_offset(elf_section_header(n)->sh_offset + offset); 105 | } 106 | 107 | template 108 | [[nodiscard]] 109 | return_t elf_section_view(std::string_view section_name, fo_t offset = 0) const { 110 | return elf_offset(elf_section_header(section_name)->sh_offset + offset); 111 | } 112 | 113 | template 114 | [[nodiscard]] 115 | fo_t convert_ptr_to_fo(ptr_t ptr) const { 116 | return address_delta(ptr, m_elf_header); 117 | } 118 | 119 | [[nodiscard]] 120 | fo_t convert_rva_to_fo(rva_t rva) const; 121 | 122 | [[nodiscard]] 123 | fo_t convert_va_to_fo(va_t va) const; 124 | 125 | [[nodiscard]] 126 | rva_t convert_fo_to_rva(fo_t file_offset) const; 127 | 128 | template 129 | [[nodiscard]] 130 | rva_t convert_ptr_to_rva(ptr_t ptr) const { 131 | return convert_fo_to_rva(convert_ptr_to_fo(ptr)); 132 | } 133 | 134 | rva_t convert_va_to_rva(va_t va) const; 135 | 136 | [[nodiscard]] 137 | va_t convert_fo_to_va(fo_t file_offset) const; 138 | 139 | [[nodiscard]] 140 | va_t convert_rva_to_va(rva_t rva) const; 141 | 142 | template 143 | va_t convert_ptr_to_va(ptr_t ptr) const { 144 | return convert_fo_to_va(convert_ptr_to_fo(ptr)); 145 | } 146 | 147 | template 148 | [[nodiscard]] 149 | ptr_t convert_fo_to_ptr(fo_t offset) const { 150 | return elf_offset(offset); 151 | } 152 | 153 | template 154 | [[nodiscard]] 155 | ptr_t convert_rva_to_ptr(rva_t rva) const { 156 | return convert_fo_to_ptr(convert_rva_to_fo(rva)); 157 | } 158 | 159 | template 160 | [[nodiscard]] 161 | ptr_t convert_va_to_ptr(va_t va) const { 162 | return convert_fo_to_ptr(convert_va_to_fo(va)); 163 | } 164 | 165 | template 166 | [[nodiscard]] 167 | return_t search_section(size_t n, callable_t&& pred_func) const noexcept { 168 | auto sect_hdr = elf_section_header(n); 169 | auto sect_view = elf_offset(sect_hdr->sh_offset); 170 | for (size_t i = 0; i < sect_hdr->sh_size; ++i) { 171 | if (pred_func(sect_view, i, sect_hdr->sh_size)) { 172 | return reinterpret_cast(sect_view + i); 173 | } 174 | } 175 | return nullptr; 176 | } 177 | 178 | template 179 | [[nodiscard]] 180 | return_t search_section(std::string_view section_name, callable_t&& pred_func) const noexcept { 181 | auto sect_hdr = elf_section_header(section_name); 182 | auto sect_view = elf_offset(sect_hdr->sh_offset); 183 | for (size_t i = 0; i < sect_hdr->sh_size; ++i) { 184 | if (pred_func(sect_view, i, sect_hdr->sh_size)) { 185 | return reinterpret_cast(sect_view + i); 186 | } 187 | } 188 | return nullptr; 189 | } 190 | 191 | [[nodiscard]] 192 | std::optional elf_dynamic_rela() const; 193 | 194 | [[nodiscard]] 195 | std::optional elf_dynamic_relasz() const; 196 | 197 | [[nodiscard]] 198 | std::optional elf_dynamic_rel() const; 199 | 200 | [[nodiscard]] 201 | std::optional elf_dynamic_relsz() const; 202 | 203 | [[nodiscard]] 204 | std::optional elf_dynamic_pltgot() const; 205 | 206 | [[nodiscard]] 207 | std::optional elf_dynamic_jmprel() const; 208 | 209 | [[nodiscard]] 210 | std::optional elf_dynamic_pltrel() const; 211 | 212 | [[nodiscard]] 213 | std::optional elf_dynamic_pltrelsz() const; 214 | 215 | [[nodiscard]] 216 | std::optional elf_dynamic_symtab() const; 217 | 218 | [[nodiscard]] 219 | std::optional elf_dynamic_strtab() const; 220 | 221 | [[nodiscard]] 222 | const std::map& relocation_distribute() const; 223 | }; 224 | 225 | class elf64_interpreter::parse_error : public ::nkg::exception { 226 | using ::nkg::exception::exception; 227 | }; 228 | 229 | class elf64_interpreter::bad_fo_exception : public ::nkg::exception { 230 | using ::nkg::exception::exception; 231 | }; 232 | 233 | class elf64_interpreter::bad_va_exception : public ::nkg::exception { 234 | using ::nkg::exception::exception; 235 | }; 236 | 237 | } 238 | -------------------------------------------------------------------------------- /navicat-patcher/keystone_assembler.cpp: -------------------------------------------------------------------------------- 1 | #include "keystone_assembler.hpp" 2 | #include "resource_traits/keystone/keystone_alloc.hpp" 3 | 4 | #define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-patcher\\keystone_assembler.cpp" 5 | #define NKG_CURRENT_SOURCE_LINE() __LINE__ 6 | 7 | namespace nkg { 8 | 9 | keystone_assembler::keystone_assembler(ks_arch architecture, ks_mode mode) { 10 | auto err = ks_open(architecture, mode, m_keystone_engine.unsafe_addressof()); 11 | if (err != KS_ERR_OK) { 12 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"ks_open failed."); 13 | } 14 | } 15 | 16 | void keystone_assembler::option(ks_opt_type option_type, size_t option_value) { 17 | auto err = ks_option(m_keystone_engine.get(), option_type, option_value); 18 | if (err != KS_ERR_OK) { 19 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"ks_option failed."); 20 | } 21 | } 22 | 23 | [[nodiscard]] 24 | std::vector keystone_assembler::assemble(std::string_view asm_string, uint64_t asm_address) const { 25 | resource_wrapper machine_code{ resource_traits::keystone::keystone_alloc{} }; 26 | 27 | size_t machine_code_size = 0; 28 | size_t stat_count = 0; 29 | if (ks_asm(m_keystone_engine.get(), asm_string.data(), asm_address, machine_code.unsafe_addressof(), &machine_code_size, &stat_count) < 0) { 30 | auto err = ks_errno(m_keystone_engine.get()); 31 | throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"ks_asm failed."); 32 | } 33 | 34 | return std::vector(machine_code.get(), machine_code.get() + machine_code_size); 35 | } 36 | 37 | } 38 | 39 | #undef NKG_CURRENT_SOURCE_LINE 40 | #undef NKG_CURRENT_SOURCE_FILE 41 | -------------------------------------------------------------------------------- /navicat-patcher/keystone_assembler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | #include "resource_wrapper.hpp" 7 | #include "resource_traits/keystone/keystone_handle.hpp" 8 | 9 | #include "exception.hpp" 10 | 11 | namespace nkg { 12 | 13 | class keystone_assembler { 14 | public: 15 | class backend_error; 16 | 17 | private: 18 | resource_wrapper m_keystone_engine; 19 | 20 | public: 21 | keystone_assembler(ks_arch architecture, ks_mode mode); 22 | 23 | void option(ks_opt_type option_type, size_t option_value); 24 | 25 | [[nodiscard]] 26 | std::vector assemble(std::string_view asm_string, uint64_t asm_address = 0) const; 27 | }; 28 | 29 | class keystone_assembler::backend_error : public ::nkg::exception { 30 | public: 31 | using error_code_t = ks_err; 32 | 33 | private: 34 | error_code_t m_error_code; 35 | std::string m_error_string; 36 | 37 | public: 38 | backend_error(std::string_view file, int line, error_code_t keystone_err, std::string_view message) noexcept : 39 | ::nkg::exception(file, line, message), m_error_code(keystone_err), m_error_string(ks_strerror(keystone_err)) {} 40 | 41 | [[nodiscard]] 42 | virtual bool error_code_exists() const noexcept override { 43 | return true; 44 | } 45 | 46 | [[nodiscard]] 47 | virtual intptr_t error_code() const noexcept override { 48 | return m_error_code; 49 | } 50 | 51 | [[nodiscard]] 52 | virtual const std::string& error_string() const noexcept override { 53 | return m_error_string; 54 | } 55 | }; 56 | 57 | } 58 | -------------------------------------------------------------------------------- /navicat-patcher/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "resource_wrapper.hpp" 15 | #include "resource_traits/cxx_object_traits.hpp" 16 | #include "resource_traits/unix_os/file_descriptor.hpp" 17 | #include "resource_traits/unix_os/map_view.hpp" 18 | 19 | #include "rsa_cipher.hpp" 20 | #include "elf64_interpreter.hpp" 21 | #include "patch_solution.hpp" 22 | #include "patch_solution_since_16.0.7.0.hpp" 23 | 24 | #include "exception.hpp" 25 | #include "exceptions/unix_exception.hpp" 26 | #include "exceptions/operation_canceled_exception.hpp" 27 | 28 | #define NKG_CURRENT_SOURCE_FILE() ".\\navicat-patcher\\main.cpp" 29 | #define NKG_CURRENT_SOURCE_LINE() __LINE__ 30 | 31 | void welcome() { 32 | puts("***************************************************"); 33 | puts("* navicat-patcher by @DoubleLabyrinth *"); 34 | puts("* version: 16.0.7.0-3 *"); 35 | puts("***************************************************"); 36 | puts(""); 37 | } 38 | 39 | void help() { 40 | puts("Usage:"); 41 | puts(" navicat-patcher [--dry-run] [RSA-2048 private key file]"); 42 | puts(""); 43 | puts(" [--dry-run] Run patcher without applying any patches."); 44 | puts(" This parameter is optional."); 45 | puts(""); 46 | puts(" Path to a directory where Navicat locates."); 47 | puts(" This parameter is mandatory."); 48 | puts(""); 49 | puts(" [RSA-2048 private key file] Path to an RSA-2048 private key file."); 50 | puts(" If not specified, an RSA-2048 private key file"); 51 | puts(" named \"RegPrivateKey.pem\" will be generated."); 52 | puts(" This parameter is optional."); 53 | puts(""); 54 | puts("Example:"); 55 | puts(" ./navicat-patcher ~/navicat16-premium-en-patched"); 56 | puts(""); 57 | } 58 | 59 | bool parse_cmdline(int argc, char* argv[], bool& dry_run, std::filesystem::path& navicat_root, std::filesystem::path& rsa_keyfile) { 60 | if (argc == 2) { 61 | dry_run = false; 62 | navicat_root = argv[1]; 63 | rsa_keyfile.clear(); 64 | return true; 65 | } else if (argc == 3) { 66 | if (strcmp(argv[1], "--dry-run") == 0) { 67 | dry_run = true; 68 | navicat_root = argv[2]; 69 | rsa_keyfile.clear(); 70 | return true; 71 | } else { 72 | dry_run = false; 73 | navicat_root = argv[1]; 74 | rsa_keyfile = argv[2]; 75 | return true; 76 | } 77 | } else if (argc == 4) { 78 | if (strcmp(argv[1], "--dry-run") == 0) { 79 | dry_run = true; 80 | navicat_root = argv[2]; 81 | rsa_keyfile = argv[3]; 82 | return true; 83 | } else { 84 | return false; 85 | } 86 | } else { 87 | return false; 88 | } 89 | } 90 | 91 | void select_patch_solutions(nkg::resource_wrapper>& solution0) { 92 | return; 93 | } 94 | 95 | void load_rsa_privkey(nkg::rsa_cipher& cipher, std::filesystem::path& rsa_key_file, nkg::patch_solution* solution0) { 96 | if (!rsa_key_file.empty()) { 97 | printf("[*] Import RSA-2048 private key from\n"); 98 | printf(" %s\n", rsa_key_file.native().c_str()); 99 | 100 | cipher.import_private_key_file(rsa_key_file.native()); 101 | 102 | if (solution0 && !solution0->check_rsa_privkey(cipher)) { 103 | throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "The RSA private key you provide cannot be used."); 104 | } 105 | } else { 106 | printf("[*] Generating new RSA private key, it may take a long time...\n"); 107 | 108 | do { 109 | cipher.generate_key(2048); 110 | } while (solution0 && !solution0->check_rsa_privkey(cipher)); // re-generate RSA key if one of `check_rsa_privkey` returns false 111 | } 112 | 113 | printf("[*] Your RSA private key:\n%s\n", cipher.export_private_key_string().c_str()); 114 | } 115 | 116 | template 117 | bool all_patch_solutions_are_suppressed(args_t&&... args) { 118 | return (!args.is_valid() && ...); 119 | } 120 | 121 | void detect_backup(const std::filesystem::path& file_path) { 122 | std::filesystem::path backup_path = file_path.native() + ".bak"; 123 | if (std::filesystem::is_regular_file(backup_path)) { 124 | while (true) { 125 | printf("[*] Previous backup %s is detected. Delete? (y/n)", backup_path.native().c_str()); 126 | 127 | auto select = getchar(); 128 | while (select != '\n' && getchar() != '\n') {} 129 | 130 | if (select == 'Y' || select == 'y') { 131 | std::filesystem::remove(backup_path); 132 | break; 133 | } else if (select == 'N' || select == 'n') { 134 | throw nkg::exceptions::operation_canceled_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Backup file still exists. Patch abort!"); 135 | } else { 136 | continue; 137 | } 138 | } 139 | } 140 | } 141 | 142 | void make_backup(const std::filesystem::path& file_path) { 143 | std::filesystem::path backup_path = file_path.native() + ".bak"; 144 | if (std::filesystem::exists(backup_path)) { 145 | throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Previous backup is detected.") 146 | .push_hint(fmt::format("Please delete {} and try again.", backup_path.native())); 147 | } else { 148 | std::filesystem::copy_file(file_path, backup_path); 149 | } 150 | } 151 | 152 | int main(int argc, char* argv[]) { 153 | welcome(); 154 | 155 | bool dry_run = false; 156 | std::filesystem::path navicat_root; 157 | std::filesystem::path rsa_key_file; 158 | 159 | if (!parse_cmdline(argc, argv, dry_run, navicat_root, rsa_key_file)) { 160 | help(); 161 | return -1; 162 | } 163 | 164 | try { 165 | if (!std::filesystem::is_directory(navicat_root)) { 166 | throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Navicat root directory path doesn't point to a directory.") 167 | .push_hint("Are you sure the path you specified is correct?") 168 | .push_hint(fmt::format("The path you specified: {}", navicat_root.native())); 169 | } 170 | 171 | if (!rsa_key_file.empty() && !std::filesystem::is_regular_file(rsa_key_file)) { 172 | throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "RSA private key file path doesn't point to a file.") 173 | .push_hint("Are you sure the path you specified is correct?") 174 | .push_hint(fmt::format("The path you specified: {}", rsa_key_file.native())); 175 | } 176 | 177 | nkg::rsa_cipher cipher; 178 | 179 | std::filesystem::path libcc_filepath = navicat_root / "usr" / "lib" / "libcc.so"; 180 | nkg::resource_wrapper libcc_fd{ nkg::resource_traits::unix_os::file_descriptor{} }; 181 | nkg::resource_wrapper libcc_stat{ nkg::resource_traits::cxx_object_traits{} }; 182 | nkg::resource_wrapper libcc_map_view{ nkg::resource_traits::unix_os::map_view{}, [&libcc_stat](void* p) { munmap(p, libcc_stat->st_size); } }; 183 | std::optional libcc_interpreter; 184 | 185 | nkg::resource_wrapper solution0{ nkg::resource_traits::cxx_object_traits{} }; 186 | 187 | // open libcc.dll 188 | libcc_fd.set(open(libcc_filepath.native().c_str(), O_RDWR)); 189 | if (libcc_fd.is_valid()) { 190 | printf("[+] Try to open libcc.dll ... OK!\n"); 191 | } else { 192 | if (errno == ENOENT) { 193 | printf("[-] Try to open libcc.dll ... NOT FOUND!\n"); 194 | } else { 195 | throw nkg::exceptions::unix_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), errno, "open failed."); 196 | } 197 | } 198 | 199 | if (libcc_fd.is_valid()) { 200 | libcc_stat.set(new struct stat()); 201 | if (fstat(libcc_fd.get(), libcc_stat.get()) != 0) { 202 | throw nkg::exceptions::unix_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), errno, "fstat failed."); 203 | } 204 | 205 | libcc_map_view.set(mmap(nullptr, libcc_stat->st_size, PROT_READ | PROT_WRITE, MAP_SHARED, libcc_fd.get(), 0)); 206 | if (!libcc_map_view.is_valid()) { 207 | throw nkg::exceptions::unix_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), errno, "mmap failed."); 208 | } 209 | 210 | libcc_interpreter = nkg::elf64_interpreter::parse(libcc_map_view.get(), libcc_stat->st_size); 211 | 212 | solution0.set(new nkg::patch_solution_since<16, 0, 7, 0>(libcc_interpreter.value())); 213 | } 214 | 215 | puts(""); 216 | 217 | // find patch and decide which solution will be applied 218 | if (solution0.is_valid()) { 219 | auto patch_found = solution0->find_patch(); 220 | puts(""); 221 | 222 | if (!patch_found) { 223 | solution0.release(); 224 | } 225 | } 226 | 227 | select_patch_solutions(solution0); 228 | 229 | if (all_patch_solutions_are_suppressed(solution0)) { 230 | throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "All patch solutions are suppressed. Patch abort!") 231 | .push_hint("Are you sure your navicat has not been patched/modified before?"); 232 | } 233 | 234 | // load key 235 | load_rsa_privkey(cipher, rsa_key_file, solution0.get()); 236 | 237 | // apply patch solutions 238 | if (dry_run) { 239 | puts("*******************************************************"); 240 | puts("* DRY-RUN MODE ENABLE! *"); 241 | puts("* NO PATCH WILL BE APPLIED! *"); 242 | puts("*******************************************************"); 243 | } else { 244 | // save private key if not given 245 | if (rsa_key_file.empty()) { 246 | cipher.export_private_key_file("RegPrivateKey.pem"); 247 | } 248 | 249 | // detecting backups 250 | if (solution0.is_valid()) { 251 | detect_backup(libcc_filepath); 252 | } 253 | 254 | // make backup 255 | if (solution0.is_valid()) { 256 | make_backup(libcc_filepath); 257 | } 258 | 259 | // make patch 260 | // no way to go back from here :-) 261 | if (solution0.is_valid()) { 262 | solution0->make_patch(cipher); 263 | } 264 | 265 | // print new key file path 266 | if (rsa_key_file.empty()) { 267 | printf("[*] New RSA-2048 private key has been saved to\n"); 268 | printf(" %s\n", (std::filesystem::current_path() / "RegPrivateKey.pem").c_str()); 269 | } 270 | 271 | puts(""); 272 | puts("*******************************************************"); 273 | puts("* PATCH HAS BEEN DONE SUCCESSFULLY! *"); 274 | puts("* HAVE FUN AND ENJOY~ *"); 275 | puts("*******************************************************"); 276 | } 277 | 278 | return 0; 279 | } catch (nkg::exception& e) { 280 | printf("[-] %s:%d ->\n", e.source_file().c_str(), e.source_line()); 281 | printf(" %s\n", e.custom_message().c_str()); 282 | if (e.error_code_exists()) { 283 | printf(" %s (0x%zx)\n", e.error_string().c_str(), e.error_code()); 284 | } 285 | 286 | for (auto& hint : e.hints()) { 287 | printf(" HINT: %s\n", hint.c_str()); 288 | } 289 | 290 | return -1; 291 | } 292 | } 293 | 294 | #undef NKG_CURRENT_SOURCE_LINE 295 | #undef NKG_CURRENT_SOURCE_FILE 296 | -------------------------------------------------------------------------------- /navicat-patcher/memory_utility.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace nkg { 7 | 8 | template 9 | [[nodiscard]] 10 | inline ptrdiff_t address_delta(ptr1_t ptr1, ptr2_t ptr2) noexcept { 11 | static_assert(std::is_pointer_v && std::is_pointer_v); 12 | return reinterpret_cast(ptr1) - reinterpret_cast(ptr2); 13 | } 14 | 15 | template 16 | [[nodiscard]] 17 | inline ptr_t address_offset(ptr_t ptr, ptrdiff_t off) noexcept { 18 | static_assert(std::is_pointer_v); 19 | return reinterpret_cast( 20 | const_cast( 21 | reinterpret_cast(ptr) + off 22 | ) 23 | ); 24 | } 25 | 26 | template 27 | [[nodiscard]] 28 | inline return_ptr_t address_offset_cast(ptr_t ptr, ptrdiff_t off) noexcept { 29 | static_assert(std::is_pointer_v && std::is_pointer_v); 30 | return reinterpret_cast(address_offset(ptr, off)); 31 | } 32 | 33 | template 34 | [[nodiscard]] 35 | inline bool is_address_in_range(ptr_t ptr, begin_ptr_t begin, end_ptr_t end) { 36 | static_assert(std::is_pointer_v && std::is_pointer_v && std::is_pointer_v); 37 | auto _ptr = reinterpret_cast(ptr); 38 | auto _begin = reinterpret_cast(begin); 39 | auto _end = reinterpret_cast(end); 40 | return _begin <= _ptr && _ptr < _end; 41 | } 42 | 43 | template 44 | [[nodiscard]] 45 | inline bool is_address_in_range(ptr_t ptr, base_ptr_t base, size_t size) { 46 | static_assert(std::is_pointer_v && std::is_pointer_v); 47 | return is_address_in_range(ptr, base, address_offset(base, size)); 48 | } 49 | 50 | template 51 | [[nodiscard]] 52 | inline bool is_address_in_range(ptr1_t ptr1, ptr2_t ptr2, begin_ptr_t begin, end_ptr_t end) { 53 | static_assert(std::is_pointer_v && std::is_pointer_v && std::is_pointer_v && std::is_pointer_v); 54 | auto _ptr1 = reinterpret_cast(ptr1); 55 | auto _ptr2 = reinterpret_cast(ptr2); 56 | auto _begin = reinterpret_cast(begin); 57 | auto _end = reinterpret_cast(end); 58 | return _begin <= _ptr1 && _ptr1 <= _ptr2 && _ptr2 <= _end; 59 | } 60 | 61 | template 62 | [[nodiscard]] 63 | inline bool is_address_in_range(ptr_t ptr, size_t size, begin_ptr_t begin, end_ptr_t end) { 64 | static_assert(std::is_pointer_v && std::is_pointer_v && std::is_pointer_v); 65 | return is_address_in_range(ptr, address_offset(ptr, size), begin, end); 66 | } 67 | 68 | template 69 | [[nodiscard]] 70 | inline bool is_address_in_range(ptr1_t ptr1, ptr2_t ptr2, base_ptr_t base, size_t size) { 71 | static_assert(std::is_pointer_v && std::is_pointer_v && std::is_pointer_v); 72 | return is_address_in_range(ptr1, ptr2, base, address_offset(base, size)); 73 | } 74 | 75 | template 76 | [[nodiscard]] 77 | inline bool is_address_in_range(ptr_t p, size_t s, base_ptr_t base, size_t size) { 78 | static_assert(std::is_pointer_v); 79 | static_assert(std::is_pointer_v); 80 | return is_address_in_range(p, address_offset(p, s), base, address_offset(base, size)); 81 | } 82 | 83 | // template 84 | // [[nodiscard]] 85 | // inline __ReadType AddressRead(__PtrType p) noexcept { 86 | // static_assert(std::is_trivial_v<__ReadType> && std::is_standard_layout_v<__ReadType>); 87 | // static_assert(std::is_pointer_v<__PtrType>); 88 | // return *reinterpret_cast(p); 89 | // } 90 | // 91 | // template 92 | // [[nodiscard]] 93 | // inline __ReadType AddressRead(__PtrType p, ptrdiff_t offset) noexcept { 94 | // static_assert(std::is_trivial_v<__ReadType> && std::is_standard_layout_v<__ReadType>); 95 | // static_assert(std::is_pointer_v<__PtrType>); 96 | // return *reinterpret_cast( 97 | // reinterpret_cast(p) + offset 98 | // ); 99 | // } 100 | // 101 | // template 102 | // [[nodiscard]] 103 | // inline __ReadType AddressRead(__PtrType p, size_t scale, ptrdiff_t index) noexcept { 104 | // static_assert(std::is_trivial_v<__ReadType> && std::is_standard_layout_v<__ReadType>); 105 | // static_assert(std::is_pointer_v<__PtrType>); 106 | // return *reinterpret_cast( 107 | // reinterpret_cast(p) + scale * index 108 | // ); 109 | // } 110 | // 111 | // template 112 | // inline void AddressWrite(__PtrType p, const __WriteType& value) noexcept { 113 | // static_assert(std::is_trivial_v<__WriteType> && std::is_standard_layout_v<__WriteType>); 114 | // static_assert(std::is_pointer_v<__PtrType>); 115 | // *reinterpret_cast(p) = value; 116 | // } 117 | // 118 | // template 119 | // inline void AddressWrite(__PtrType p, ptrdiff_t offset, const __WriteType& value) noexcept { 120 | // static_assert(std::is_trivial_v<__WriteType> && std::is_standard_layout_v<__WriteType>); 121 | // static_assert(std::is_pointer_v<__PtrType>); 122 | // *reinterpret_cast( 123 | // reinterpret_cast(p) + offset 124 | // ) = value; 125 | // } 126 | // 127 | // template 128 | // inline void AddressWrite(__PtrType p, size_t scale, ptrdiff_t index, const __WriteType& value) noexcept { 129 | // static_assert(std::is_trivial_v<__WriteType> && std::is_standard_layout_v<__WriteType>); 130 | // static_assert(std::is_pointer_v<__PtrType>); 131 | // *reinterpret_cast( 132 | // reinterpret_cast(p) + scale * index 133 | // ) = value; 134 | // } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /navicat-patcher/patch_solution.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "rsa_cipher.hpp" 3 | 4 | namespace nkg { 5 | 6 | class patch_solution { 7 | public: 8 | [[nodiscard]] 9 | virtual bool find_patch() = 0; 10 | 11 | [[nodiscard]] 12 | virtual bool check_rsa_privkey(const rsa_cipher& cipher) = 0; 13 | 14 | virtual void make_patch(const rsa_cipher& cipher) = 0; 15 | 16 | virtual ~patch_solution() = default; 17 | }; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /navicat-patcher/patch_solution_since.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "patch_solution.hpp" 3 | 4 | namespace nkg { 5 | 6 | template 7 | class patch_solution_since; 8 | 9 | } 10 | -------------------------------------------------------------------------------- /navicat-patcher/patch_solution_since_16.0.7.0.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include "patch_solution_since.hpp" 5 | #include "elf64_interpreter.hpp" 6 | #include "amd64_emulator.hpp" 7 | 8 | namespace nkg { 9 | 10 | template<> 11 | class patch_solution_since<16, 0, 7, 0> final : public patch_solution { 12 | private: 13 | static inline std::string_view official_encoded_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889IqdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginva5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOFR0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmtYyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQawIDAQAB"; 14 | 15 | elf64_interpreter& m_libcc_interpreter; 16 | elf64_interpreter::va_t m_va_CSRegistrationInfoFetcher_LINUX_vtable; 17 | elf64_interpreter::va_t m_va_CSRegistrationInfoFetcher_LINUX_GenerateRegistrationKey; 18 | elf64_interpreter::va_t m_va_pltgot_std_string_append; 19 | std::list> m_tracing; 20 | 21 | uint64_t _emulator_append_external_api_impl(amd64_emulator& x64_emulator, std::string_view api_name, const std::vector& api_impl); 22 | 23 | uint64_t _emulator_malloc(amd64_emulator& x64_emulator, size_t alloc_size); 24 | 25 | void _emulator_free(amd64_emulator& x64_emulator, uint64_t alloc_p); 26 | 27 | bool _emulator_page_fault_handler(amd64_emulator& x64_emulator, uc_mem_type access, uint64_t address, size_t size, int64_t value); 28 | 29 | void _emulator_dl_runtime_resolve_handler(amd64_emulator& x64_emulator, uint64_t address, size_t size); 30 | 31 | void _emulator_malloc_handler(amd64_emulator& x64_emulator, uint64_t address, size_t size); 32 | 33 | void _emulator_free_handler(amd64_emulator& x64_emulator, uint64_t address, size_t size); 34 | 35 | static std::string _build_encoded_key(const rsa_cipher& cipher); 36 | 37 | std::list> _calculate_reliable_areas() const; 38 | 39 | public: 40 | patch_solution_since(elf64_interpreter& libcc_interpreter); 41 | 42 | [[nodiscard]] 43 | virtual bool find_patch() override; 44 | 45 | [[nodiscard]] 46 | virtual bool check_rsa_privkey(const rsa_cipher& cipher) override; 47 | 48 | virtual void make_patch(const rsa_cipher& cipher) override; 49 | 50 | }; 51 | 52 | } 53 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | appimagetoolUrl='https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-x86_64.AppImage' 5 | baseAppImage=$(ls | grep -i '\.AppImage$' | head -n 1) 6 | 7 | echo 8 | 9 | # Check if AppImage exists 10 | if [ -z $baseAppImage ]; then 11 | echo "No AppImage found. Exiting..." 12 | exit 1 13 | fi 14 | baseAppImagePath=$(readlink -f $baseAppImage) 15 | 16 | # User prompt 17 | echo 18 | echo "Detected AppImage: $baseAppImage" 19 | read -p "Start patching with this AppImage? [Y/n] " startScript 20 | if [ $startScript == "n" ] || [ $startScript == "N" ]; then 21 | echo "Exiting..." 22 | exit 0 23 | fi 24 | 25 | # Build navicat patcher 26 | echo 27 | echo "Building Navicat Patcher..." 28 | cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build . -- -j4 29 | cp navicat-patcher navicat-keygen /patcher/bin 30 | cd /patcher 31 | 32 | # Extract AppImage 33 | echo 34 | echo "Extracting AppImage..." 35 | mkdir -p /navicat_mount /navicat 36 | mount -o loop $baseAppImagePath /navicat_mount 37 | cp -r /navicat_mount/* /navicat 38 | umount /navicat_mount 39 | echo "Extracted AppImage." 40 | 41 | # Start Patcher 42 | echo 43 | echo "Starting Patcher..." 44 | /patcher/bin/navicat-patcher /navicat 45 | echo "Navicat Patched." 46 | 47 | # Repack 48 | echo 49 | echo "Repacking AppImage..." 50 | wget -O appimagetool.AppImage $appimagetoolUrl 51 | chmod +x appimagetool.AppImage 52 | ./appimagetool.AppImage /navicat /patcher/bin/$(echo $baseAppImage | sed 's/\.AppImage$/-patched.AppImage/') 53 | echo "Repacked AppImage." 54 | 55 | # Start Keygen 56 | echo 57 | echo "Starting Keygen... (REMEMBER TO DISCONNECT INTERNET)" 58 | read -p "Continue? [Y/n] " startKeygen 59 | if [ $startKeygen == "n" ] || [ $startKeygen == "N" ]; then 60 | echo "Exiting..." 61 | exit 0 62 | fi 63 | /patcher/bin/navicat-keygen --text ./RegPrivateKey.pem 64 | echo 65 | echo "Copy activation code and continue activation" 66 | echo "Exiting..." 67 | exit 0 --------------------------------------------------------------------------------