upload codebase
Signed-off-by: Double Sine <xiao_ai_yu@live.cn>
This commit is contained in:
commit
9017721a5c
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
cmake-build-*
|
||||||
84
CMakeLists.txt
Normal file
84
CMakeLists.txt
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
cmake_minimum_required(VERSION 3.21)
|
||||||
|
project(navicat_keygen)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
set(KEYSTONE_BUILD_STATIC_RUNTIME ON CACHE BOOL "" FORCE)
|
||||||
|
set(BUILD_LIBS_ONLY ON CACHE BOOL "" FORCE)
|
||||||
|
set(UNICORN_ARCH "x86" CACHE STRING "" FORCE)
|
||||||
|
set(UNICORN_INSTALL OFF CACHE BOOL "" FORCE)
|
||||||
|
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
||||||
|
set(UNICORN_BUILD_SAMPLES OFF CACHE BOOL "" FORCE)
|
||||||
|
FetchContent_Declare(
|
||||||
|
keystone
|
||||||
|
GIT_REPOSITORY "https://github.com/keystone-engine/keystone.git"
|
||||||
|
GIT_TAG "1475885daa7e566c064ae9754706e1a0ba24be3b"
|
||||||
|
)
|
||||||
|
FetchContent_Declare(
|
||||||
|
unicorn
|
||||||
|
URL "https://github.com/unicorn-engine/unicorn/archive/refs/tags/1.0.3.tar.gz"
|
||||||
|
URL_HASH "SHA256=64fba177dec64baf3f11c046fbb70e91483e029793ec6a3e43b028ef14dc0d65"
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(keystone unicorn)
|
||||||
|
|
||||||
|
set(
|
||||||
|
NKG_COMMON_SOURCE
|
||||||
|
./common/exception.hpp
|
||||||
|
./common/exceptions/index_exception.hpp
|
||||||
|
./common/exceptions/key_exception.hpp
|
||||||
|
./common/exceptions/operation_canceled_exception.hpp
|
||||||
|
./common/exceptions/unix_exception.hpp
|
||||||
|
./common/resource_wrapper.hpp
|
||||||
|
./common/resource_traits/cxx_object_traits.hpp
|
||||||
|
./common/resource_traits/cxx_dynamic_array_traits.hpp
|
||||||
|
./common/resource_traits/keystone/keystone_alloc.hpp
|
||||||
|
./common/resource_traits/keystone/keystone_handle.hpp
|
||||||
|
./common/resource_traits/openssl/bignum.hpp
|
||||||
|
./common/resource_traits/openssl/bio.hpp
|
||||||
|
./common/resource_traits/openssl/bio_chain.hpp
|
||||||
|
./common/resource_traits/openssl/rsa.hpp
|
||||||
|
./common/resource_traits/unicorn/unicorn_handle.hpp
|
||||||
|
./common/resource_traits/unicorn/unicorn_alloc.hpp
|
||||||
|
./common/resource_traits/unix_os/file_descriptor.hpp
|
||||||
|
./common/resource_traits/unix_os/map_view.hpp
|
||||||
|
./common/rsa_cipher.hpp
|
||||||
|
./common/rsa_cipher.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(
|
||||||
|
NKG_KEYGEN_SOURCE
|
||||||
|
./navicat-keygen/base32_rfc4648.hpp
|
||||||
|
./navicat-keygen/base32_rfc4648.cpp
|
||||||
|
./navicat-keygen/base64_rfc4648.hpp
|
||||||
|
./navicat-keygen/base64_rfc4648.cpp
|
||||||
|
./navicat-keygen/navicat_serial_generator.hpp
|
||||||
|
./navicat-keygen/navicat_serial_generator.cpp
|
||||||
|
./navicat-keygen/CollectInformation.cpp
|
||||||
|
./navicat-keygen/GenerateLicense.cpp
|
||||||
|
./navicat-keygen/main.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
set(
|
||||||
|
NKG_PATCHER_SOURCE
|
||||||
|
./navicat-patcher/amd64_emulator.hpp
|
||||||
|
./navicat-patcher/amd64_emulator.cpp
|
||||||
|
./navicat-patcher/keystone_assembler.hpp
|
||||||
|
./navicat-patcher/keystone_assembler.cpp
|
||||||
|
./navicat-patcher/elf64_interpreter.hpp
|
||||||
|
./navicat-patcher/elf64_interpreter.cpp
|
||||||
|
./navicat-patcher/patch_solution.hpp
|
||||||
|
./navicat-patcher/patch_solution_since.hpp
|
||||||
|
./navicat-patcher/patch_solution_since_16.0.7.0.hpp
|
||||||
|
./navicat-patcher/patch_solution_since_16.0.7.0.cpp
|
||||||
|
./navicat-patcher/memory_utility.hpp
|
||||||
|
./navicat-patcher/main.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(navicat-keygen ${NKG_COMMON_SOURCE} ${NKG_KEYGEN_SOURCE})
|
||||||
|
target_include_directories(navicat-keygen PRIVATE ./common)
|
||||||
|
target_link_libraries(navicat-keygen fmt crypto)
|
||||||
|
|
||||||
|
add_executable(navicat-patcher ${NKG_COMMON_SOURCE} ${NKG_PATCHER_SOURCE})
|
||||||
|
target_include_directories(navicat-patcher PRIVATE ./common ${keystone_SOURCE_DIR}/include)
|
||||||
|
target_link_libraries(navicat-patcher fmt crypto keystone unicorn pthread)
|
||||||
90
common/exception.hpp
Normal file
90
common/exception.hpp
Normal file
|
|
@ -0,0 +1,90 @@
|
||||||
|
#pragma once
|
||||||
|
#include <exception>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
class exception : public std::exception {
|
||||||
|
private:
|
||||||
|
int m_source_line;
|
||||||
|
std::string m_source_file;
|
||||||
|
std::string m_custom_message;
|
||||||
|
std::vector<std::string> m_hints;
|
||||||
|
|
||||||
|
public:
|
||||||
|
[[noreturn]]
|
||||||
|
static void trap_then_terminate() {
|
||||||
|
#if _MSC_VER
|
||||||
|
__debugbreak();
|
||||||
|
#elif defined(__GNUC__) || defined(__GNUG__) || defined(__clang__)
|
||||||
|
__builtin_trap();
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "exception.hpp: unknown compiler is detected."
|
||||||
|
#endif
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
exception(std::string_view file, int line, std::string_view message) noexcept :
|
||||||
|
std::exception(), m_source_line(line), m_source_file(file), m_custom_message(message) {}
|
||||||
|
|
||||||
|
exception(const exception&) noexcept = default;
|
||||||
|
exception(exception&&) noexcept = default;
|
||||||
|
|
||||||
|
exception& operator=(const exception&) noexcept = default;
|
||||||
|
exception& operator=(exception&&) noexcept = default;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
int source_line() const noexcept {
|
||||||
|
return m_source_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
const std::string& source_file() const noexcept {
|
||||||
|
return m_source_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
const std::string& custom_message() const noexcept {
|
||||||
|
return m_custom_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
exception&& push_hint(std::string_view hint) noexcept {
|
||||||
|
m_hints.emplace_back(hint);
|
||||||
|
return std::move(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
exception&& pop_hint() noexcept {
|
||||||
|
m_hints.pop_back();
|
||||||
|
return std::move(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string>& hints() const noexcept {
|
||||||
|
return m_hints;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual const char* what() const noexcept override {
|
||||||
|
return m_custom_message.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual bool error_code_exists() const noexcept {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual intptr_t error_code() const noexcept {
|
||||||
|
trap_then_terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual const std::string& error_string() const noexcept {
|
||||||
|
trap_then_terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~exception() override = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
12
common/exceptions/index_exception.hpp
Normal file
12
common/exceptions/index_exception.hpp
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
#include "../exception.hpp"
|
||||||
|
|
||||||
|
namespace nkg::exceptions {
|
||||||
|
|
||||||
|
class index_exception : public ::nkg::exception {
|
||||||
|
public:
|
||||||
|
index_exception(std::string_view file, int line, std::string_view message) noexcept :
|
||||||
|
::nkg::exception(file, line, message) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
12
common/exceptions/key_exception.hpp
Normal file
12
common/exceptions/key_exception.hpp
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
#include "../exception.hpp"
|
||||||
|
|
||||||
|
namespace nkg::exceptions {
|
||||||
|
|
||||||
|
class key_exception : public ::nkg::exception {
|
||||||
|
public:
|
||||||
|
key_exception(std::string_view file, int line, std::string_view message) noexcept :
|
||||||
|
::nkg::exception(file, line, message) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
12
common/exceptions/operation_canceled_exception.hpp
Normal file
12
common/exceptions/operation_canceled_exception.hpp
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
#include "../exception.hpp"
|
||||||
|
|
||||||
|
namespace nkg::exceptions {
|
||||||
|
|
||||||
|
class operation_canceled_exception : public ::nkg::exception {
|
||||||
|
public:
|
||||||
|
operation_canceled_exception(std::string_view file, int line, std::string_view message) noexcept :
|
||||||
|
::nkg::exception(file, line, message) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
36
common/exceptions/unix_exception.hpp
Normal file
36
common/exceptions/unix_exception.hpp
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
#pragma once
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "../exception.hpp"
|
||||||
|
|
||||||
|
namespace nkg::exceptions {
|
||||||
|
|
||||||
|
class unix_exception : public ::nkg::exception {
|
||||||
|
public:
|
||||||
|
using error_code_t = decltype(errno);
|
||||||
|
|
||||||
|
private:
|
||||||
|
error_code_t m_error_code;
|
||||||
|
std::string m_error_string;
|
||||||
|
|
||||||
|
public:
|
||||||
|
unix_exception(std::string_view file, int line, error_code_t unix_errno, std::string_view message) noexcept :
|
||||||
|
::nkg::exception(file, line, message), m_error_code(unix_errno), m_error_string(strerror(unix_errno)) {}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual bool error_code_exists() const noexcept override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual intptr_t error_code() const noexcept override {
|
||||||
|
return m_error_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual const std::string& error_string() const noexcept override {
|
||||||
|
return m_error_string;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
21
common/resource_traits/cxx_dynamic_array_traits.hpp
Normal file
21
common/resource_traits/cxx_dynamic_array_traits.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace nkg::resource_traits {
|
||||||
|
|
||||||
|
template<typename element_t>
|
||||||
|
struct cxx_dynamic_array_traits {
|
||||||
|
using handle_t = element_t*;
|
||||||
|
|
||||||
|
static constexpr handle_t invalid_value = nullptr;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static bool is_valid(const handle_t& handle) noexcept {
|
||||||
|
return handle != invalid_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void release(const handle_t& handle) {
|
||||||
|
delete[] handle;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
21
common/resource_traits/cxx_object_traits.hpp
Normal file
21
common/resource_traits/cxx_object_traits.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace nkg::resource_traits {
|
||||||
|
|
||||||
|
template<typename object_t>
|
||||||
|
struct cxx_object_traits {
|
||||||
|
using handle_t = object_t*;
|
||||||
|
|
||||||
|
static constexpr handle_t invalid_value = nullptr;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static bool is_valid(const handle_t& handle) noexcept {
|
||||||
|
return handle != invalid_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void release(const handle_t& handle) {
|
||||||
|
delete handle;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
20
common/resource_traits/keystone/keystone_alloc.hpp
Normal file
20
common/resource_traits/keystone/keystone_alloc.hpp
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
#include <keystone/keystone.h>
|
||||||
|
|
||||||
|
namespace nkg::resource_traits::keystone {
|
||||||
|
|
||||||
|
struct keystone_alloc {
|
||||||
|
using handle_t = unsigned char*;
|
||||||
|
|
||||||
|
static constexpr handle_t invalid_value = nullptr;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static bool is_valid(const handle_t& handle) noexcept {
|
||||||
|
return handle != invalid_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void release(const handle_t& handle) noexcept {
|
||||||
|
ks_free(handle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
21
common/resource_traits/keystone/keystone_handle.hpp
Normal file
21
common/resource_traits/keystone/keystone_handle.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
#include <keystone/keystone.h>
|
||||||
|
|
||||||
|
namespace nkg::resource_traits::keystone {
|
||||||
|
|
||||||
|
struct keystone_handle {
|
||||||
|
using handle_t = ks_engine*;
|
||||||
|
|
||||||
|
static constexpr handle_t invalid_value = nullptr;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static bool is_valid(const handle_t& handle) noexcept {
|
||||||
|
return handle != invalid_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void release(const handle_t& handle) {
|
||||||
|
ks_close(handle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
21
common/resource_traits/openssl/bignum.hpp
Normal file
21
common/resource_traits/openssl/bignum.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
#include <openssl/bn.h>
|
||||||
|
|
||||||
|
namespace nkg::resource_traits::openssl {
|
||||||
|
|
||||||
|
struct bignum {
|
||||||
|
using handle_t = BIGNUM*;
|
||||||
|
|
||||||
|
static constexpr handle_t invalid_value = nullptr;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static bool is_valid(const handle_t& handle) noexcept {
|
||||||
|
return handle != invalid_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void release(const handle_t& handle) noexcept {
|
||||||
|
BN_free(handle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
21
common/resource_traits/openssl/bio.hpp
Normal file
21
common/resource_traits/openssl/bio.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
#include <openssl/bio.h>
|
||||||
|
|
||||||
|
namespace nkg::resource_traits::openssl {
|
||||||
|
|
||||||
|
struct bio {
|
||||||
|
using handle_t = BIO*;
|
||||||
|
|
||||||
|
static constexpr handle_t invalid_value = nullptr;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static bool is_valid(const handle_t& handle) noexcept {
|
||||||
|
return handle != invalid_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void release(const handle_t& handle) noexcept {
|
||||||
|
BIO_free(handle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
21
common/resource_traits/openssl/bio_chain.hpp
Normal file
21
common/resource_traits/openssl/bio_chain.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
#include <openssl/bio.h>
|
||||||
|
|
||||||
|
namespace nkg::resource_traits::openssl {
|
||||||
|
|
||||||
|
struct bio_chain {
|
||||||
|
using handle_t = BIO*;
|
||||||
|
|
||||||
|
static constexpr handle_t invalid_value = nullptr;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static bool is_valid(const handle_t& handle) noexcept {
|
||||||
|
return handle != invalid_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void release(const handle_t& handle) noexcept {
|
||||||
|
BIO_free_all(handle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
21
common/resource_traits/openssl/rsa.hpp
Normal file
21
common/resource_traits/openssl/rsa.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
#include <openssl/rsa.h>
|
||||||
|
|
||||||
|
namespace nkg::resource_traits::openssl {
|
||||||
|
|
||||||
|
struct rsa {
|
||||||
|
using handle_t = RSA*;
|
||||||
|
|
||||||
|
static constexpr handle_t invalid_value = nullptr;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static bool is_valid(const handle_t& handle) noexcept {
|
||||||
|
return handle != invalid_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void release(const handle_t& handle) noexcept {
|
||||||
|
RSA_free(handle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
21
common/resource_traits/unicorn/unicorn_alloc.hpp
Normal file
21
common/resource_traits/unicorn/unicorn_alloc.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
#include <unicorn/unicorn.h>
|
||||||
|
|
||||||
|
namespace nkg::resource_traits::unicorn {
|
||||||
|
|
||||||
|
struct unicorn_alloc {
|
||||||
|
using handle_t = void*;
|
||||||
|
|
||||||
|
static constexpr handle_t invalid_value = nullptr;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static bool is_valid(const handle_t& handle) noexcept {
|
||||||
|
return handle != invalid_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void release(const handle_t& handle) {
|
||||||
|
uc_free(handle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
21
common/resource_traits/unicorn/unicorn_handle.hpp
Normal file
21
common/resource_traits/unicorn/unicorn_handle.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
#include <unicorn/unicorn.h>
|
||||||
|
|
||||||
|
namespace nkg::resource_traits::unicorn {
|
||||||
|
|
||||||
|
struct unicorn_handle {
|
||||||
|
using handle_t = uc_engine*;
|
||||||
|
|
||||||
|
static constexpr handle_t invalid_value = nullptr;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static bool is_valid(const handle_t& handle) noexcept {
|
||||||
|
return handle != invalid_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void release(const handle_t& handle) {
|
||||||
|
uc_close(handle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
21
common/resource_traits/unix_os/file_descriptor.hpp
Normal file
21
common/resource_traits/unix_os/file_descriptor.hpp
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
namespace nkg::resource_traits::unix_os {
|
||||||
|
|
||||||
|
struct file_descriptor {
|
||||||
|
using handle_t = int;
|
||||||
|
|
||||||
|
static constexpr handle_t invalid_value = -1;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static bool is_valid(const handle_t& handle) noexcept {
|
||||||
|
return handle != invalid_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void release(const handle_t& handle) {
|
||||||
|
close(handle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
19
common/resource_traits/unix_os/map_view.hpp
Normal file
19
common/resource_traits/unix_os/map_view.hpp
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
#pragma once
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
namespace nkg::resource_traits::unix_os {
|
||||||
|
|
||||||
|
struct map_view {
|
||||||
|
using handle_t = void*;
|
||||||
|
|
||||||
|
static inline const handle_t invalid_value = MAP_FAILED;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static bool is_valid(const handle_t& handle) noexcept {
|
||||||
|
return handle != invalid_value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
238
common/resource_wrapper.hpp
Normal file
238
common/resource_wrapper.hpp
Normal file
|
|
@ -0,0 +1,238 @@
|
||||||
|
#pragma once
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
template<typename resource_traits_t, typename releaser_t = void>
|
||||||
|
class resource_wrapper {
|
||||||
|
public:
|
||||||
|
using handle_t = typename resource_traits_t::handle_t;
|
||||||
|
static_assert(std::is_trivial_v<handle_t> && std::is_standard_layout_v<handle_t>, "`resource_wrapper` requires a handle with POD type.");
|
||||||
|
|
||||||
|
private:
|
||||||
|
handle_t m_handle;
|
||||||
|
releaser_t m_releaser;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<typename releaser_arg_t>
|
||||||
|
resource_wrapper(releaser_arg_t&& releaser) noexcept :
|
||||||
|
m_handle(resource_traits_t::invalid_value),
|
||||||
|
m_releaser(std::forward<releaser_arg_t>(releaser)) {}
|
||||||
|
|
||||||
|
template<typename releaser_arg_t>
|
||||||
|
resource_wrapper(const handle_t& handle, releaser_arg_t&& releaser) noexcept :
|
||||||
|
m_handle(handle),
|
||||||
|
m_releaser(std::forward<releaser_arg_t>(releaser)) {}
|
||||||
|
|
||||||
|
template<typename releaser_arg_t>
|
||||||
|
resource_wrapper(resource_traits_t, releaser_arg_t&& releaser) noexcept :
|
||||||
|
m_handle(resource_traits_t::invalid_value),
|
||||||
|
m_releaser(std::forward<releaser_arg_t>(releaser)) {}
|
||||||
|
|
||||||
|
template<typename releaser_arg_t>
|
||||||
|
resource_wrapper(resource_traits_t, const handle_t& handle, releaser_arg_t&& releaser) noexcept :
|
||||||
|
m_handle(handle),
|
||||||
|
m_releaser(std::forward<releaser_t>(releaser)) {}
|
||||||
|
|
||||||
|
//
|
||||||
|
// `resource_wrapper` does not allow copy-construct
|
||||||
|
//
|
||||||
|
resource_wrapper(const resource_wrapper& other) = delete;
|
||||||
|
|
||||||
|
//
|
||||||
|
// `resource_wrapper` allows move-construct.
|
||||||
|
//
|
||||||
|
resource_wrapper(resource_wrapper&& other) noexcept :
|
||||||
|
m_handle(other.m_handle),
|
||||||
|
m_releaser(std::move(other.m_releaser))
|
||||||
|
{
|
||||||
|
other.m_handle = resource_traits_t::invalid_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// `resource_wrapper` does not allow to copy.
|
||||||
|
//
|
||||||
|
resource_wrapper& operator=(const resource_wrapper& other) = delete;
|
||||||
|
|
||||||
|
//
|
||||||
|
// `resource_wrapper` allows to move.
|
||||||
|
//
|
||||||
|
resource_wrapper& operator=(resource_wrapper&& other) noexcept {
|
||||||
|
if (this != std::addressof(other)) {
|
||||||
|
m_handle = other.m_handle;
|
||||||
|
m_releaser = std::move(other.m_releaser);
|
||||||
|
other.m_handle = resource_traits_t::invalid_value;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ptr_t = handle_t, std::enable_if_t<std::is_pointer_v<handle_t>, ptr_t> = nullptr>
|
||||||
|
[[nodiscard]]
|
||||||
|
ptr_t operator->() const noexcept {
|
||||||
|
return m_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename as_t>
|
||||||
|
[[nodiscard]]
|
||||||
|
as_t as() const noexcept {
|
||||||
|
return reinterpret_cast<as_t>(m_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
bool is_valid() const noexcept {
|
||||||
|
return resource_traits_t::is_valid(m_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
const handle_t& get() const noexcept {
|
||||||
|
return m_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename as_t = handle_t>
|
||||||
|
[[nodiscard]]
|
||||||
|
as_t* unsafe_addressof() noexcept {
|
||||||
|
return reinterpret_cast<as_t*>(std::addressof(m_handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(const handle_t& handle) {
|
||||||
|
if (is_valid()) {
|
||||||
|
m_releaser(m_handle);
|
||||||
|
}
|
||||||
|
m_handle = handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void discard() noexcept {
|
||||||
|
m_handle = resource_traits_t::invalid_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
handle_t transfer() noexcept {
|
||||||
|
handle_t t = m_handle;
|
||||||
|
m_handle = resource_traits_t::invalid_value;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
void release() {
|
||||||
|
if (is_valid()) {
|
||||||
|
m_releaser(m_handle);
|
||||||
|
m_handle = resource_traits_t::invalid_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~resource_wrapper() {
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename resource_traits_t>
|
||||||
|
class resource_wrapper<resource_traits_t, void> {
|
||||||
|
public:
|
||||||
|
using handle_t = typename resource_traits_t::handle_t;
|
||||||
|
static_assert(std::is_trivial_v<handle_t>&& std::is_standard_layout_v<handle_t>, "`resource_wrapper` requires a handle with POD type.");
|
||||||
|
|
||||||
|
private:
|
||||||
|
handle_t m_handle;
|
||||||
|
|
||||||
|
public:
|
||||||
|
resource_wrapper() noexcept :
|
||||||
|
m_handle(resource_traits_t::invalid_value) {}
|
||||||
|
|
||||||
|
resource_wrapper(const handle_t& handle) noexcept :
|
||||||
|
m_handle(handle) {}
|
||||||
|
|
||||||
|
resource_wrapper(resource_traits_t) noexcept :
|
||||||
|
m_handle(resource_traits_t::invalid_value) {}
|
||||||
|
|
||||||
|
resource_wrapper(resource_traits_t, const handle_t& handle) noexcept :
|
||||||
|
m_handle(handle) {}
|
||||||
|
|
||||||
|
resource_wrapper(const resource_wrapper& other) = delete;
|
||||||
|
|
||||||
|
resource_wrapper(resource_wrapper&& other) noexcept :
|
||||||
|
m_handle(other.m_handle)
|
||||||
|
{
|
||||||
|
other.m_handle = resource_traits_t::invalid_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
resource_wrapper& operator=(const resource_wrapper& other) = delete;
|
||||||
|
|
||||||
|
resource_wrapper& operator=(resource_wrapper&& other) noexcept {
|
||||||
|
if (this != std::addressof(other)) {
|
||||||
|
m_handle = other.m_handle;
|
||||||
|
other.m_handle = resource_traits_t::invalid_value;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ptr_t = handle_t, std::enable_if_t<std::is_pointer_v<handle_t>, ptr_t> = nullptr>
|
||||||
|
[[nodiscard]]
|
||||||
|
ptr_t operator->() const noexcept {
|
||||||
|
return m_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename as_t>
|
||||||
|
[[nodiscard]]
|
||||||
|
as_t as() const noexcept {
|
||||||
|
return reinterpret_cast<as_t>(m_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
bool is_valid() const noexcept {
|
||||||
|
return resource_traits_t::is_valid(m_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
const handle_t& get() const noexcept {
|
||||||
|
return m_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename as_t = handle_t>
|
||||||
|
[[nodiscard]]
|
||||||
|
as_t* unsafe_addressof() noexcept {
|
||||||
|
return reinterpret_cast<as_t*>(std::addressof(m_handle));
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(const handle_t& handle) {
|
||||||
|
if (is_valid()) {
|
||||||
|
resource_traits_t::release(m_handle);
|
||||||
|
}
|
||||||
|
m_handle = handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void discard() noexcept {
|
||||||
|
m_handle = resource_traits_t::invalid_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
handle_t transfer() noexcept {
|
||||||
|
handle_t t = m_handle;
|
||||||
|
m_handle = resource_traits_t::invalid_value;
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
void release() {
|
||||||
|
if (is_valid()) {
|
||||||
|
resource_traits_t::release(m_handle);
|
||||||
|
m_handle = resource_traits_t::invalid_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~resource_wrapper() {
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename resource_traits_t>
|
||||||
|
resource_wrapper(resource_traits_t) ->
|
||||||
|
resource_wrapper<resource_traits_t, void>;
|
||||||
|
|
||||||
|
template<typename resource_traits_t, typename arg_t>
|
||||||
|
resource_wrapper(resource_traits_t, arg_t&&) ->
|
||||||
|
resource_wrapper<resource_traits_t, std::conditional_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<arg_t>>, typename resource_traits_t::handle_t>, std::remove_reference_t<arg_t>, void>>;
|
||||||
|
|
||||||
|
template<typename resource_traits_t, typename releaser_t, typename handle_t = typename resource_traits_t::handle_t>
|
||||||
|
resource_wrapper(resource_traits_t, const handle_t&, releaser_t&&) ->
|
||||||
|
resource_wrapper<resource_traits_t, std::remove_reference_t<releaser_t>>;
|
||||||
|
|
||||||
|
}
|
||||||
327
common/rsa_cipher.cpp
Normal file
327
common/rsa_cipher.cpp
Normal file
|
|
@ -0,0 +1,327 @@
|
||||||
|
#include "rsa_cipher.hpp"
|
||||||
|
#include <openssl/pem.h>
|
||||||
|
#include <openssl/bio.h>
|
||||||
|
|
||||||
|
#include "resource_traits/openssl/bio.hpp"
|
||||||
|
#include "resource_traits/openssl/bignum.hpp"
|
||||||
|
|
||||||
|
#define NKG_CURRENT_SOURCE_FILE() u8".\\common\\rsa_cipher.cpp"
|
||||||
|
#define NKG_CURRENT_SOURCE_LINE() __LINE__
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
RSA* rsa_cipher::_read_private_key_from_bio(BIO* p_bio) {
|
||||||
|
resource_wrapper new_rsa
|
||||||
|
{ resource_traits::openssl::rsa{}, PEM_read_bio_RSAPrivateKey(p_bio, nullptr, nullptr, nullptr) };
|
||||||
|
|
||||||
|
if (new_rsa.is_valid()) {
|
||||||
|
return new_rsa.transfer();
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_read_bio_RSAPrivateKey failed.")
|
||||||
|
.push_hint(u8"Are you sure that you DO provide a valid RSA private key file?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RSA* rsa_cipher::_read_public_key_pem_from_bio(BIO* p_bio) {
|
||||||
|
resource_wrapper new_rsa
|
||||||
|
{ resource_traits::openssl::rsa{}, PEM_read_bio_RSA_PUBKEY(p_bio, nullptr, nullptr, nullptr) };
|
||||||
|
|
||||||
|
if (new_rsa.is_valid()) {
|
||||||
|
return new_rsa.transfer();
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_read_bio_RSA_PUBKEY failed.")
|
||||||
|
.push_hint(u8"Are you sure that you DO provide a valid RSA public key file with PEM format?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RSA* rsa_cipher::_read_public_key_pkcs1_from_bio(BIO* p_bio) {
|
||||||
|
resource_wrapper new_rsa
|
||||||
|
{ resource_traits::openssl::rsa{}, PEM_read_bio_RSAPublicKey(p_bio, nullptr, nullptr, nullptr) };
|
||||||
|
|
||||||
|
if (new_rsa.is_valid()) {
|
||||||
|
return new_rsa.transfer();
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_read_bio_RSAPublicKey failed.")
|
||||||
|
.push_hint(u8"Are you sure that you DO provide a valid RSA public key file with PKCS1 format?");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rsa_cipher::_write_private_key_to_bio(RSA* p_rsa, BIO* p_bio) {
|
||||||
|
auto r = PEM_write_bio_RSAPrivateKey(p_bio, p_rsa, nullptr, nullptr, 0, nullptr, nullptr);
|
||||||
|
if (r == 0) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_write_bio_RSAPrivateKey failed.");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void rsa_cipher::_write_public_key_pem_to_bio(RSA* p_rsa, BIO* p_bio) {
|
||||||
|
auto r = PEM_write_bio_RSA_PUBKEY(p_bio, p_rsa);
|
||||||
|
if (r == 0) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_write_bio_RSA_PUBKEY failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rsa_cipher::_write_public_key_pkcs1_to_bio(RSA* p_rsa, BIO* p_bio) {
|
||||||
|
auto r = PEM_write_bio_RSAPublicKey(p_bio, p_rsa);
|
||||||
|
if (r == 0) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"PEM_write_bio_RSAPublicKey failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rsa_cipher::rsa_cipher() : m_rsa(RSA_new()) {
|
||||||
|
if (!m_rsa.is_valid()) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_new failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
size_t rsa_cipher::bits() const {
|
||||||
|
#if (OPENSSL_VERSION_NUMBER & 0xffff0000) == 0x10000000 // openssl 1.0.x
|
||||||
|
if (m_rsa->n) {
|
||||||
|
return BN_num_bits(m_rsa->n);
|
||||||
|
} else {
|
||||||
|
throw no_key_assigned_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"RSA modulus has not been set.");
|
||||||
|
}
|
||||||
|
#elif (OPENSSL_VERSION_NUMBER & 0xffff0000) == 0x10100000 // openssl 1.1.x
|
||||||
|
return RSA_bits(m_rsa.get());
|
||||||
|
#else
|
||||||
|
#error "rsa_cipher.cpp: uexpected OpenSSL version"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void rsa_cipher::generate_key(int bits, unsigned int e) {
|
||||||
|
resource_wrapper bn_e{ resource_traits::openssl::bignum{}, BN_new() };
|
||||||
|
|
||||||
|
if (!bn_e.is_valid()) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"BN_new failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BN_set_word(bn_e.get(), e) == 0) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BN_set_word failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RSA_generate_key_ex(m_rsa.get(), bits, bn_e.get(), nullptr) == 0) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_generate_key_ex failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rsa_cipher::export_private_key_file(std::string_view file_path) const {
|
||||||
|
resource_wrapper bio_file
|
||||||
|
{ resource_traits::openssl::bio{}, BIO_new_file(file_path.data(), "w")};
|
||||||
|
|
||||||
|
if (bio_file.is_valid()) {
|
||||||
|
_write_private_key_to_bio(m_rsa.get(), bio_file.get());
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rsa_cipher::export_public_key_file_pem(std::string_view file_path) const {
|
||||||
|
resource_wrapper bio_file
|
||||||
|
{ resource_traits::openssl::bio{}, BIO_new_file(file_path.data(), "w")};
|
||||||
|
|
||||||
|
if (bio_file.is_valid()) {
|
||||||
|
_write_public_key_pem_to_bio(m_rsa.get(), bio_file.get());
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rsa_cipher::export_public_key_file_pkcs1(std::string_view file_path) const {
|
||||||
|
resource_wrapper bio_file
|
||||||
|
{ resource_traits::openssl::bio{}, BIO_new_file(file_path.data(), "w")};
|
||||||
|
|
||||||
|
if (bio_file.is_valid()) {
|
||||||
|
_write_public_key_pkcs1_to_bio(m_rsa.get(), bio_file.get());
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rsa_cipher::import_private_key_file(std::string_view file_path) {
|
||||||
|
resource_wrapper bio_file
|
||||||
|
{ resource_traits::openssl::bio{}, BIO_new_file(file_path.data(), "r") };
|
||||||
|
|
||||||
|
if (bio_file.is_valid()) {
|
||||||
|
m_rsa.set(_read_private_key_from_bio(bio_file.get()));
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rsa_cipher::import_public_key_file_pem(std::string_view file_path) {
|
||||||
|
resource_wrapper bio_file
|
||||||
|
{ resource_traits::openssl::bio{}, BIO_new_file(file_path.data(), "r") };
|
||||||
|
|
||||||
|
if (bio_file.is_valid()) {
|
||||||
|
m_rsa.set(_read_public_key_pem_from_bio(bio_file.get()));
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rsa_cipher::import_public_key_file_pkcs1(std::string_view file_path) {
|
||||||
|
resource_wrapper bio_file
|
||||||
|
{ resource_traits::openssl::bio{}, BIO_new_file(file_path.data(), "r") };
|
||||||
|
|
||||||
|
if (bio_file.is_valid()) {
|
||||||
|
m_rsa.set(_read_public_key_pkcs1_from_bio(bio_file.get()));
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new_file failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::string rsa_cipher::export_private_key_string() const {
|
||||||
|
resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) };
|
||||||
|
|
||||||
|
if (bio_memory.is_valid()) {
|
||||||
|
_write_private_key_to_bio(m_rsa.get(), bio_memory.get());
|
||||||
|
|
||||||
|
const char* pch = nullptr;
|
||||||
|
long lch = BIO_get_mem_data(bio_memory.get(), &pch);
|
||||||
|
|
||||||
|
return std::string(pch, lch);
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::string rsa_cipher::export_public_key_string_pem() const {
|
||||||
|
resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) };
|
||||||
|
|
||||||
|
if (bio_memory.is_valid()) {
|
||||||
|
_write_public_key_pem_to_bio(m_rsa.get(), bio_memory.get());
|
||||||
|
|
||||||
|
const char* pch = nullptr;
|
||||||
|
long lch = BIO_get_mem_data(bio_memory.get(), &pch);
|
||||||
|
|
||||||
|
return std::string(pch, lch);
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::string rsa_cipher::export_public_key_string_pkcs1() const {
|
||||||
|
resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) };
|
||||||
|
|
||||||
|
if (bio_memory.is_valid()) {
|
||||||
|
_write_public_key_pkcs1_to_bio(m_rsa.get(), bio_memory.get());
|
||||||
|
|
||||||
|
const char* pch = nullptr;
|
||||||
|
long lch = BIO_get_mem_data(bio_memory.get(), &pch);
|
||||||
|
|
||||||
|
return std::string(pch, lch);
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rsa_cipher::import_private_key_string(std::string_view key_string) {
|
||||||
|
resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) };
|
||||||
|
|
||||||
|
if (!bio_memory.is_valid()) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BIO_puts(bio_memory.get(), key_string.data()) <= 0) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_puts failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_rsa.set(_read_private_key_from_bio(bio_memory.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void rsa_cipher::import_public_key_string_pem(std::string_view key_string) {
|
||||||
|
resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) };
|
||||||
|
|
||||||
|
if (!bio_memory.is_valid()) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BIO_puts(bio_memory.get(), key_string.data()) <= 0) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_puts failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_rsa.set(_read_public_key_pem_from_bio(bio_memory.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void rsa_cipher::import_public_key_string_pkcs1(std::string_view key_string) {
|
||||||
|
resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) };
|
||||||
|
|
||||||
|
if (!bio_memory.is_valid()) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BIO_puts(bio_memory.get(), key_string.data()) <= 0) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_puts failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_rsa.set(_read_public_key_pkcs1_from_bio(bio_memory.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t rsa_cipher::public_encrypt(const void* plaintext, size_t plaintext_size, void* ciphertext, int padding) const {
|
||||||
|
if (plaintext_size <= INT_MAX) {
|
||||||
|
int bytes_written =
|
||||||
|
RSA_public_encrypt(static_cast<int>(plaintext_size), reinterpret_cast<const unsigned char*>(plaintext), reinterpret_cast<unsigned char*>(ciphertext), m_rsa.get(), padding);
|
||||||
|
|
||||||
|
if (bytes_written != -1) {
|
||||||
|
return bytes_written;
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_encrypt failed.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"plaintext_size > INT_MAX");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t rsa_cipher::private_encrypt(const void* plaintext, size_t plaintext_size, void* ciphertext, int padding) const {
|
||||||
|
if (plaintext_size <= INT_MAX) {
|
||||||
|
int bytes_written =
|
||||||
|
RSA_private_encrypt(static_cast<int>(plaintext_size), reinterpret_cast<const unsigned char*>(plaintext), reinterpret_cast<unsigned char*>(ciphertext), m_rsa.get(), padding);
|
||||||
|
|
||||||
|
if (bytes_written != -1) {
|
||||||
|
return bytes_written;
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_encrypt failed.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"plaintext_size > INT_MAX");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t rsa_cipher::public_decrypt(const void* ciphertext, size_t ciphertext_size, void* plaintext, int padding) const {
|
||||||
|
if (ciphertext_size <= INT_MAX) {
|
||||||
|
int bytes_written =
|
||||||
|
RSA_public_decrypt(static_cast<int>(ciphertext_size), reinterpret_cast<const unsigned char*>(ciphertext), reinterpret_cast<unsigned char*>(plaintext), m_rsa.get(), padding);
|
||||||
|
|
||||||
|
if (bytes_written != -1) {
|
||||||
|
return bytes_written;
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_decrypt failed.")
|
||||||
|
.push_hint(u8"Are your sure you DO provide a correct public key?");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"ciphertext_size > INT_MAX");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t rsa_cipher::private_decrypt(const void* ciphertext, size_t ciphertext_size, void* plaintext, int padding) const {
|
||||||
|
if (ciphertext_size <= INT_MAX) {
|
||||||
|
int bytes_written =
|
||||||
|
RSA_private_decrypt(static_cast<int>(ciphertext_size), reinterpret_cast<const unsigned char*>(ciphertext), reinterpret_cast<unsigned char*>(plaintext), m_rsa.get(), padding);
|
||||||
|
|
||||||
|
if (bytes_written != -1) {
|
||||||
|
return bytes_written;
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), ERR_get_error(), u8"RSA_public_decrypt failed.")
|
||||||
|
.push_hint(u8"Are your sure you DO provide a correct private key?");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"ciphertext_size > INT_MAX");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef NKG_CURRENT_SOURCE_FILE
|
||||||
|
#undef NKG_CURRENT_SOURCE_LINE
|
||||||
123
common/rsa_cipher.hpp
Normal file
123
common/rsa_cipher.hpp
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#include <openssl/rsa.h>
|
||||||
|
|
||||||
|
#include "resource_wrapper.hpp"
|
||||||
|
#include "resource_traits/openssl/rsa.hpp"
|
||||||
|
|
||||||
|
#include "exception.hpp"
|
||||||
|
|
||||||
|
#define NKG_CURRENT_SOURCE_FILE() u8".\\common\\rsa_cipher.hpp"
|
||||||
|
#define NKG_CURRENT_SOURCE_LINE() __LINE__
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
class rsa_cipher {
|
||||||
|
public:
|
||||||
|
class no_key_assigned_error : public ::nkg::exception {
|
||||||
|
public:
|
||||||
|
no_key_assigned_error(std::string_view file, int line, std::string_view message) noexcept :
|
||||||
|
::nkg::exception(file, line, message) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class backend_error : public ::nkg::exception {
|
||||||
|
public:
|
||||||
|
using error_code_t = decltype(ERR_get_error());
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::optional<error_code_t> m_error_code;
|
||||||
|
std::string m_error_string;
|
||||||
|
|
||||||
|
public:
|
||||||
|
backend_error(std::string_view file, int line, std::string_view message) noexcept :
|
||||||
|
::nkg::exception(file, line, message) {}
|
||||||
|
|
||||||
|
backend_error(std::string_view file, int line, error_code_t openssl_errno, std::string_view message) noexcept :
|
||||||
|
::nkg::exception(file, line, message), m_error_code(openssl_errno) {}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual bool error_code_exists() const noexcept override {
|
||||||
|
return m_error_code.has_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual intptr_t error_code() const noexcept override {
|
||||||
|
if (error_code_exists()) { return m_error_code.value(); } else { trap_then_terminate(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual const std::string& error_string() const noexcept override {
|
||||||
|
if (error_code_exists()) { return m_error_string; } else { trap_then_terminate(); }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
resource_wrapper<resource_traits::openssl::rsa> m_rsa;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static RSA* _read_private_key_from_bio(BIO* p_bio);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static RSA* _read_public_key_pem_from_bio(BIO* p_bio);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static RSA* _read_public_key_pkcs1_from_bio(BIO* p_bio);
|
||||||
|
|
||||||
|
static void _write_private_key_to_bio(RSA* p_rsa, BIO* p_bio);
|
||||||
|
|
||||||
|
static void _write_public_key_pem_to_bio(RSA* p_rsa, BIO* p_bio);
|
||||||
|
|
||||||
|
static void _write_public_key_pkcs1_to_bio(RSA* p_rsa, BIO* p_bio);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
rsa_cipher();
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
size_t bits() const;
|
||||||
|
|
||||||
|
void generate_key(int bits, unsigned int e = RSA_F4);
|
||||||
|
|
||||||
|
void export_private_key_file(std::string_view file_path) const;
|
||||||
|
|
||||||
|
void export_public_key_file_pem(std::string_view file_path) const;
|
||||||
|
|
||||||
|
void export_public_key_file_pkcs1(std::string_view file_path) const;
|
||||||
|
|
||||||
|
void import_private_key_file(std::string_view file_path);
|
||||||
|
|
||||||
|
void import_public_key_file_pem(std::string_view file_path);
|
||||||
|
|
||||||
|
void import_public_key_file_pkcs1(std::string_view file_path);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::string export_private_key_string() const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::string export_public_key_string_pem() const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::string export_public_key_string_pkcs1() const;
|
||||||
|
|
||||||
|
void import_private_key_string(std::string_view key_string);
|
||||||
|
|
||||||
|
void import_public_key_string_pem(std::string_view key_string);
|
||||||
|
|
||||||
|
void import_public_key_string_pkcs1(std::string_view key_string);
|
||||||
|
|
||||||
|
size_t public_encrypt(const void* plaintext, size_t plaintext_size, void* ciphertext, int padding) const;
|
||||||
|
|
||||||
|
size_t private_encrypt(const void* plaintext, size_t plaintext_size, void* ciphertext, int padding) const;
|
||||||
|
|
||||||
|
size_t public_decrypt(const void* ciphertext, size_t ciphertext_size, void* plaintext, int padding) const;
|
||||||
|
|
||||||
|
size_t private_decrypt(const void* ciphertext, size_t ciphertext_size, void* plaintext, int padding) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef NKG_CURRENT_SOURCE_FILE
|
||||||
|
#undef NKG_CURRENT_SOURCE_LINE
|
||||||
132
navicat-keygen/CollectInformation.cpp
Normal file
132
navicat-keygen/CollectInformation.cpp
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include "navicat_serial_generator.hpp"
|
||||||
|
#include "exceptions/operation_canceled_exception.hpp"
|
||||||
|
|
||||||
|
#define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-keygen\\CollectInformation.cpp"
|
||||||
|
#define NKG_CURRENT_SOURCE_LINE() __LINE__
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static int read_int(int min_val, int max_val, std::string_view prompt, std::string_view error_msg) {
|
||||||
|
int val;
|
||||||
|
|
||||||
|
for (std::string s;;) {
|
||||||
|
std::cout << prompt;
|
||||||
|
if (!std::getline(std::cin, s)) {
|
||||||
|
throw exceptions::operation_canceled_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Operation is canceled by user.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
val = std::stoi(s, nullptr, 0);
|
||||||
|
if (min_val <= val && val <= max_val) {
|
||||||
|
return val;
|
||||||
|
} else {
|
||||||
|
throw std::invalid_argument("Out of range.");
|
||||||
|
}
|
||||||
|
} catch (std::invalid_argument&) {
|
||||||
|
std::cout << error_msg << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static int read_int(int min_val, int max_val, int default_val, std::string_view prompt, std::string_view error_msg) {
|
||||||
|
int val;
|
||||||
|
|
||||||
|
for (std::string s;;) {
|
||||||
|
std::cout << prompt;
|
||||||
|
if (!std::getline(std::cin, s)) {
|
||||||
|
throw exceptions::operation_canceled_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Operation is canceled by user.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.empty()) {
|
||||||
|
return default_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val = std::stoi(s, nullptr, 0);
|
||||||
|
if (min_val <= val && val <= max_val) {
|
||||||
|
return val;
|
||||||
|
} else {
|
||||||
|
throw std::invalid_argument("Out of range.");
|
||||||
|
}
|
||||||
|
} catch (std::invalid_argument&) {
|
||||||
|
std::cout << error_msg << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
navicat_serial_generator CollectInformationNormal() {
|
||||||
|
navicat_serial_generator sn_generator;
|
||||||
|
|
||||||
|
std::cout << "[*] Select Navicat product:" << std::endl;
|
||||||
|
std::cout << " 0. DataModeler" << std::endl;
|
||||||
|
std::cout << " 1. Premium" << std::endl;
|
||||||
|
std::cout << " 2. MySQL" << std::endl;
|
||||||
|
std::cout << " 3. PostgreSQL" << std::endl;
|
||||||
|
std::cout << " 4. Oracle" << std::endl;
|
||||||
|
std::cout << " 5. SQLServer" << std::endl;
|
||||||
|
std::cout << " 6. SQLite" << std::endl;
|
||||||
|
std::cout << " 7. MariaDB" << std::endl;
|
||||||
|
std::cout << " 8. MongoDB" << std::endl;
|
||||||
|
std::cout << " 9. ReportViewer" << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
sn_generator.set_software_type(static_cast<navicat_software_type>(read_int(0, 9, "(Input index)> ", "Invalid index.")));
|
||||||
|
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << "[*] Select product language:" << std::endl;
|
||||||
|
std::cout << " 0. English" << std::endl;
|
||||||
|
std::cout << " 1. Simplified Chinese" << std::endl;
|
||||||
|
std::cout << " 2. Traditional Chinese" << std::endl;
|
||||||
|
std::cout << " 3. Japanese" << std::endl;
|
||||||
|
std::cout << " 4. Polish" << std::endl;
|
||||||
|
std::cout << " 5. Spanish" << std::endl;
|
||||||
|
std::cout << " 6. French" << std::endl;
|
||||||
|
std::cout << " 7. German" << std::endl;
|
||||||
|
std::cout << " 8. Korean" << std::endl;
|
||||||
|
std::cout << " 9. Russian" << std::endl;
|
||||||
|
std::cout << " 10. Portuguese" << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
sn_generator.set_software_language(static_cast<navicat_software_language>(read_int(0, 10, "(Input index)> ", "Invalid index.")));
|
||||||
|
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << "[*] Input major version number:" << std::endl;
|
||||||
|
sn_generator.set_software_version(read_int(11, 16, 16, "(range: 11 ~ 16, default: 16)> ", "Invalid number."));
|
||||||
|
|
||||||
|
std::cout << std::endl;
|
||||||
|
return sn_generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
navicat_serial_generator CollectInformationAdvanced() {
|
||||||
|
navicat_serial_generator sn_generator;
|
||||||
|
|
||||||
|
std::cout << "[*] Navicat Product Signature:" << std::endl;
|
||||||
|
sn_generator.set_software_type(static_cast<uint8_t>(read_int(0x00, 0xff, "(range: 0x00 ~ 0xFF)> ", "Invalid number.")));
|
||||||
|
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << "[*] Navicat Language Signature 0:" << std::endl;
|
||||||
|
auto s1 = static_cast<uint8_t>(read_int(0x00, 0xff, "(range: 0x00 ~ 0xFF)> ", "Invalid number."));
|
||||||
|
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << "[*] Navicat Language Signature 1:" << std::endl;
|
||||||
|
auto s2 = static_cast<uint8_t>(read_int(0x00, 0xff, "(range: 0x00 ~ 0xFF)> ", "Invalid number."));
|
||||||
|
|
||||||
|
sn_generator.set_software_language(s1, s2);
|
||||||
|
|
||||||
|
std::cout << std::endl;
|
||||||
|
std::cout << "[*] Input major version number:" << std::endl;
|
||||||
|
sn_generator.set_software_version(read_int(0, 15, 12, "(range: 0 ~ 15, default: 12)> ", "Invalid number."));
|
||||||
|
|
||||||
|
std::cout << std::endl;
|
||||||
|
return sn_generator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef NKG_CURRENT_SOURCE_FILE
|
||||||
|
#undef NKG_CURRENT_SOURCE_LINE
|
||||||
211
navicat-keygen/GenerateLicense.cpp
Normal file
211
navicat-keygen/GenerateLicense.cpp
Normal file
|
|
@ -0,0 +1,211 @@
|
||||||
|
#include <iostream>
|
||||||
|
#include <ctime>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "exceptions/operation_canceled_exception.hpp"
|
||||||
|
#include "exceptions/unix_exception.hpp"
|
||||||
|
|
||||||
|
#include "resource_wrapper.hpp"
|
||||||
|
#include "resource_traits/unix_os/file_descriptor.hpp"
|
||||||
|
|
||||||
|
#include "rsa_cipher.hpp"
|
||||||
|
#include "navicat_serial_generator.hpp"
|
||||||
|
#include "base64_rfc4648.hpp"
|
||||||
|
|
||||||
|
#include <rapidjson/document.h>
|
||||||
|
#include <rapidjson/writer.h>
|
||||||
|
#include <rapidjson/stringbuffer.h>
|
||||||
|
|
||||||
|
#define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-keygen\\GenerateLicense.cpp"
|
||||||
|
#define NKG_CURRENT_SOURCE_LINE() __LINE__
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
void GenerateLicenseText(const rsa_cipher& cipher, const navicat_serial_generator& sn_generator) {
|
||||||
|
std::string u8_username;
|
||||||
|
std::string u8_organization;
|
||||||
|
|
||||||
|
std::string b64_request_code;
|
||||||
|
std::vector<uint8_t> request_code;
|
||||||
|
std::string u8_request_info;
|
||||||
|
std::string u8_response_info;
|
||||||
|
std::vector<uint8_t> response_code;
|
||||||
|
std::string b64_response_code;
|
||||||
|
|
||||||
|
std::cout << "[*] Your name: ";
|
||||||
|
if (!std::getline(std::cin, u8_username)) {
|
||||||
|
throw exceptions::operation_canceled_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Operation is canceled by user.");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "[*] Your organization: ";
|
||||||
|
if (!std::getline(std::cin, u8_organization)) {
|
||||||
|
throw exceptions::operation_canceled_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Operation is canceled by user.");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
std::cout << "[*] Input request code in Base64: (Double press ENTER to end)" << std::endl;
|
||||||
|
while (true) {
|
||||||
|
std::string temp;
|
||||||
|
if (!std::getline(std::cin, temp)) {
|
||||||
|
throw exceptions::operation_canceled_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Operation is canceled by user.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (temp.empty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
b64_request_code.append(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
request_code = base64_rfc4648::decode(b64_request_code);
|
||||||
|
if (request_code.size() != 256) {
|
||||||
|
throw ::nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Broken request code. %zu", request_code.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
u8_request_info.resize((cipher.bits() + 7) / 8);
|
||||||
|
u8_request_info.resize(cipher.private_decrypt(request_code.data(), request_code.size(), u8_request_info.data(), RSA_PKCS1_PADDING));
|
||||||
|
|
||||||
|
std::cout << "[*] Request Info:" << std::endl;
|
||||||
|
std::cout << u8_request_info << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
rapidjson::Document json;
|
||||||
|
rapidjson::Value N_Key;
|
||||||
|
rapidjson::Value N_Value;
|
||||||
|
rapidjson::Value O_Key;
|
||||||
|
rapidjson::Value O_Value;
|
||||||
|
rapidjson::Value T_Key;
|
||||||
|
rapidjson::Value T_Value;
|
||||||
|
rapidjson::StringBuffer buffer;
|
||||||
|
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Begin to parse
|
||||||
|
//
|
||||||
|
json.Parse(u8_request_info.c_str());
|
||||||
|
//
|
||||||
|
// Remove "Platform" info
|
||||||
|
//
|
||||||
|
json.RemoveMember("P");
|
||||||
|
//
|
||||||
|
// Set "Name" info
|
||||||
|
//
|
||||||
|
N_Key.SetString("N", 1);
|
||||||
|
N_Value.SetString(u8_username.c_str(), static_cast<rapidjson::SizeType>(u8_username.length()));
|
||||||
|
//
|
||||||
|
// Set "Organization" info
|
||||||
|
//
|
||||||
|
O_Key.SetString("O", 1);
|
||||||
|
O_Value.SetString(u8_organization.c_str(), static_cast<rapidjson::SizeType>(u8_organization.length()));
|
||||||
|
//
|
||||||
|
// Set "Time" info
|
||||||
|
//
|
||||||
|
T_Key.SetString("T", 1);
|
||||||
|
T_Value.SetUint(static_cast<unsigned int>(std::time(nullptr)));
|
||||||
|
//
|
||||||
|
// Add "Name", "Organization" and "Time"
|
||||||
|
//
|
||||||
|
json.AddMember(N_Key, N_Value, json.GetAllocator());
|
||||||
|
json.AddMember(O_Key, O_Value, json.GetAllocator());
|
||||||
|
json.AddMember(T_Key, T_Value, json.GetAllocator());
|
||||||
|
|
||||||
|
json.Accept(writer);
|
||||||
|
if (buffer.GetSize() > 240) {
|
||||||
|
throw ::nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Response Info is too long.");
|
||||||
|
}
|
||||||
|
|
||||||
|
u8_response_info.assign(buffer.GetString(), buffer.GetSize());
|
||||||
|
|
||||||
|
std::cout << "[*] Response Info:" << std::endl;
|
||||||
|
std::cout << u8_response_info << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
response_code.resize((cipher.bits() + 7) / 8);
|
||||||
|
response_code.resize(cipher.private_encrypt(u8_response_info.data(), u8_response_info.size(), response_code.data(), RSA_PKCS1_PADDING));
|
||||||
|
|
||||||
|
b64_response_code = base64_rfc4648::encode(response_code);
|
||||||
|
|
||||||
|
std::cout << "[*] Activation Code:" << std::endl;
|
||||||
|
std::cout << b64_response_code << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GenerateLicenseBinary(const rsa_cipher& cipher, const navicat_serial_generator& sn_generator) {
|
||||||
|
std::string u8_serial_number = sn_generator.serial_number();
|
||||||
|
|
||||||
|
std::string u8_username;
|
||||||
|
std::string u8_organization;
|
||||||
|
|
||||||
|
std::string u8_response_info;
|
||||||
|
std::vector<uint8_t> response_code;
|
||||||
|
|
||||||
|
std::cout << "[*] Your name: ";
|
||||||
|
if (!std::getline(std::cin, u8_username)) {
|
||||||
|
throw exceptions::operation_canceled_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Operation is canceled by user.");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "[*] Your organization: ";
|
||||||
|
if (!std::getline(std::cin, u8_organization)) {
|
||||||
|
throw exceptions::operation_canceled_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Operation is canceled by user.");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
rapidjson::Document json;
|
||||||
|
rapidjson::Value N_Key;
|
||||||
|
rapidjson::Value N_Value;
|
||||||
|
rapidjson::Value O_Key;
|
||||||
|
rapidjson::Value O_Value;
|
||||||
|
rapidjson::Value T_Key;
|
||||||
|
rapidjson::Value T_Value;
|
||||||
|
rapidjson::Value K_Key;
|
||||||
|
rapidjson::Value K_Value;
|
||||||
|
rapidjson::StringBuffer buffer;
|
||||||
|
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||||
|
|
||||||
|
json.Parse("{}");
|
||||||
|
K_Key.SetString("K", 1);
|
||||||
|
K_Value.SetString(u8_serial_number.c_str(), static_cast<rapidjson::SizeType>(u8_serial_number.length()));
|
||||||
|
N_Key.SetString("N", 1);
|
||||||
|
N_Value.SetString(u8_username.c_str(), static_cast<rapidjson::SizeType>(u8_username.length()));
|
||||||
|
O_Key.SetString("O", 1);
|
||||||
|
O_Value.SetString(u8_organization.c_str(), static_cast<rapidjson::SizeType>(u8_organization.length()));
|
||||||
|
T_Key.SetString("T", 1);
|
||||||
|
T_Value.SetUint(static_cast<unsigned int>(std::time(nullptr)));
|
||||||
|
|
||||||
|
json.AddMember(K_Key, K_Value, json.GetAllocator());
|
||||||
|
json.AddMember(N_Key, N_Value, json.GetAllocator());
|
||||||
|
json.AddMember(O_Key, O_Value, json.GetAllocator());
|
||||||
|
json.AddMember(T_Key, T_Value, json.GetAllocator());
|
||||||
|
|
||||||
|
json.Accept(writer);
|
||||||
|
if (buffer.GetSize() > 240) {
|
||||||
|
throw ::nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Response Info is too long.");
|
||||||
|
}
|
||||||
|
|
||||||
|
u8_response_info.assign(buffer.GetString(), buffer.GetSize());
|
||||||
|
|
||||||
|
std::cout << "[*] Response Info:" << std::endl;
|
||||||
|
std::cout << u8_response_info << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
response_code.resize((cipher.bits() + 7) / 8);
|
||||||
|
response_code.resize(cipher.private_encrypt(u8_response_info.data(), u8_response_info.size(), response_code.data(), RSA_PKCS1_PADDING));
|
||||||
|
|
||||||
|
resource_wrapper license_file{ resource_traits::unix_os::file_descriptor{}, open("license_file", O_WRONLY | O_CREAT) };
|
||||||
|
if (!license_file.is_valid()) {
|
||||||
|
throw exceptions::unix_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), errno, u8"open failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write(license_file.get(), response_code.data(), response_code.size()) < 0) {
|
||||||
|
throw exceptions::unix_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), errno, u8"write failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "[+] license_file has been generated." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef NKG_CURRENT_SOURCE_FILE
|
||||||
|
#undef NKG_CURRENT_SOURCE_LINE
|
||||||
122
navicat-keygen/base32_rfc4648.cpp
Normal file
122
navicat-keygen/base32_rfc4648.cpp
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
#include "base32_rfc4648.hpp"
|
||||||
|
#include <limits>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-keygen\\base32_rfc4648.cpp"
|
||||||
|
#define NKG_CURRENT_SOURCE_LINE() __LINE__
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
char base32_rfc4648::symbol(alphabet_index_t idx) {
|
||||||
|
return alphabet[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
base32_rfc4648::alphabet_index_t base32_rfc4648::reverse_symbol(char c) {
|
||||||
|
if ('A' <= c && c <= 'Z') {
|
||||||
|
return c - 'A';
|
||||||
|
} else if ('2' <= c && c <= '7') {
|
||||||
|
return c - '2' + 26;
|
||||||
|
} else {
|
||||||
|
throw decoding_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Non-base32 digit is found.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string base32_rfc4648::encode(const std::vector<uint8_t>& data) {
|
||||||
|
return encode(data.data(), data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string base32_rfc4648::encode(const void* data_ptr, size_t data_size) {
|
||||||
|
std::string retval;
|
||||||
|
|
||||||
|
if (data_size) {
|
||||||
|
retval.reserve((data_size * 8 + 4) / 5);
|
||||||
|
|
||||||
|
auto p = reinterpret_cast<const uint8_t*>(data_ptr);
|
||||||
|
alphabet_index_t left_bits = 0;
|
||||||
|
alphabet_index_t bit_buffer = 0;
|
||||||
|
for (size_t i = 0; i < data_size; ++i) {
|
||||||
|
bit_buffer = (bit_buffer << 8) | p[i];
|
||||||
|
left_bits += 8;
|
||||||
|
|
||||||
|
while (left_bits >= 5) {
|
||||||
|
alphabet_index_t idx = (bit_buffer >> (left_bits - 5)) & 0x1f;
|
||||||
|
retval.push_back(symbol(idx));
|
||||||
|
left_bits -= 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (left_bits > 0) {
|
||||||
|
alphabet_index_t idx = (bit_buffer << (5 - left_bits)) & 0x1f;
|
||||||
|
retval.push_back(symbol(idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (data_size % 5) {
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
retval.append(6, padding_character);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
retval.append(4, padding_character);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
retval.append(3, padding_character);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
retval.append(1, padding_character);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> base32_rfc4648::decode(std::string_view b32_string) {
|
||||||
|
if (b32_string.length() % 8 == 0) {
|
||||||
|
std::vector<uint8_t> retval;
|
||||||
|
|
||||||
|
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; }));
|
||||||
|
switch (count_of_padding) {
|
||||||
|
case 1:
|
||||||
|
retval.reserve(b32_string.length() / 8 * 5 - (5 - 4));
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
retval.reserve(b32_string.length() / 8 * 5 - (5 - 3));
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
retval.reserve(b32_string.length() / 8 * 5 - (5 - 2));
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
retval.reserve(b32_string.length() / 8 * 5 - (5 - 1));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw decoding_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Incorrect padding");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t count_of_encoded = b32_string.length() - count_of_padding;
|
||||||
|
|
||||||
|
alphabet_index_t left_bits = 0;
|
||||||
|
alphabet_index_t bit_buffer = 0;
|
||||||
|
for (size_t i = 0; i < count_of_encoded; ++i) {
|
||||||
|
bit_buffer = (bit_buffer << 5) | reverse_symbol(b32_string[i]);
|
||||||
|
left_bits += 5;
|
||||||
|
|
||||||
|
while (left_bits >= 8) {
|
||||||
|
auto val = static_cast<uint8_t>((bit_buffer >> (left_bits - 8)) & 0xff);
|
||||||
|
retval.push_back(val);
|
||||||
|
left_bits -= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
} else {
|
||||||
|
throw decoding_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Incorrect padding");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef NKG_CURRENT_SOURCE_LINE
|
||||||
|
#undef NKG_CURRENT_SOURCE_FILE
|
||||||
31
navicat-keygen/base32_rfc4648.hpp
Normal file
31
navicat-keygen/base32_rfc4648.hpp
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "exception.hpp"
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
struct base32_rfc4648 {
|
||||||
|
using alphabet_index_t = size_t;
|
||||||
|
|
||||||
|
static constexpr const char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||||
|
static constexpr const char padding_character = '=';
|
||||||
|
|
||||||
|
class decoding_error : public ::nkg::exception {
|
||||||
|
public:
|
||||||
|
decoding_error(std::string_view file, int line, std::string_view message) noexcept :
|
||||||
|
::nkg::exception(file, line, message) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
static char symbol(alphabet_index_t idx);
|
||||||
|
|
||||||
|
static alphabet_index_t reverse_symbol(char c);
|
||||||
|
|
||||||
|
static std::string encode(const std::vector<uint8_t>& data);
|
||||||
|
|
||||||
|
static std::string encode(const void* data_ptr, size_t data_size);
|
||||||
|
|
||||||
|
static std::vector<uint8_t> decode(std::string_view b32_string);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
99
navicat-keygen/base64_rfc4648.cpp
Normal file
99
navicat-keygen/base64_rfc4648.cpp
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
#include "base64_rfc4648.hpp"
|
||||||
|
|
||||||
|
#include <openssl/bio.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
|
||||||
|
#include "resource_wrapper.hpp"
|
||||||
|
#include "resource_traits/openssl/bio.hpp"
|
||||||
|
#include "resource_traits/openssl/bio_chain.hpp"
|
||||||
|
|
||||||
|
#define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-keygen\\base64_rfc4648.cpp"
|
||||||
|
#define NKG_CURRENT_SOURCE_LINE() __LINE__
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
std::string base64_rfc4648::encode(const std::vector<std::uint8_t>& data) {
|
||||||
|
resource_wrapper bio_b64{ resource_traits::openssl::bio_chain{}, BIO_new(BIO_f_base64()) };
|
||||||
|
if (!bio_b64.is_valid()) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
BIO_set_flags(bio_b64.get(), BIO_FLAGS_BASE64_NO_NL);
|
||||||
|
|
||||||
|
resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) };
|
||||||
|
if (!bio_memory.is_valid()) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
BIO_push(bio_b64.get(), bio_memory.get());
|
||||||
|
|
||||||
|
for (size_t written_size = 0, left_size = data.size(); left_size != 0;) {
|
||||||
|
int size_to_write = static_cast<int>(std::min(left_size, static_cast<size_t>(INT_MAX)));
|
||||||
|
|
||||||
|
int r = BIO_write(bio_b64.get(), data.data() + written_size, size_to_write);
|
||||||
|
if (r > 0) {
|
||||||
|
written_size += r;
|
||||||
|
left_size -= r;
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_write failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BIO_flush(bio_b64.get());
|
||||||
|
|
||||||
|
const char* pch = nullptr;
|
||||||
|
long lch = BIO_get_mem_data(bio_memory.get(), &pch);
|
||||||
|
|
||||||
|
bio_memory.discard(); // the bio_chain `bio_b64` will free it
|
||||||
|
|
||||||
|
return std::string(pch, lch);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> base64_rfc4648::decode(std::string_view b64_string) {
|
||||||
|
resource_wrapper bio_b64{ resource_traits::openssl::bio_chain{}, BIO_new(BIO_f_base64()) };
|
||||||
|
if (!bio_b64.is_valid()) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
BIO_set_flags(bio_b64.get(), BIO_FLAGS_BASE64_NO_NL);
|
||||||
|
|
||||||
|
resource_wrapper bio_memory{ resource_traits::openssl::bio{}, BIO_new(BIO_s_mem()) };
|
||||||
|
if (!bio_memory.is_valid()) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_new failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
BIO_push(bio_b64.get(), bio_memory.get());
|
||||||
|
|
||||||
|
for (size_t written_length = 0, left_length = b64_string.length(); left_length != 0;) {
|
||||||
|
int length_to_write = static_cast<int>(std::min(left_length, static_cast<size_t>(INT_MAX)));
|
||||||
|
|
||||||
|
int r = BIO_write(bio_memory.get(), b64_string.data() + written_length, length_to_write);
|
||||||
|
if (r > 0) {
|
||||||
|
written_length += r;
|
||||||
|
left_length -= r;
|
||||||
|
} else {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"BIO_write failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> retval;
|
||||||
|
retval.reserve(b64_string.length() * 3 / 4 + 1);
|
||||||
|
|
||||||
|
for (uint8_t buf[256];;) {
|
||||||
|
auto len = BIO_read(bio_b64.get(), buf, sizeof(buf));
|
||||||
|
if (len > 0) {
|
||||||
|
retval.insert(retval.end(), buf, buf + len);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bio_memory.discard(); // the bio_chain `bio_b64` will free it
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef NKG_CURRENT_SOURCE_FILE
|
||||||
|
#undef NKG_CURRENT_SOURCE_LINE
|
||||||
20
navicat-keygen/base64_rfc4648.hpp
Normal file
20
navicat-keygen/base64_rfc4648.hpp
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include "exception.hpp"
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
struct base64_rfc4648 {
|
||||||
|
class backend_error : public ::nkg::exception {
|
||||||
|
public:
|
||||||
|
backend_error(std::string_view file, int line, std::string_view message) noexcept :
|
||||||
|
::nkg::exception(file, line, message) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::string encode(const std::vector<std::uint8_t>& data);
|
||||||
|
|
||||||
|
static std::vector<uint8_t> decode(std::string_view str_b64);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
123
navicat-keygen/main.cpp
Normal file
123
navicat-keygen/main.cpp
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include "exception.hpp"
|
||||||
|
#include "exceptions/operation_canceled_exception.hpp"
|
||||||
|
|
||||||
|
#include "base64_rfc4648.hpp"
|
||||||
|
#include "navicat_serial_generator.hpp"
|
||||||
|
#include "rsa_cipher.hpp"
|
||||||
|
|
||||||
|
#define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-keygen\\wmain.cpp"
|
||||||
|
#define NKG_CURRENT_SOURCE_LINE() __LINE__
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
using fnCollectInformation = std::function<navicat_serial_generator()>;
|
||||||
|
using fnGenerateLicense = std::function<void(const rsa_cipher& cipher, const navicat_serial_generator& generator)>;
|
||||||
|
|
||||||
|
navicat_serial_generator CollectInformationNormal();
|
||||||
|
navicat_serial_generator CollectInformationAdvanced();
|
||||||
|
void GenerateLicenseText(const rsa_cipher& cipher, const navicat_serial_generator& sn_generator);
|
||||||
|
void GenerateLicenseBinary(const rsa_cipher& cipher, const navicat_serial_generator& sn_generator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void welcome() {
|
||||||
|
puts("***************************************************");
|
||||||
|
puts("* navicat-keygen by @DoubleLabyrinth *");
|
||||||
|
puts("* version: 16.0.7.0 *");
|
||||||
|
puts("***************************************************");
|
||||||
|
puts("");
|
||||||
|
}
|
||||||
|
|
||||||
|
void help() {
|
||||||
|
puts("Usage:");
|
||||||
|
puts(" navicat-keygen.exe <-bin|-text> [-adv] <RSA-2048 Private Key File>");
|
||||||
|
puts("");
|
||||||
|
puts(" <-bin|-text> Specify \"-bin\" to generate \"license_file\" used by Navicat 11.");
|
||||||
|
puts(" Specify \"-text\" to generate base64-encoded activation code.");
|
||||||
|
puts(" This parameter must be specified.");
|
||||||
|
puts("");
|
||||||
|
puts(" [-adv] Enable advance mode.");
|
||||||
|
puts(" This parameter is optional.");
|
||||||
|
puts("");
|
||||||
|
puts(" <RSA-2048 Private Key File> A path to an RSA-2048 private key file.");
|
||||||
|
puts(" This parameter must be specified.");
|
||||||
|
puts("");
|
||||||
|
puts("Example:");
|
||||||
|
puts(" navicat-keygen.exe -text .\\RegPrivateKey.pem");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
welcome();
|
||||||
|
|
||||||
|
if (argc == 3 || argc == 4) {
|
||||||
|
nkg::fnCollectInformation lpfnCollectInformation;
|
||||||
|
nkg::fnGenerateLicense lpfnGenerateLicense;
|
||||||
|
|
||||||
|
if (strcmp(argv[1], "-bin") == 0) {
|
||||||
|
lpfnGenerateLicense = nkg::GenerateLicenseBinary;
|
||||||
|
} else if (strcmp(argv[1], "-text") == 0) {
|
||||||
|
lpfnGenerateLicense = nkg::GenerateLicenseText;
|
||||||
|
} else {
|
||||||
|
help();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc == 4) {
|
||||||
|
if (strcmp(argv[2], "-adv") == 0) {
|
||||||
|
lpfnCollectInformation = nkg::CollectInformationAdvanced;
|
||||||
|
} else {
|
||||||
|
help();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lpfnCollectInformation = nkg::CollectInformationNormal;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
nkg::rsa_cipher cipher;
|
||||||
|
|
||||||
|
cipher.import_private_key_file(argv[argc - 1]);
|
||||||
|
if (cipher.bits() != 2048) {
|
||||||
|
throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"RSA key length mismatches.")
|
||||||
|
.push_hint(u8"You must provide an RSA key whose modulus length is 2048 bits.");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sn_generator = lpfnCollectInformation();
|
||||||
|
sn_generator.generate();
|
||||||
|
|
||||||
|
puts("[*] Serial number:");
|
||||||
|
puts(sn_generator.serial_number_formatted().c_str());
|
||||||
|
puts("");
|
||||||
|
|
||||||
|
lpfnGenerateLicense(cipher, sn_generator);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} catch (nkg::exceptions::operation_canceled_exception&) {
|
||||||
|
return -1;
|
||||||
|
} catch (nkg::exception& e) {
|
||||||
|
printf("[-] %s:%d ->\n", e.source_file().c_str(), e.source_line());
|
||||||
|
printf(" %s\n", e.custom_message().c_str());
|
||||||
|
|
||||||
|
if (e.error_code_exists()) {
|
||||||
|
printf(" %s (0x%zx)\n", e.error_string().c_str(), e.error_code());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& hint : e.hints()) {
|
||||||
|
printf(" Hints: %s\n", hint.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
printf("[-] %s\n", e.what());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
help();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef NKG_CURRENT_SOURCE_FILE
|
||||||
|
#undef NKG_CURRENT_SOURCE_LINE
|
||||||
160
navicat-keygen/navicat_serial_generator.cpp
Normal file
160
navicat-keygen/navicat_serial_generator.cpp
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
#include "navicat_serial_generator.hpp"
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include "base32_rfc4648.hpp"
|
||||||
|
|
||||||
|
#define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-keygen\\navicat_serial_generator.cpp"
|
||||||
|
#define NKG_CURRENT_SOURCE_LINE() __LINE__
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
char navicat_serial_generator::_replace_confusing_chars(char c) {
|
||||||
|
if (c == 'I') {
|
||||||
|
return '8';
|
||||||
|
} else if (c == 'O') {
|
||||||
|
return '9';
|
||||||
|
} else {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
navicat_serial_generator::navicat_serial_generator() noexcept :
|
||||||
|
m_data{ 0x68 , 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32 }, m_des_key{} {}
|
||||||
|
|
||||||
|
void navicat_serial_generator::set_software_language(navicat_software_language lang) noexcept {
|
||||||
|
switch (lang) {
|
||||||
|
case navicat_software_language::English:
|
||||||
|
m_data[5] = 0xAC; // Must be 0xAC for English version.
|
||||||
|
m_data[6] = 0x88; // Must be 0x88 for English version.
|
||||||
|
break;
|
||||||
|
case navicat_software_language::SimplifiedChinese:
|
||||||
|
m_data[5] = 0xCE; // Must be 0xCE for Simplified Chinese version.
|
||||||
|
m_data[6] = 0x32; // Must be 0x32 for Simplified Chinese version.
|
||||||
|
break;
|
||||||
|
case navicat_software_language::TraditionalChinese:
|
||||||
|
m_data[5] = 0xAA; // Must be 0xAA for Traditional Chinese version.
|
||||||
|
m_data[6] = 0x99; // Must be 0x99 for Traditional Chinese version.
|
||||||
|
break;
|
||||||
|
case navicat_software_language::Japanese:
|
||||||
|
m_data[5] = 0xAD; // Must be 0xAD for Japanese version. Discoverer: @dragonflylee
|
||||||
|
m_data[6] = 0x82; // Must be 0x82 for Japanese version. Discoverer: @dragonflylee
|
||||||
|
break;
|
||||||
|
case navicat_software_language::Polish:
|
||||||
|
m_data[5] = 0xBB; // Must be 0xBB for Polish version. Discoverer: @dragonflylee
|
||||||
|
m_data[6] = 0x55; // Must be 0x55 for Polish version. Discoverer: @dragonflylee
|
||||||
|
break;
|
||||||
|
case navicat_software_language::Spanish:
|
||||||
|
m_data[5] = 0xAE; // Must be 0xAE for Spanish version. Discoverer: @dragonflylee
|
||||||
|
m_data[6] = 0x10; // Must be 0x10 for Spanish version. Discoverer: @dragonflylee
|
||||||
|
break;
|
||||||
|
case navicat_software_language::French:
|
||||||
|
m_data[5] = 0xFA; // Must be 0xFA for French version. Discoverer: @Deltafox79
|
||||||
|
m_data[6] = 0x20; // Must be 0x20 for French version. Discoverer: @Deltafox79
|
||||||
|
break;
|
||||||
|
case navicat_software_language::German:
|
||||||
|
m_data[5] = 0xB1; // Must be 0xB1 for German version. Discoverer: @dragonflylee
|
||||||
|
m_data[6] = 0x60; // Must be 0x60 for German version. Discoverer: @dragonflylee
|
||||||
|
break;
|
||||||
|
case navicat_software_language::Korean:
|
||||||
|
m_data[5] = 0xB5; // Must be 0xB5 for Korean version. Discoverer: @dragonflylee
|
||||||
|
m_data[6] = 0x60; // Must be 0x60 for Korean version. Discoverer: @dragonflylee
|
||||||
|
break;
|
||||||
|
case navicat_software_language::Russian:
|
||||||
|
m_data[5] = 0xEE; // Must be 0xB5 for Russian version. Discoverer: @dragonflylee
|
||||||
|
m_data[6] = 0x16; // Must be 0x60 for Russian version. Discoverer: @dragonflylee
|
||||||
|
break;
|
||||||
|
case navicat_software_language::Portuguese:
|
||||||
|
m_data[5] = 0xCD; // Must be 0xCD for Portuguese version. Discoverer: @dragonflylee
|
||||||
|
m_data[6] = 0x49; // Must be 0x49 for Portuguese version. Discoverer: @dragonflylee
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void navicat_serial_generator::set_software_language(uint8_t lang_sig0, uint8_t lang_sig1) noexcept {
|
||||||
|
m_data[5] = lang_sig0;
|
||||||
|
m_data[6] = lang_sig1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void navicat_serial_generator::set_software_type(navicat_software_type software_type) noexcept {
|
||||||
|
switch (software_type) {
|
||||||
|
case navicat_software_type::DataModeler:
|
||||||
|
m_data[7] = 0x84;
|
||||||
|
break;
|
||||||
|
case navicat_software_type::Premium:
|
||||||
|
m_data[7] = 0x65;
|
||||||
|
break;
|
||||||
|
case navicat_software_type::MySQL:
|
||||||
|
m_data[7] = 0x68;
|
||||||
|
break;
|
||||||
|
case navicat_software_type::PostgreSQL:
|
||||||
|
m_data[7] = 0x6C;
|
||||||
|
break;
|
||||||
|
case navicat_software_type::Oracle:
|
||||||
|
m_data[7] = 0x70;
|
||||||
|
break;
|
||||||
|
case navicat_software_type::SQLServer:
|
||||||
|
m_data[7] = 0x74;
|
||||||
|
break;
|
||||||
|
case navicat_software_type::SQLite:
|
||||||
|
m_data[7] = 0x78;
|
||||||
|
break;
|
||||||
|
case navicat_software_type::MariaDB:
|
||||||
|
m_data[7] = 0x7C;
|
||||||
|
break;
|
||||||
|
case navicat_software_type::MongoDB:
|
||||||
|
m_data[7] = 0x80;
|
||||||
|
break;
|
||||||
|
case navicat_software_type::ReportViewer:
|
||||||
|
m_data[7] = 0xb;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void navicat_serial_generator::set_software_type(uint8_t software_type_sig) noexcept {
|
||||||
|
m_data[7] = software_type_sig;
|
||||||
|
}
|
||||||
|
|
||||||
|
void navicat_serial_generator::set_software_version(int ver) {
|
||||||
|
if (11 <= ver && ver < 16) {
|
||||||
|
m_data[8] = static_cast<std::uint8_t>((ver << 4) | (m_data[8] & 0x0f));
|
||||||
|
memcpy(m_des_key, s_des_key0, sizeof(s_des_key0));
|
||||||
|
} else if (16 <= ver && ver < 32) {
|
||||||
|
m_data[8] = static_cast<std::uint8_t>(((ver - 16) << 4) | (m_data[8] & 0x0f));
|
||||||
|
memcpy(m_des_key, s_des_key1, sizeof(s_des_key1));
|
||||||
|
} else {
|
||||||
|
throw version_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Invalid navicat version.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void navicat_serial_generator::generate() {
|
||||||
|
RAND_bytes(m_data + 2, 3);
|
||||||
|
|
||||||
|
DES_key_schedule schedule;
|
||||||
|
DES_set_key_unchecked(&m_des_key, &schedule);
|
||||||
|
DES_ecb_encrypt(reinterpret_cast<const_DES_cblock*>(m_data + 2), reinterpret_cast<const_DES_cblock*>(m_data + 2), &schedule, DES_ENCRYPT);
|
||||||
|
|
||||||
|
m_serial_number = base32_rfc4648::encode(m_data, sizeof(m_data));
|
||||||
|
std::transform(m_serial_number.begin(), m_serial_number.end(), m_serial_number.begin(), _replace_confusing_chars);
|
||||||
|
|
||||||
|
std::string_view sn = m_serial_number;
|
||||||
|
m_serial_number_formatted = fmt::format("{}-{}-{}-{}", sn.substr(0, 4), sn.substr(4, 4), sn.substr(8, 4), sn.substr(12, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
const std::string& navicat_serial_generator::serial_number() const noexcept {
|
||||||
|
return m_serial_number;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
const std::string& navicat_serial_generator::serial_number_formatted() const noexcept {
|
||||||
|
return m_serial_number_formatted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef NKG_CURRENT_SOURCE_LINE
|
||||||
|
#undef NKG_CURRENT_SOURCE_FILE
|
||||||
75
navicat-keygen/navicat_serial_generator.hpp
Normal file
75
navicat-keygen/navicat_serial_generator.hpp
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <openssl/des.h>
|
||||||
|
#include "exception.hpp"
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
enum class navicat_software_language {
|
||||||
|
English,
|
||||||
|
SimplifiedChinese,
|
||||||
|
TraditionalChinese,
|
||||||
|
Japanese,
|
||||||
|
Polish,
|
||||||
|
Spanish,
|
||||||
|
French,
|
||||||
|
German,
|
||||||
|
Korean,
|
||||||
|
Russian,
|
||||||
|
Portuguese
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class navicat_software_type {
|
||||||
|
DataModeler,
|
||||||
|
Premium,
|
||||||
|
MySQL,
|
||||||
|
PostgreSQL,
|
||||||
|
Oracle,
|
||||||
|
SQLServer,
|
||||||
|
SQLite,
|
||||||
|
MariaDB,
|
||||||
|
MongoDB,
|
||||||
|
ReportViewer
|
||||||
|
};
|
||||||
|
|
||||||
|
class navicat_serial_generator {
|
||||||
|
public:
|
||||||
|
class version_error : public ::nkg::exception {
|
||||||
|
public:
|
||||||
|
version_error(std::string_view file, int line, std::string_view message) noexcept :
|
||||||
|
::nkg::exception(file, line, message) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
static inline const DES_cblock s_des_key0 = { 0x64, 0xAD, 0xF3, 0x2F, 0xAE, 0xF2, 0x1A, 0x27 };
|
||||||
|
static inline const DES_cblock s_des_key1 = { 0xE9, 0x7F, 0xB0, 0x60, 0x77, 0x45, 0x90, 0xAE };
|
||||||
|
|
||||||
|
uint8_t m_data[10];
|
||||||
|
DES_cblock m_des_key;
|
||||||
|
std::string m_serial_number;
|
||||||
|
std::string m_serial_number_formatted;
|
||||||
|
|
||||||
|
static char _replace_confusing_chars(char c);
|
||||||
|
|
||||||
|
public:
|
||||||
|
navicat_serial_generator() noexcept;
|
||||||
|
|
||||||
|
void set_software_language(navicat_software_language lang) noexcept;
|
||||||
|
void set_software_language(uint8_t lang_sig0, uint8_t lang_sig1) noexcept;
|
||||||
|
|
||||||
|
void set_software_type(navicat_software_type software_type) noexcept;
|
||||||
|
void set_software_type(uint8_t software_type_sig) noexcept;
|
||||||
|
|
||||||
|
void set_software_version(int Version);
|
||||||
|
|
||||||
|
void generate();
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
const std::string& serial_number() const noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
const std::string& serial_number_formatted() const noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
199
navicat-patcher/amd64_emulator.cpp
Normal file
199
navicat-patcher/amd64_emulator.cpp
Normal file
|
|
@ -0,0 +1,199 @@
|
||||||
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#include "amd64_emulator.hpp"
|
||||||
|
#include "exceptions/key_exception.hpp"
|
||||||
|
#include "resource_traits/unicorn/unicorn_alloc.hpp"
|
||||||
|
|
||||||
|
#define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-patcher\\amd64_emulator.cpp"
|
||||||
|
#define NKG_CURRENT_SOURCE_LINE() __LINE__
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
void amd64_emulator::_unicorn_hookcode_cb_stub(uc_engine* uc, uint64_t address, uint32_t size, void* user_data) {
|
||||||
|
auto hook_stub_ctx = reinterpret_cast<hook_stub_context_t*>(user_data);
|
||||||
|
auto& hook_callback = std::any_cast<std::function<hookcode_cb_t>&>(hook_stub_ctx->self->m_unicorn_hook_callbacks[hook_stub_ctx->unicorn_hook_handle]);
|
||||||
|
hook_callback(address, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
auto hook_stub_ctx = reinterpret_cast<hook_stub_context_t*>(user_data);
|
||||||
|
auto& hook_callback = std::any_cast<std::function<hookmem_cb_t>&>(hook_stub_ctx->self->m_unicorn_hook_callbacks[hook_stub_ctx->unicorn_hook_handle]);
|
||||||
|
hook_callback(type, address, static_cast<unsigned int>(size), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
auto hook_stub_ctx = reinterpret_cast<hook_stub_context_t*>(user_data);
|
||||||
|
auto& hook_callback = std::any_cast<std::function<eventmem_cb_t>&>(hook_stub_ctx->self->m_unicorn_hook_callbacks[hook_stub_ctx->unicorn_hook_handle]);
|
||||||
|
return hook_callback(type, address, static_cast<unsigned int>(size), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
amd64_emulator::amd64_emulator() {
|
||||||
|
auto err = uc_open(UC_ARCH_X86, UC_MODE_64, m_unicorn_engine.unsafe_addressof());
|
||||||
|
if (err != UC_ERR_OK) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_open failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void amd64_emulator::reg_read(int regid, void* value) {
|
||||||
|
auto err = uc_reg_read(m_unicorn_engine.get(), regid, value);
|
||||||
|
if (err != UC_ERR_OK) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_reg_read failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void amd64_emulator::reg_write(int regid, const void* value) {
|
||||||
|
auto err = uc_reg_write(m_unicorn_engine.get(), regid, value);
|
||||||
|
if (err != UC_ERR_OK) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_reg_write failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t amd64_emulator::msr_read(uint32_t rid) {
|
||||||
|
uc_x86_msr msr;
|
||||||
|
msr.rid = rid;
|
||||||
|
|
||||||
|
auto err = uc_reg_read(m_unicorn_engine.get(), UC_X86_REG_MSR, &msr);
|
||||||
|
if (err != UC_ERR_OK) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_reg_write failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return msr.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void amd64_emulator::msr_write(uint32_t rid, uint64_t value) {
|
||||||
|
uc_x86_msr msr;
|
||||||
|
msr.rid = rid;
|
||||||
|
msr.value = value;
|
||||||
|
|
||||||
|
auto err = uc_reg_write(m_unicorn_engine.get(), UC_X86_REG_MSR, &msr);
|
||||||
|
if (err != UC_ERR_OK) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_reg_write failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void amd64_emulator::mem_map(uint64_t address, size_t size, uint32_t perms) {
|
||||||
|
auto err = uc_mem_map(m_unicorn_engine.get(), address, size, perms);
|
||||||
|
if (err != UC_ERR_OK) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_mem_map failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void amd64_emulator::mem_unmap(uint64_t address, size_t size) {
|
||||||
|
auto err = uc_mem_unmap(m_unicorn_engine.get(), address, size);
|
||||||
|
if (err != UC_ERR_OK) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_mem_unmap failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void amd64_emulator::mem_read(uint64_t address, void* buf, size_t size) {
|
||||||
|
auto err = uc_mem_read(m_unicorn_engine.get(), address, buf, size);
|
||||||
|
if (err != UC_ERR_OK) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_mem_read failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> amd64_emulator::mem_read(uint64_t address, size_t size) {
|
||||||
|
std::vector<uint8_t> ret_buf(size);
|
||||||
|
|
||||||
|
auto err = uc_mem_read(m_unicorn_engine.get(), address, ret_buf.data(), ret_buf.size());
|
||||||
|
if (err != UC_ERR_OK) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_mem_read failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
void amd64_emulator::mem_write(uint64_t address, const void* buf, size_t size) {
|
||||||
|
auto err = uc_mem_write(m_unicorn_engine.get(), address, buf, size);
|
||||||
|
if (err != UC_ERR_OK) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_mem_write failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void amd64_emulator::mem_write(uint64_t address, const std::vector<uint8_t>& buf) {
|
||||||
|
mem_write(address, buf.data(), buf.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool amd64_emulator::is_address_mapped(uint64_t address) {
|
||||||
|
resource_wrapper mapped_regions{ resource_traits::unicorn::unicorn_alloc{} };
|
||||||
|
uint32_t mapped_regions_num;
|
||||||
|
|
||||||
|
auto err = uc_mem_regions(m_unicorn_engine.get(), mapped_regions.unsafe_addressof<uc_mem_region*>(), &mapped_regions_num);
|
||||||
|
if (err != UC_ERR_OK) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_mem_regions failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < mapped_regions_num; ++i) {
|
||||||
|
auto& region = mapped_regions.as<uc_mem_region*>()[i];
|
||||||
|
if (region.begin <= address && address <= region.end) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void amd64_emulator::hook_del(uc_hook hook_handle) {
|
||||||
|
auto iter_of_hook_stub_ctxs = m_unicorn_hook_stub_ctxs.find(hook_handle);
|
||||||
|
if (iter_of_hook_stub_ctxs == m_unicorn_hook_stub_ctxs.end()) {
|
||||||
|
throw exceptions::key_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Target hook is not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto iter_of_hook_callbacks = m_unicorn_hook_callbacks.find(hook_handle);
|
||||||
|
if (iter_of_hook_callbacks != m_unicorn_hook_callbacks.end()) {
|
||||||
|
auto err = uc_hook_del(m_unicorn_engine.get(), hook_handle);
|
||||||
|
if (err) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"hook_del failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_unicorn_hook_callbacks.erase(iter_of_hook_callbacks);
|
||||||
|
m_unicorn_hook_stub_ctxs.erase(iter_of_hook_stub_ctxs);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void amd64_emulator::emu_start(uint64_t begin_address, uint64_t end_address, uint64_t timeout, size_t count) {
|
||||||
|
auto err = uc_emu_start(m_unicorn_engine.get(), begin_address, end_address, timeout, count);
|
||||||
|
if (err != UC_ERR_OK) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"emu_start failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void amd64_emulator::emu_stop() {
|
||||||
|
auto err = uc_emu_stop(m_unicorn_engine.get());
|
||||||
|
if (err != UC_ERR_OK) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_emu_stop failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//void amd64_emulator::create_gdt_entry(uint64_t gdt_entry_address, uint32_t base, uint32_t limit, uint8_t access_byte, uint8_t flags) {
|
||||||
|
// struct {
|
||||||
|
// uint16_t limit0;
|
||||||
|
// uint16_t base0;
|
||||||
|
// uint8_t base1;
|
||||||
|
// uint8_t access_byte;
|
||||||
|
// uint8_t limit1 : 4;
|
||||||
|
// uint8_t flags : 4;
|
||||||
|
// uint8_t base2;
|
||||||
|
// } segment_descriptor;
|
||||||
|
|
||||||
|
// static_assert(sizeof(segment_descriptor) == 8);
|
||||||
|
|
||||||
|
// segment_descriptor.limit0 = limit & 0xffff;
|
||||||
|
// segment_descriptor.base0 = base & 0xffff;
|
||||||
|
// segment_descriptor.base1 = (base >> 16) & 0xff;
|
||||||
|
// segment_descriptor.access_byte = access_byte;
|
||||||
|
// segment_descriptor.limit1 = (limit >> 16) & 0xf;
|
||||||
|
// segment_descriptor.flags = flags & 0xf;
|
||||||
|
// segment_descriptor.base2 = (base >> 24) & 0xff;
|
||||||
|
|
||||||
|
// auto err = uc_mem_write(m_unicorn_engine.get(), gdt_entry_address, &segment_descriptor, sizeof(segment_descriptor));
|
||||||
|
// if (err != UC_ERR_OK) {
|
||||||
|
// throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_mem_write failed.");
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef NKG_CURRENT_SOURCE_LINE
|
||||||
|
#undef NKG_CURRENT_SOURCE_FILE
|
||||||
152
navicat-patcher/amd64_emulator.hpp
Normal file
152
navicat-patcher/amd64_emulator.hpp
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
#pragma once
|
||||||
|
#include <any>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <functional>
|
||||||
|
#include <unicorn/unicorn.h>
|
||||||
|
#include "resource_wrapper.hpp"
|
||||||
|
#include "resource_traits/unicorn/unicorn_handle.hpp"
|
||||||
|
#include "exception.hpp"
|
||||||
|
|
||||||
|
#define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-patcher\\amd64_emulator.hpp"
|
||||||
|
#define NKG_CURRENT_SOURCE_LINE() __LINE__
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
class amd64_emulator {
|
||||||
|
public:
|
||||||
|
class backend_error : public ::nkg::exception {
|
||||||
|
public:
|
||||||
|
using error_code_t = uc_err;
|
||||||
|
|
||||||
|
private:
|
||||||
|
error_code_t m_error_code;
|
||||||
|
std::string m_error_string;
|
||||||
|
|
||||||
|
public:
|
||||||
|
backend_error(std::string_view file, int line, error_code_t unicorn_err, std::string_view message) noexcept :
|
||||||
|
::nkg::exception(file, line, message), m_error_code(unicorn_err), m_error_string(uc_strerror(unicorn_err)) {}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual bool error_code_exists() const noexcept override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual intptr_t error_code() const noexcept override {
|
||||||
|
return m_error_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual const std::string& error_string() const noexcept override {
|
||||||
|
return m_error_string;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using hookcode_cb_t = void(uint64_t address, size_t size);
|
||||||
|
using hookmem_cb_t = void(uc_mem_type type, uint64_t address, size_t size, int64_t value);
|
||||||
|
using eventmem_cb_t = bool(uc_mem_type type, uint64_t address, size_t size, int64_t value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct hook_stub_context_t {
|
||||||
|
amd64_emulator* self;
|
||||||
|
uc_hook unicorn_hook_handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
resource_wrapper<resource_traits::unicorn::unicorn_handle> m_unicorn_engine;
|
||||||
|
std::unordered_map<std::string, std::any> m_unicorn_user_ctx;
|
||||||
|
std::unordered_map<uc_hook, std::unique_ptr<hook_stub_context_t>> m_unicorn_hook_stub_ctxs;
|
||||||
|
std::unordered_map<uc_hook, std::any> m_unicorn_hook_callbacks;
|
||||||
|
|
||||||
|
static void _unicorn_hookcode_cb_stub(uc_engine* uc, uint64_t address, uint32_t size, void* user_data);
|
||||||
|
static void _unicorn_hookmem_cb_stub(uc_engine* uc, uc_mem_type type, uint64_t address, int size, int64_t value, void* user_data);
|
||||||
|
static bool _unicorn_eventmem_cb_stub(uc_engine* uc, uc_mem_type type, uint64_t address, int size, int64_t value, void* user_data);
|
||||||
|
|
||||||
|
public:
|
||||||
|
amd64_emulator();
|
||||||
|
|
||||||
|
void reg_read(int regid, void* buf);
|
||||||
|
|
||||||
|
void reg_write(int regid, const void* buf);
|
||||||
|
|
||||||
|
uint64_t msr_read(uint32_t rid);
|
||||||
|
|
||||||
|
void msr_write(uint32_t rid, uint64_t value);
|
||||||
|
|
||||||
|
void mem_map(uint64_t address, size_t size, uint32_t perms);
|
||||||
|
|
||||||
|
void mem_unmap(uint64_t address, size_t size);
|
||||||
|
|
||||||
|
void mem_read(uint64_t address, void* buf, size_t size);
|
||||||
|
|
||||||
|
std::vector<uint8_t> mem_read(uint64_t address, size_t size);
|
||||||
|
|
||||||
|
void mem_write(uint64_t address, const void* buf, size_t size);
|
||||||
|
|
||||||
|
void mem_write(uint64_t address, const std::vector<uint8_t>& buf);
|
||||||
|
|
||||||
|
bool is_address_mapped(uint64_t address);
|
||||||
|
|
||||||
|
template<int hook_type, typename callable_t>
|
||||||
|
uc_hook hook_add(callable_t&& hook_callback, uint64_t begin_address = 1, uint64_t end_address = 0) {
|
||||||
|
uc_err err;
|
||||||
|
|
||||||
|
auto hook_stub_ctx = std::make_unique<hook_stub_context_t>();
|
||||||
|
hook_stub_ctx->self = this;
|
||||||
|
hook_stub_ctx->unicorn_hook_handle = 0;
|
||||||
|
|
||||||
|
if constexpr (hook_type == UC_HOOK_CODE) {
|
||||||
|
err = uc_hook_add(m_unicorn_engine.get(), &hook_stub_ctx->unicorn_hook_handle, hook_type, reinterpret_cast<void*>(_unicorn_hookcode_cb_stub), hook_stub_ctx.get(), begin_address, end_address);
|
||||||
|
if (err != UC_ERR_OK) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_hook_add failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_unicorn_hook_callbacks.emplace(std::make_pair(hook_stub_ctx->unicorn_hook_handle, std::function<hookcode_cb_t>{ std::forward<callable_t>(hook_callback) }));
|
||||||
|
} else if constexpr ((hook_type & ~UC_HOOK_MEM_VALID) == 0) {
|
||||||
|
err = uc_hook_add(m_unicorn_engine.get(), &hook_stub_ctx->unicorn_hook_handle, hook_type, reinterpret_cast<void*>(_unicorn_hookmem_cb_stub), hook_stub_ctx.get(), begin_address, end_address);
|
||||||
|
if (err != UC_ERR_OK) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_hook_add failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_unicorn_hook_callbacks.emplace(std::make_pair(hook_stub_ctx->unicorn_hook_handle, std::function<hookmem_cb_t>{ std::forward<callable_t>(hook_callback) }));
|
||||||
|
} else if constexpr ((hook_type & ~UC_HOOK_MEM_UNMAPPED) == 0 || (hook_type & ~UC_HOOK_MEM_PROT) == 0) {
|
||||||
|
err = uc_hook_add(m_unicorn_engine.get(), &hook_stub_ctx->unicorn_hook_handle, hook_type, reinterpret_cast<void*>(_unicorn_eventmem_cb_stub), hook_stub_ctx.get(), begin_address, end_address);
|
||||||
|
if (err != UC_ERR_OK) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"uc_hook_add failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_unicorn_hook_callbacks.emplace(std::make_pair(hook_stub_ctx->unicorn_hook_handle, std::function<eventmem_cb_t>{ std::forward<callable_t>(hook_callback) }));
|
||||||
|
} else {
|
||||||
|
static_assert(
|
||||||
|
hook_type == UC_HOOK_CODE ||
|
||||||
|
(hook_type & ~UC_HOOK_MEM_VALID) == 0 ||
|
||||||
|
(hook_type & ~UC_HOOK_MEM_UNMAPPED) == 0 || (hook_type & ~UC_HOOK_MEM_PROT) == 0, "Unsupported hook type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_unicorn_hook_stub_ctxs.emplace(std::make_pair(hook_stub_ctx->unicorn_hook_handle, std::move(hook_stub_ctx))).first->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hook_del(uc_hook hook_handle);
|
||||||
|
|
||||||
|
void emu_start(uint64_t begin_address, uint64_t end_address = 0, uint64_t timeout = 0, size_t count = 0);
|
||||||
|
|
||||||
|
void emu_stop();
|
||||||
|
|
||||||
|
// void create_gdt_entry(uint64_t gdt_entry_address, uint32_t base, uint32_t limit, uint8_t access_byte, uint8_t flags);
|
||||||
|
|
||||||
|
template<typename val_t>
|
||||||
|
void context_set(const std::string& name, val_t&& value) {
|
||||||
|
m_unicorn_user_ctx[name] = std::forward<val_t>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename val_t>
|
||||||
|
val_t context_get(const std::string& name) {
|
||||||
|
return std::any_cast<val_t>(m_unicorn_user_ctx[name]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef NKG_CURRENT_SOURCE_LINE
|
||||||
|
#undef NKG_CURRENT_SOURCE_FILE
|
||||||
635
navicat-patcher/elf64_interpreter.cpp
Normal file
635
navicat-patcher/elf64_interpreter.cpp
Normal file
|
|
@ -0,0 +1,635 @@
|
||||||
|
#include "elf64_interpreter.hpp"
|
||||||
|
#include "exceptions/index_exception.hpp"
|
||||||
|
#include "exceptions/key_exception.hpp"
|
||||||
|
#include <memory.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#define NKG_CURRENT_SOURCE_FILE() ".\\navicat-patcher\\elf64_interpreter.cpp"
|
||||||
|
#define NKG_CURRENT_SOURCE_LINE() __LINE__
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
elf64_interpreter::elf64_interpreter() :
|
||||||
|
m_elf_size(0),
|
||||||
|
m_elf_header(nullptr),
|
||||||
|
m_elf_program_headers(nullptr),
|
||||||
|
m_elf_section_headers(nullptr),
|
||||||
|
m_dynamic_rela(nullptr),
|
||||||
|
m_dynamic_relasz(nullptr),
|
||||||
|
m_dynamic_rel(nullptr),
|
||||||
|
m_dynamic_relsz(nullptr),
|
||||||
|
m_dynamic_pltgot(nullptr),
|
||||||
|
m_dynamic_jmprel(nullptr),
|
||||||
|
m_dynamic_pltrel(nullptr),
|
||||||
|
m_dynamic_pltrelsz(nullptr),
|
||||||
|
m_dynamic_symtab(nullptr),
|
||||||
|
m_dynamic_strtab(nullptr) {}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
elf64_interpreter elf64_interpreter::parse(void* image_ptr, size_t image_size) {
|
||||||
|
elf64_interpreter new_image;
|
||||||
|
|
||||||
|
// check ELF header
|
||||||
|
new_image.m_elf_size = image_size;
|
||||||
|
new_image.m_elf_header = reinterpret_cast<Elf64_Ehdr*>(image_ptr);
|
||||||
|
if (is_address_in_range(new_image.m_elf_header, sizeof(Elf64_Ehdr), image_ptr, image_size) == false) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: image is corrupted.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memcmp(new_image.m_elf_header->e_ident, ELFMAG, SELFMAG) != 0) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: header magic check failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_image.m_elf_header->e_ident[EI_CLASS] != ELFCLASS64) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Unsupported ELF file: not ELF64 image.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_image.m_elf_header->e_ident[EI_DATA] == ELFDATA2LSB && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) {
|
||||||
|
; // pass
|
||||||
|
} else if (new_image.m_elf_header->e_ident[EI_DATA] == ELFDATA2MSB && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) {
|
||||||
|
; // pass
|
||||||
|
} else {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Unsupported ELF file: unsupported endian.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_image.m_elf_header->e_ident[EI_VERSION] != EV_CURRENT) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_ident[EI_VERSION] check failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// new_image.m_elf_header->e_ident[EI_OSABI]
|
||||||
|
// new_image.m_elf_header->e_ident[EI_ABIVERSION]
|
||||||
|
|
||||||
|
for (int i = EI_PAD; i < sizeof(new_image.m_elf_header->e_ident); ++i) {
|
||||||
|
if (new_image.m_elf_header->e_ident[i] != 0) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_ident padding contains non-zero byte(s).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_image.m_elf_header->e_version != EV_CURRENT) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_version check failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_image.m_elf_header->e_ehsize != sizeof(Elf64_Ehdr)) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_ehsize check failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_image.m_elf_header->e_phoff && new_image.m_elf_header->e_phentsize && new_image.m_elf_header->e_phnum) {
|
||||||
|
if (new_image.m_elf_header->e_phentsize != sizeof(Elf64_Phdr)) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_phentsize check failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
new_image.m_elf_program_headers = address_offset_cast<Elf64_Phdr*>(image_ptr, new_image.m_elf_header->e_phoff);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: image is corrupted.");
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
new_image.m_elf_program_headers = nullptr;
|
||||||
|
} else {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_ph* check failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_image.m_elf_header->e_shoff && new_image.m_elf_header->e_shentsize && new_image.m_elf_header->e_shnum) {
|
||||||
|
if (new_image.m_elf_header->e_shentsize != sizeof(Elf64_Shdr)) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_shentsize check failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
new_image.m_elf_section_headers = address_offset_cast<Elf64_Shdr*>(image_ptr, new_image.m_elf_header->e_shoff);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: image is corrupted.");
|
||||||
|
}
|
||||||
|
} 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) {
|
||||||
|
new_image.m_elf_section_headers = nullptr;
|
||||||
|
} else {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_sh* check failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_image.m_elf_header->e_shstrndx != SHN_UNDEF) {
|
||||||
|
if (new_image.m_elf_header->e_shstrndx >= new_image.m_elf_header->e_shnum) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: Elf64_Ehdr::e_shstrndx is out of range.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check program header table and section header table are not overlapped
|
||||||
|
if (new_image.m_elf_program_headers && new_image.m_elf_section_headers) {
|
||||||
|
auto a1 = new_image.m_elf_program_headers;
|
||||||
|
auto a2 = new_image.m_elf_program_headers + new_image.m_elf_header->e_phnum;
|
||||||
|
auto b1 = new_image.m_elf_section_headers;
|
||||||
|
auto b2 = new_image.m_elf_section_headers + new_image.m_elf_header->e_shnum;
|
||||||
|
bool not_overlapped = address_delta(a1, b1) < 0 && address_delta(a2, b1) <= 0 || address_delta(b1, a1) < 0 && address_delta(b2, a1) <= 0;
|
||||||
|
if (!not_overlapped) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: program header table and section header table overlapped.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse program header
|
||||||
|
for (size_t i = 0; i < new_image.m_elf_header->e_phnum; ++i) {
|
||||||
|
auto& prog_hdr = new_image.m_elf_program_headers[i];
|
||||||
|
if (!is_address_in_range(address_offset(image_ptr, static_cast<ptrdiff_t>(prog_hdr.p_offset)), prog_hdr.p_filesz, image_ptr, image_size)) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: image is corrupted.");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto prog_hdr_align = prog_hdr.p_align;
|
||||||
|
if (prog_hdr_align) {
|
||||||
|
// align must be a power of 2
|
||||||
|
if ((prog_hdr_align & (prog_hdr_align - 1)) != 0) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prog_hdr.p_offset % prog_hdr_align != prog_hdr.p_vaddr % prog_hdr_align) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prog_hdr.p_type == PT_LOAD) {
|
||||||
|
new_image.m_segment_va_lookup_table.emplace(std::make_pair(prog_hdr.p_vaddr, &prog_hdr));
|
||||||
|
new_image.m_segment_fo_lookup_table.emplace(std::make_pair(prog_hdr.p_offset, &prog_hdr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse section header
|
||||||
|
if (new_image.m_elf_header->e_shstrndx != SHN_UNDEF) {
|
||||||
|
auto sect_hdr_strtab = &new_image.m_elf_section_headers[new_image.m_elf_header->e_shstrndx];
|
||||||
|
auto sect_view_strtab = address_offset_cast<const char*>(image_ptr, sect_hdr_strtab->sh_offset);
|
||||||
|
|
||||||
|
if (sect_hdr_strtab->sh_type != SHT_STRTAB) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: sect_hdr_strtab->sh_type != SHT_STRTAB.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_address_in_range(sect_view_strtab, sect_hdr_strtab->sh_size, image_ptr, image_size)) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: image is corrupted.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < new_image.m_elf_header->e_shnum; ++i) {
|
||||||
|
new_image.m_section_name_lookup_table
|
||||||
|
.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]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < new_image.m_elf_header->e_shnum; ++i) {
|
||||||
|
auto& sect_hdr = new_image.m_elf_section_headers[i];
|
||||||
|
switch (sect_hdr.sh_type) {
|
||||||
|
case SHT_SYMTAB:
|
||||||
|
if (sect_hdr.sh_entsize != sizeof(Elf64_Sym)) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_entsize != sizeof(Elf64_Sym).", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check sh_link
|
||||||
|
if (sect_hdr.sh_link == SHN_UNDEF) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link == SHN_UNDEF.", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sect_hdr.sh_link >= new_image.m_elf_header->e_shnum) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_image.m_elf_section_headers[sect_hdr.sh_link].sh_type != SHT_STRTAB) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: bad value of sh_link.", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: check sh_info
|
||||||
|
break;
|
||||||
|
case SHT_RELA:
|
||||||
|
if (sect_hdr.sh_entsize != sizeof(Elf64_Rela)) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_entsize != sizeof(Elf64_Rela).", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check sh_link
|
||||||
|
if (sect_hdr.sh_link == SHN_UNDEF) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link == SHN_UNDEF.", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sect_hdr.sh_link >= new_image.m_elf_header->e_shnum) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check sh_info
|
||||||
|
if (sect_hdr.sh_flags & SHF_INFO_LINK) {
|
||||||
|
if (sect_hdr.sh_info == SHN_UNDEF) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info == SHN_UNDEF.", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sect_hdr.sh_info >= new_image.m_elf_header->e_shnum) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (sect_hdr.sh_info != 0) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info != 0.", i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SHT_HASH:
|
||||||
|
if (sect_hdr.sh_link == SHN_UNDEF) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link == SHN_UNDEF.", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sect_hdr.sh_link >= new_image.m_elf_header->e_shnum) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: bad value of sh_link.", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sect_hdr.sh_info != 0) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info != 0.", i));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SHT_DYNAMIC:
|
||||||
|
if (sect_hdr.sh_entsize != sizeof(Elf64_Dyn)) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_entsize != sizeof(Elf64_Dyn).", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check sh_link
|
||||||
|
if (sect_hdr.sh_link == SHN_UNDEF) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link == SHN_UNDEF.", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sect_hdr.sh_link >= new_image.m_elf_header->e_shnum) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_image.m_elf_section_headers[sect_hdr.sh_link].sh_type != SHT_STRTAB) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: bad value of sh_link.", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check sh_info
|
||||||
|
if (sect_hdr.sh_info != 0) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info != 0.", i));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SHT_REL:
|
||||||
|
if (sect_hdr.sh_entsize != sizeof(Elf64_Rel)) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_entsize != sizeof(Elf64_Rel).", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check sh_link
|
||||||
|
if (sect_hdr.sh_link == SHN_UNDEF) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link == SHN_UNDEF.", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sect_hdr.sh_link >= new_image.m_elf_header->e_shnum) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check sh_info
|
||||||
|
if (sect_hdr.sh_flags & SHF_INFO_LINK) {
|
||||||
|
if (sect_hdr.sh_info == SHN_UNDEF) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info == SHN_UNDEF.", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sect_hdr.sh_info >= new_image.m_elf_header->e_shnum) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (sect_hdr.sh_info != 0) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_info != 0.", i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SHT_DYNSYM:
|
||||||
|
if (sect_hdr.sh_entsize != sizeof(Elf64_Sym)) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_entsize != sizeof(Elf64_Dyn).", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check sh_link
|
||||||
|
if (sect_hdr.sh_link == SHN_UNDEF) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: sh_link == SHN_UNDEF.", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sect_hdr.sh_link >= new_image.m_elf_header->e_shnum) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_image.m_elf_section_headers[sect_hdr.sh_link].sh_type != SHT_STRTAB) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: bad value of sh_link.", i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: check sh_info
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sect_hdr.sh_type != SHT_NOBITS) {
|
||||||
|
if (is_address_in_range(address_offset(image_ptr, sect_hdr.sh_offset), sect_hdr.sh_size, image_ptr, image_size) == false) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Bad ELF file: Elf64_Shdr[{}]: image is corrupted.", i));
|
||||||
|
}
|
||||||
|
new_image.m_section_fo_lookup_table.emplace(std::make_pair(sect_hdr.sh_offset, new_image.m_elf_section_headers + i));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sect_hdr.sh_addr) {
|
||||||
|
if (sect_hdr.sh_addralign && sect_hdr.sh_addr % sect_hdr.sh_addralign != 0) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
new_image.m_section_va_lookup_table.emplace(std::make_pair(sect_hdr.sh_addr, &new_image.m_elf_section_headers[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse program header, second parse
|
||||||
|
for (size_t i = 0; i < new_image.m_elf_header->e_phnum; ++i) {
|
||||||
|
auto& prog_hdr = new_image.m_elf_program_headers[i];
|
||||||
|
if (prog_hdr.p_type == PT_DYNAMIC) {
|
||||||
|
auto seg_dynamic_base = address_offset_cast<Elf64_Dyn*>(image_ptr, prog_hdr.p_offset);
|
||||||
|
auto seg_dyncmic_size = prog_hdr.p_filesz;
|
||||||
|
|
||||||
|
// first parse
|
||||||
|
for (size_t j = 0; j * sizeof(Elf64_Dyn) < seg_dyncmic_size && seg_dynamic_base[j].d_tag != DT_NULL; ++j) {
|
||||||
|
auto& dyn_entry = seg_dynamic_base[j];
|
||||||
|
switch (dyn_entry.d_tag) {
|
||||||
|
case DT_RELA:
|
||||||
|
new_image.m_dynamic_rela = &seg_dynamic_base[j];
|
||||||
|
break;
|
||||||
|
case DT_RELAENT:
|
||||||
|
if (seg_dynamic_base[j].d_un.d_val != sizeof(Elf64_Rela)) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: the value of DT_RELAENT.dval != sizeof(Elf64_Rela).");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DT_RELASZ:
|
||||||
|
new_image.m_dynamic_relasz = &seg_dynamic_base[j];
|
||||||
|
break;
|
||||||
|
case DT_REL:
|
||||||
|
new_image.m_dynamic_rel = &seg_dynamic_base[j];
|
||||||
|
break;
|
||||||
|
case DT_RELENT:
|
||||||
|
if (seg_dynamic_base[j].d_un.d_val != sizeof(Elf64_Rel)) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Bad ELF file: the value of DT_RELENT.dval != sizeof(Elf64_Rel).");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DT_RELSZ:
|
||||||
|
new_image.m_dynamic_relsz = &seg_dynamic_base[j];
|
||||||
|
break;
|
||||||
|
case DT_PLTGOT:
|
||||||
|
new_image.m_dynamic_pltgot = &seg_dynamic_base[j];
|
||||||
|
break;
|
||||||
|
case DT_JMPREL:
|
||||||
|
new_image.m_dynamic_jmprel = &seg_dynamic_base[j];
|
||||||
|
break;
|
||||||
|
case DT_PLTREL:
|
||||||
|
if (seg_dynamic_base[j].d_un.d_val == DT_REL || seg_dynamic_base[j].d_un.d_val == DT_RELA) {
|
||||||
|
new_image.m_dynamic_pltrel = &seg_dynamic_base[j];
|
||||||
|
} else {
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DT_PLTRELSZ:
|
||||||
|
new_image.m_dynamic_pltrelsz = &seg_dynamic_base[j];
|
||||||
|
break;
|
||||||
|
case DT_STRTAB:
|
||||||
|
new_image.m_dynamic_strtab = &seg_dynamic_base[j];
|
||||||
|
break;
|
||||||
|
case DT_SYMTAB:
|
||||||
|
new_image.m_dynamic_symtab = &seg_dynamic_base[j];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_image.m_dynamic_rela && new_image.m_dynamic_relasz) {
|
||||||
|
auto rela_base = new_image.convert_va_to_ptr<Elf64_Rela*>(new_image.m_dynamic_rela->d_un.d_ptr);
|
||||||
|
auto rela_size = new_image.m_dynamic_relasz->d_un.d_val;
|
||||||
|
for (size_t i = 0; i * sizeof(Elf64_Rela) < rela_size; ++i) {
|
||||||
|
auto reloc_va = rela_base[i].r_offset;
|
||||||
|
auto reloc_type = ELF64_R_TYPE(rela_base[i].r_info);
|
||||||
|
switch(reloc_type) {
|
||||||
|
case R_X86_64_64:
|
||||||
|
case R_X86_64_GLOB_DAT:
|
||||||
|
case R_X86_64_RELATIVE:
|
||||||
|
new_image.m_relocation_distribute.emplace(std::make_pair(reloc_va, 8));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Unsupported ELF file: unhandled relocation type({}).", reloc_type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_image.m_dynamic_rel && new_image.m_dynamic_relsz) {
|
||||||
|
throw parse_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Unsupported ELF file: DT_REL is not parsed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_image.m_dynamic_jmprel && new_image.m_dynamic_pltrel && new_image.m_dynamic_pltrelsz) {
|
||||||
|
if (new_image.m_dynamic_pltrel->d_un.d_val == DT_RELA) {
|
||||||
|
auto jmprel_base = new_image.convert_va_to_ptr<Elf64_Rela*>(new_image.m_dynamic_jmprel->d_un.d_ptr);
|
||||||
|
auto jmprel_size = new_image.m_dynamic_pltrelsz->d_un.d_val;
|
||||||
|
for (size_t i = 0; i * sizeof(Elf64_Rela) < jmprel_size; ++i) {
|
||||||
|
auto reloc_va = jmprel_base[i].r_offset;
|
||||||
|
auto reloc_type = ELF64_R_TYPE(jmprel_base[i].r_info);
|
||||||
|
if (reloc_type == R_X86_64_JUMP_SLOT) {
|
||||||
|
new_image.m_relocation_distribute.emplace(std::make_pair(reloc_va, 8));
|
||||||
|
} else {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto jmprel_base = new_image.convert_va_to_ptr<Elf64_Rel*>(new_image.m_dynamic_jmprel->d_un.d_ptr);
|
||||||
|
auto jmprel_size = new_image.m_dynamic_pltrelsz->d_un.d_val;
|
||||||
|
for (size_t i = 0; i * sizeof(Elf64_Rela) < jmprel_size; ++i) {
|
||||||
|
auto reloc_va = jmprel_base[i].r_offset;
|
||||||
|
auto reloc_type = ELF64_R_TYPE(jmprel_base[i].r_info);
|
||||||
|
if (reloc_type == R_X86_64_JUMP_SLOT) {
|
||||||
|
new_image.m_relocation_distribute.emplace(std::make_pair(reloc_va, 8));
|
||||||
|
} else {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_image;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
size_t elf64_interpreter::elf_size() const noexcept {
|
||||||
|
return m_elf_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Elf64_Ehdr* elf64_interpreter::elf_header() const noexcept {
|
||||||
|
return m_elf_header;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Elf64_Phdr* elf64_interpreter::elf_program_header(size_t n) const {
|
||||||
|
if (n < m_elf_header->e_phnum) {
|
||||||
|
return m_elf_program_headers + n;
|
||||||
|
} else {
|
||||||
|
throw exceptions::index_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Out of range.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Elf64_Phdr* elf64_interpreter::elf_program_header_from_fo(fo_t file_offset) const {
|
||||||
|
auto it = m_segment_fo_lookup_table.upper_bound(file_offset);
|
||||||
|
if (it != m_segment_fo_lookup_table.begin()) {
|
||||||
|
--it;
|
||||||
|
if (it->second->p_offset <= file_offset && file_offset < it->second->p_offset + it->second->p_filesz) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw bad_fo_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("File offset({:#x}) doesn't point to any segment."));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Elf64_Phdr* elf64_interpreter::elf_program_header_from_rva(rva_t rva) const {
|
||||||
|
return elf_program_header_from_va(convert_rva_to_va(rva));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Elf64_Phdr* elf64_interpreter::elf_program_header_from_va(va_t va) const {
|
||||||
|
auto it = m_segment_va_lookup_table.upper_bound(va);
|
||||||
|
if (it != m_segment_va_lookup_table.begin()) {
|
||||||
|
--it;
|
||||||
|
if (it->second->p_vaddr <= va && va < it->second->p_vaddr + it->second->p_memsz) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw bad_va_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Invalid virtual address({:#016x}).", va));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
size_t elf64_interpreter::elf_program_headers_num() const noexcept {
|
||||||
|
return m_elf_header->e_shnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Elf64_Shdr* elf64_interpreter::elf_section_header(size_t n) const {
|
||||||
|
if (n < m_elf_header->e_shnum) {
|
||||||
|
return m_elf_section_headers + n;
|
||||||
|
} else {
|
||||||
|
throw exceptions::index_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Out of range.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Elf64_Shdr* elf64_interpreter::elf_section_header(std::string_view section_name) const {
|
||||||
|
auto it = m_section_name_lookup_table.find(section_name);
|
||||||
|
if (it != m_section_name_lookup_table.end()) {
|
||||||
|
return it->second;
|
||||||
|
} else {
|
||||||
|
throw exceptions::key_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Section `{}` is not found.", section_name.data()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
size_t elf64_interpreter::elf_section_headers_num() const noexcept {
|
||||||
|
return m_elf_header->e_shnum;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
elf64_interpreter::fo_t elf64_interpreter::convert_rva_to_fo(rva_t rva) const {
|
||||||
|
return convert_va_to_fo(convert_rva_to_va(rva));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
elf64_interpreter::fo_t elf64_interpreter::convert_va_to_fo(va_t va) const {
|
||||||
|
auto it = m_segment_va_lookup_table.upper_bound(va);
|
||||||
|
if (it != m_segment_va_lookup_table.begin()) {
|
||||||
|
--it;
|
||||||
|
if (it->second->p_vaddr <= va && va < it->second->p_vaddr + it->second->p_memsz) {
|
||||||
|
if (va - it->second->p_vaddr < it->second->p_filesz) {
|
||||||
|
return it->second->p_offset + (va - it->second->p_vaddr);
|
||||||
|
} else {
|
||||||
|
throw bad_va_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Virtual address({:#016x}) doesn't have corresponding file offset.", va));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw bad_va_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), fmt::format("Invalid virtual address({:#016x})", va));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
elf64_interpreter::rva_t elf64_interpreter::convert_fo_to_rva(fo_t file_offset) const {
|
||||||
|
return convert_va_to_rva(convert_fo_to_va(file_offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
elf64_interpreter::rva_t elf64_interpreter::convert_va_to_rva(va_t va) const {
|
||||||
|
return va - m_segment_va_lookup_table.begin()->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
elf64_interpreter::va_t elf64_interpreter::convert_fo_to_va(fo_t file_offset) const {
|
||||||
|
auto it = m_segment_fo_lookup_table.upper_bound(file_offset);
|
||||||
|
if (it != m_segment_fo_lookup_table.begin()) {
|
||||||
|
--it;
|
||||||
|
if (it->second->p_offset <= file_offset && file_offset < it->second->p_offset + it->second->p_filesz) {
|
||||||
|
return it->second->p_vaddr + (file_offset - it->second->p_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
elf64_interpreter::va_t elf64_interpreter::convert_rva_to_va(rva_t rva) const {
|
||||||
|
return m_segment_va_lookup_table.begin()->first + rva;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::optional<elf64_interpreter::va_t> elf64_interpreter::elf_dynamic_rela() const {
|
||||||
|
return m_dynamic_rela ? std::make_optional(m_dynamic_rela->d_un.d_ptr) : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::optional<size_t> elf64_interpreter::elf_dynamic_relasz() const {
|
||||||
|
return m_dynamic_relasz ? std::make_optional(m_dynamic_relasz->d_un.d_ptr) : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::optional<elf64_interpreter::va_t> elf64_interpreter::elf_dynamic_rel() const {
|
||||||
|
return m_dynamic_rel ? std::make_optional(m_dynamic_rel->d_un.d_ptr) : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::optional<size_t> elf64_interpreter::elf_dynamic_relsz() const {
|
||||||
|
return m_dynamic_relsz ? std::make_optional(m_dynamic_relsz->d_un.d_ptr) : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::optional<elf64_interpreter::va_t> elf64_interpreter::elf_dynamic_pltgot() const {
|
||||||
|
return m_dynamic_pltgot ? std::make_optional(m_dynamic_pltgot->d_un.d_ptr) : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::optional<elf64_interpreter::va_t> elf64_interpreter::elf_dynamic_jmprel() const {
|
||||||
|
return m_dynamic_jmprel ? std::make_optional(m_dynamic_jmprel->d_un.d_ptr) : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::optional<int> elf64_interpreter::elf_dynamic_pltrel() const {
|
||||||
|
return m_dynamic_pltrel ? std::make_optional(m_dynamic_pltrel->d_un.d_val) : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::optional<size_t> elf64_interpreter::elf_dynamic_pltrelsz() const {
|
||||||
|
return m_dynamic_pltrelsz ? std::make_optional(m_dynamic_pltrelsz->d_un.d_val) : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::optional<elf64_interpreter::va_t> elf64_interpreter::elf_dynamic_symtab() const {
|
||||||
|
return m_dynamic_symtab ? std::make_optional(m_dynamic_symtab->d_un.d_ptr) : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::optional<elf64_interpreter::va_t> elf64_interpreter::elf_dynamic_strtab() const {
|
||||||
|
return m_dynamic_strtab ? std::make_optional(m_dynamic_strtab->d_un.d_ptr) : std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
const std::map<elf64_interpreter::va_t, size_t>& elf64_interpreter::relocation_distribute() const {
|
||||||
|
return m_relocation_distribute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef NKG_CURRENT_SOURCE_LINE
|
||||||
|
#undef NKG_CURRENT_SOURCE_FILE
|
||||||
239
navicat-patcher/elf64_interpreter.hpp
Normal file
239
navicat-patcher/elf64_interpreter.hpp
Normal file
|
|
@ -0,0 +1,239 @@
|
||||||
|
#pragma once
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <elf.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "exception.hpp"
|
||||||
|
#include "memory_utility.hpp"
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
class elf64_interpreter {
|
||||||
|
public:
|
||||||
|
using fo_t = uintptr_t;
|
||||||
|
using rva_t = uintptr_t;
|
||||||
|
using va_t = uintptr_t;
|
||||||
|
|
||||||
|
class parse_error : public ::nkg::exception {
|
||||||
|
public:
|
||||||
|
parse_error(std::string_view file, int line, std::string_view message) noexcept :
|
||||||
|
::nkg::exception(file, line, message) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class bad_fo_exception : public ::nkg::exception {
|
||||||
|
public:
|
||||||
|
bad_fo_exception(std::string_view file, int line, std::string_view message) noexcept :
|
||||||
|
::nkg::exception(file, line, message) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class bad_va_exception : public ::nkg::exception {
|
||||||
|
public:
|
||||||
|
bad_va_exception(std::string_view file, int line, std::string_view message) noexcept :
|
||||||
|
::nkg::exception(file, line, message) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t m_elf_size;
|
||||||
|
Elf64_Ehdr* m_elf_header;
|
||||||
|
Elf64_Phdr* m_elf_program_headers;
|
||||||
|
Elf64_Shdr* m_elf_section_headers;
|
||||||
|
|
||||||
|
std::map<Elf64_Addr, Elf64_Phdr*> m_segment_va_lookup_table;
|
||||||
|
std::map<Elf64_Off, Elf64_Phdr*> m_segment_fo_lookup_table;
|
||||||
|
|
||||||
|
std::map<Elf64_Addr, Elf64_Shdr*> m_section_va_lookup_table;
|
||||||
|
std::map<Elf64_Off, Elf64_Shdr*> m_section_fo_lookup_table;
|
||||||
|
std::map<std::string_view, Elf64_Shdr*> m_section_name_lookup_table;
|
||||||
|
|
||||||
|
Elf64_Dyn* m_dynamic_rela;
|
||||||
|
Elf64_Dyn* m_dynamic_relasz;
|
||||||
|
Elf64_Dyn* m_dynamic_rel;
|
||||||
|
Elf64_Dyn* m_dynamic_relsz;
|
||||||
|
Elf64_Dyn* m_dynamic_pltgot;
|
||||||
|
Elf64_Dyn* m_dynamic_jmprel;
|
||||||
|
Elf64_Dyn* m_dynamic_pltrel;
|
||||||
|
Elf64_Dyn* m_dynamic_pltrelsz;
|
||||||
|
Elf64_Dyn* m_dynamic_symtab;
|
||||||
|
Elf64_Dyn* m_dynamic_strtab;
|
||||||
|
|
||||||
|
std::map<va_t, size_t> m_relocation_distribute;
|
||||||
|
|
||||||
|
elf64_interpreter();
|
||||||
|
|
||||||
|
public:
|
||||||
|
[[nodiscard]]
|
||||||
|
static elf64_interpreter parse(void* image_ptr, size_t image_size);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
size_t elf_size() const noexcept;
|
||||||
|
|
||||||
|
template<typename return_t = void*>
|
||||||
|
[[nodiscard]]
|
||||||
|
return_t elf_base() const noexcept {
|
||||||
|
static_assert(std::is_pointer_v<return_t>);
|
||||||
|
return reinterpret_cast<return_t>(m_elf_header);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename return_t = void*>
|
||||||
|
[[nodiscard]]
|
||||||
|
return_t elf_offset(fo_t offset) const noexcept {
|
||||||
|
static_assert(std::is_pointer_v<return_t>);
|
||||||
|
return address_offset_cast<return_t>(m_elf_header, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Elf64_Ehdr* elf_header() const noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Elf64_Phdr* elf_program_header(size_t n) const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Elf64_Phdr* elf_program_header_from_fo(fo_t file_offset) const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Elf64_Phdr* elf_program_header_from_rva(rva_t rva) const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Elf64_Phdr* elf_program_header_from_va(va_t va) const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
size_t elf_program_headers_num() const noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Elf64_Shdr* elf_section_header(size_t n) const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Elf64_Shdr* elf_section_header(std::string_view section_name) const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
size_t elf_section_headers_num() const noexcept;
|
||||||
|
|
||||||
|
template<typename return_t = void*>
|
||||||
|
[[nodiscard]]
|
||||||
|
return_t elf_section_view(size_t n, fo_t offset = 0) const {
|
||||||
|
return elf_offset<return_t>(elf_section_header(n)->sh_offset + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename return_t = void*>
|
||||||
|
[[nodiscard]]
|
||||||
|
return_t elf_section_view(std::string_view section_name, fo_t offset = 0) const {
|
||||||
|
return elf_offset<return_t>(elf_section_header(section_name)->sh_offset + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ptr_t>
|
||||||
|
[[nodiscard]]
|
||||||
|
fo_t convert_ptr_to_fo(ptr_t ptr) const {
|
||||||
|
return address_delta(ptr, m_elf_header);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
fo_t convert_rva_to_fo(rva_t rva) const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
fo_t convert_va_to_fo(va_t va) const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
rva_t convert_fo_to_rva(fo_t file_offset) const;
|
||||||
|
|
||||||
|
template<typename ptr_t>
|
||||||
|
[[nodiscard]]
|
||||||
|
rva_t convert_ptr_to_rva(ptr_t ptr) const {
|
||||||
|
return convert_fo_to_rva(convert_ptr_to_fo(ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
rva_t convert_va_to_rva(va_t va) const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
va_t convert_fo_to_va(fo_t file_offset) const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
va_t convert_rva_to_va(rva_t rva) const;
|
||||||
|
|
||||||
|
template<typename ptr_t>
|
||||||
|
va_t convert_ptr_to_va(ptr_t ptr) const {
|
||||||
|
return convert_fo_to_va(convert_ptr_to_fo(ptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ptr_t = void*>
|
||||||
|
[[nodiscard]]
|
||||||
|
ptr_t convert_fo_to_ptr(fo_t offset) const {
|
||||||
|
return elf_offset<ptr_t>(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ptr_t = void*>
|
||||||
|
[[nodiscard]]
|
||||||
|
ptr_t convert_rva_to_ptr(rva_t rva) const {
|
||||||
|
return convert_fo_to_ptr<ptr_t>(convert_rva_to_fo(rva));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ptr_t = void*>
|
||||||
|
[[nodiscard]]
|
||||||
|
ptr_t convert_va_to_ptr(va_t va) const {
|
||||||
|
return convert_fo_to_ptr<ptr_t>(convert_va_to_fo(va));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename return_t = void*, typename callable_t>
|
||||||
|
[[nodiscard]]
|
||||||
|
return_t search_section(size_t n, callable_t&& pred_func) const noexcept {
|
||||||
|
auto sect_hdr = elf_section_header(n);
|
||||||
|
auto sect_view = elf_offset<uint8_t*>(sect_hdr->sh_offset);
|
||||||
|
for (size_t i = 0; i < sect_hdr->sh_size; ++i) {
|
||||||
|
if (pred_func(sect_view, i, sect_hdr->sh_size)) {
|
||||||
|
return reinterpret_cast<return_t>(sect_view + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename return_t = void*, typename callable_t>
|
||||||
|
[[nodiscard]]
|
||||||
|
return_t search_section(std::string_view section_name, callable_t&& pred_func) const noexcept {
|
||||||
|
auto sect_hdr = elf_section_header(section_name);
|
||||||
|
auto sect_view = elf_offset<uint8_t*>(sect_hdr->sh_offset);
|
||||||
|
for (size_t i = 0; i < sect_hdr->sh_size; ++i) {
|
||||||
|
if (pred_func(sect_view, i, sect_hdr->sh_size)) {
|
||||||
|
return reinterpret_cast<return_t>(sect_view + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::optional<va_t> elf_dynamic_rela() const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::optional<size_t> elf_dynamic_relasz() const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::optional<va_t> elf_dynamic_rel() const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::optional<size_t> elf_dynamic_relsz() const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::optional<va_t> elf_dynamic_pltgot() const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::optional<va_t> elf_dynamic_jmprel() const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::optional<int> elf_dynamic_pltrel() const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::optional<size_t> elf_dynamic_pltrelsz() const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::optional<va_t> elf_dynamic_symtab() const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::optional<va_t> elf_dynamic_strtab() const;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
const std::map<va_t, size_t>& relocation_distribute() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
40
navicat-patcher/keystone_assembler.cpp
Normal file
40
navicat-patcher/keystone_assembler.cpp
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
#include "keystone_assembler.hpp"
|
||||||
|
#include "resource_traits/keystone/keystone_alloc.hpp"
|
||||||
|
|
||||||
|
#define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-patcher\\keystone_assembler.cpp"
|
||||||
|
#define NKG_CURRENT_SOURCE_LINE() __LINE__
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
keystone_assembler::keystone_assembler(ks_arch architecture, ks_mode mode) {
|
||||||
|
auto err = ks_open(architecture, mode, m_keystone_engine.unsafe_addressof());
|
||||||
|
if (err != KS_ERR_OK) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"ks_open failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void keystone_assembler::option(ks_opt_type option_type, size_t option_value) {
|
||||||
|
auto err = ks_option(m_keystone_engine.get(), option_type, option_value);
|
||||||
|
if (err != KS_ERR_OK) {
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"ks_option failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::vector<uint8_t> keystone_assembler::assemble(std::string_view asm_string, uint64_t asm_address) const {
|
||||||
|
resource_wrapper machine_code{ resource_traits::keystone::keystone_alloc{} };
|
||||||
|
|
||||||
|
size_t machine_code_size = 0;
|
||||||
|
size_t stat_count = 0;
|
||||||
|
if (ks_asm(m_keystone_engine.get(), asm_string.data(), asm_address, machine_code.unsafe_addressof(), &machine_code_size, &stat_count) < 0) {
|
||||||
|
auto err = ks_errno(m_keystone_engine.get());
|
||||||
|
throw backend_error(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), err, u8"ks_asm failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::vector<uint8_t>(machine_code.get(), machine_code.get() + machine_code_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef NKG_CURRENT_SOURCE_LINE
|
||||||
|
#undef NKG_CURRENT_SOURCE_FILE
|
||||||
55
navicat-patcher/keystone_assembler.hpp
Normal file
55
navicat-patcher/keystone_assembler.hpp
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <keystone/keystone.h>
|
||||||
|
|
||||||
|
#include "resource_wrapper.hpp"
|
||||||
|
#include "resource_traits/keystone/keystone_handle.hpp"
|
||||||
|
|
||||||
|
#include "exception.hpp"
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
class keystone_assembler {
|
||||||
|
public:
|
||||||
|
class backend_error : public ::nkg::exception {
|
||||||
|
public:
|
||||||
|
using error_code_t = ks_err;
|
||||||
|
|
||||||
|
private:
|
||||||
|
error_code_t m_error_code;
|
||||||
|
std::string m_error_string;
|
||||||
|
|
||||||
|
public:
|
||||||
|
backend_error(std::string_view file, int line, error_code_t keystone_err, std::string_view message) noexcept :
|
||||||
|
::nkg::exception(file, line, message), m_error_code(keystone_err), m_error_string(ks_strerror(keystone_err)) {}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual bool error_code_exists() const noexcept override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual intptr_t error_code() const noexcept override {
|
||||||
|
return m_error_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual const std::string& error_string() const noexcept override {
|
||||||
|
return m_error_string;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
resource_wrapper<resource_traits::keystone::keystone_handle> m_keystone_engine;
|
||||||
|
|
||||||
|
public:
|
||||||
|
keystone_assembler(ks_arch architecture, ks_mode mode);
|
||||||
|
|
||||||
|
void option(ks_opt_type option_type, size_t option_value);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
std::vector<uint8_t> assemble(std::string_view asm_string, uint64_t asm_address = 0) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
265
navicat-patcher/main.cpp
Normal file
265
navicat-patcher/main.cpp
Normal file
|
|
@ -0,0 +1,265 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <cxxopts.hpp>
|
||||||
|
|
||||||
|
#include "resource_wrapper.hpp"
|
||||||
|
#include "resource_traits/cxx_object_traits.hpp"
|
||||||
|
#include "resource_traits/unix_os/file_descriptor.hpp"
|
||||||
|
#include "resource_traits/unix_os/map_view.hpp"
|
||||||
|
|
||||||
|
#include "rsa_cipher.hpp"
|
||||||
|
#include "elf64_interpreter.hpp"
|
||||||
|
#include "patch_solution.hpp"
|
||||||
|
#include "patch_solution_since_16.0.7.0.hpp"
|
||||||
|
|
||||||
|
#include "exception.hpp"
|
||||||
|
#include "exceptions/unix_exception.hpp"
|
||||||
|
#include "exceptions/operation_canceled_exception.hpp"
|
||||||
|
|
||||||
|
#define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-patcher\\main.cpp"
|
||||||
|
#define NKG_CURRENT_SOURCE_LINE() __LINE__
|
||||||
|
|
||||||
|
void welcome() {
|
||||||
|
puts("***************************************************");
|
||||||
|
puts("* navicat-patcher by @DoubleLabyrinth *");
|
||||||
|
puts("* version: 16.0.7.0 *");
|
||||||
|
puts("***************************************************");
|
||||||
|
}
|
||||||
|
|
||||||
|
void select_patch_solutions(nkg::resource_wrapper<nkg::resource_traits::cxx_object_traits<nkg::patch_solution>>& solution0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_rsa_privkey(nkg::rsa_cipher& cipher, std::filesystem::path& rsa_key_file, nkg::patch_solution* solution0) {
|
||||||
|
if (!rsa_key_file.empty()) {
|
||||||
|
printf("[*] Import RSA-2048 private key from\n");
|
||||||
|
printf(" %s\n", rsa_key_file.native().c_str());
|
||||||
|
|
||||||
|
cipher.import_private_key_file(rsa_key_file.native());
|
||||||
|
|
||||||
|
if (solution0 && !solution0->check_rsa_privkey(cipher)) {
|
||||||
|
throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "The RSA private key you provide cannot be used.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("[*] Generating new RSA private key, it may take a long time...\n");
|
||||||
|
|
||||||
|
do {
|
||||||
|
cipher.generate_key(2048);
|
||||||
|
} while (solution0 && !solution0->check_rsa_privkey(cipher)); // re-generate RSA key if one of `check_rsa_privkey` returns false
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[*] Your RSA private key:\n%s\n", cipher.export_private_key_string().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... args_t>
|
||||||
|
bool all_patch_solutions_are_suppressed(args_t&&... args) {
|
||||||
|
return (!args.is_valid() && ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
void detect_backup(const std::filesystem::path& file_path) {
|
||||||
|
std::filesystem::path backup_path = file_path.native() + ".bak";
|
||||||
|
if (std::filesystem::is_regular_file(backup_path)) {
|
||||||
|
while (true) {
|
||||||
|
printf("[*] Previous backup %s is detected. Delete? (y/n)", backup_path.native().c_str());
|
||||||
|
|
||||||
|
auto select = getchar();
|
||||||
|
while (select != '\n' && getchar() != '\n') {}
|
||||||
|
|
||||||
|
if (select == 'Y' || select == 'y') {
|
||||||
|
std::filesystem::remove(backup_path);
|
||||||
|
break;
|
||||||
|
} else if (select == 'N' || select == 'n') {
|
||||||
|
throw nkg::exceptions::operation_canceled_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Backup file still exists. Patch abort!");
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void make_backup(const std::filesystem::path& file_path) {
|
||||||
|
std::filesystem::path backup_path = file_path.native() + ".bak";
|
||||||
|
if (std::filesystem::exists(backup_path)) {
|
||||||
|
throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Previous backup is detected.")
|
||||||
|
.push_hint(fmt::format("Please delete {} and try again.", backup_path.native()));
|
||||||
|
} else {
|
||||||
|
std::filesystem::copy_file(file_path, backup_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
welcome();
|
||||||
|
|
||||||
|
bool dry_run = false;
|
||||||
|
std::filesystem::path navicat_root;
|
||||||
|
std::filesystem::path rsa_key_file;
|
||||||
|
|
||||||
|
cxxopts::Options cmd_parser{ "navicat-patcher" };
|
||||||
|
cmd_parser.add_options()
|
||||||
|
("dry-run", "Run patcher without applying any patches", cxxopts::value(dry_run))
|
||||||
|
("navicat-root", "Navicat root directory", cxxopts::value(navicat_root))
|
||||||
|
("rsa-key-file", "RSA-2048 private key file", cxxopts::value(rsa_key_file))
|
||||||
|
("h,help", "Print help");
|
||||||
|
|
||||||
|
cmd_parser.positional_help("<navicat root path> [RSA-2048 private key file]");
|
||||||
|
cmd_parser.parse_positional({ "navicat-root", "rsa-key-file" });
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto cmd_result = cmd_parser.parse(argc, argv);
|
||||||
|
|
||||||
|
if (cmd_result.count("help")) {
|
||||||
|
puts(cmd_parser.help().c_str());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd_result.count("navicat-root") == 0) {
|
||||||
|
puts(cmd_parser.help().c_str());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
puts("");
|
||||||
|
|
||||||
|
if (!std::filesystem::is_directory(navicat_root)) {
|
||||||
|
throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Navicat root path doesn't point to a directory.")
|
||||||
|
.push_hint("Are you sure the path you specified is correct?")
|
||||||
|
.push_hint(fmt::format("The path you specified: {}", navicat_root.native()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rsa_key_file.empty() && !std::filesystem::is_regular_file(rsa_key_file)) {
|
||||||
|
throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "RSA private key file path doesn't point to a file.")
|
||||||
|
.push_hint("Are you sure the path you specified is correct?")
|
||||||
|
.push_hint(fmt::format("The path you specified: {}", rsa_key_file.native()));
|
||||||
|
}
|
||||||
|
|
||||||
|
nkg::rsa_cipher cipher;
|
||||||
|
|
||||||
|
std::filesystem::path libcc_filepath = navicat_root / "usr" / "lib" / "libcc.so";
|
||||||
|
nkg::resource_wrapper libcc_fd{ nkg::resource_traits::unix_os::file_descriptor{} };
|
||||||
|
nkg::resource_wrapper libcc_stat{ nkg::resource_traits::cxx_object_traits<struct stat>{} };
|
||||||
|
nkg::resource_wrapper libcc_map_view{ nkg::resource_traits::unix_os::map_view{}, [&libcc_stat](void* p) { munmap(p, libcc_stat->st_size); } };
|
||||||
|
std::optional<nkg::elf64_interpreter> libcc_interpreter;
|
||||||
|
|
||||||
|
nkg::resource_wrapper solution0{ nkg::resource_traits::cxx_object_traits<nkg::patch_solution>{} };
|
||||||
|
|
||||||
|
// open libcc.dll
|
||||||
|
libcc_fd.set(open(libcc_filepath.native().c_str(), O_RDWR));
|
||||||
|
if (libcc_fd.is_valid()) {
|
||||||
|
printf("[+] Try to open libcc.dll ... OK!\n");
|
||||||
|
} else {
|
||||||
|
if (errno == ENOENT) {
|
||||||
|
printf("[-] Try to open libcc.dll ... NOT FOUND!\n");
|
||||||
|
} else {
|
||||||
|
throw nkg::exceptions::unix_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), errno, "open failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (libcc_fd.is_valid()) {
|
||||||
|
libcc_stat.set(new struct stat());
|
||||||
|
if (fstat(libcc_fd.get(), libcc_stat.get()) != 0) {
|
||||||
|
throw nkg::exceptions::unix_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), errno, "fstat failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
libcc_map_view.set(mmap(nullptr, libcc_stat->st_size, PROT_READ | PROT_WRITE, MAP_SHARED, libcc_fd.get(), 0));
|
||||||
|
if (!libcc_map_view.is_valid()) {
|
||||||
|
throw nkg::exceptions::unix_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), errno, "mmap failed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
libcc_interpreter = nkg::elf64_interpreter::parse(libcc_map_view.get(), libcc_stat->st_size);
|
||||||
|
|
||||||
|
solution0.set(new nkg::patch_solution_since<16, 0, 7, 0>(libcc_interpreter.value()));
|
||||||
|
}
|
||||||
|
|
||||||
|
puts("");
|
||||||
|
|
||||||
|
// find patch and decide which solution will be applied
|
||||||
|
if (solution0.is_valid()) {
|
||||||
|
auto patch_found = solution0->find_patch();
|
||||||
|
puts("");
|
||||||
|
|
||||||
|
if (!patch_found) {
|
||||||
|
solution0.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
select_patch_solutions(solution0);
|
||||||
|
|
||||||
|
if (all_patch_solutions_are_suppressed(solution0)) {
|
||||||
|
throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "All patch solutions are suppressed. Patch abort!")
|
||||||
|
.push_hint("Are you sure your navicat has not been patched/modified before?");
|
||||||
|
}
|
||||||
|
|
||||||
|
// load key
|
||||||
|
load_rsa_privkey(cipher, rsa_key_file, solution0.get());
|
||||||
|
|
||||||
|
// apply patch solutions
|
||||||
|
if (dry_run) {
|
||||||
|
puts("*******************************************************");
|
||||||
|
puts("* DRY-RUN MODE ENABLE! *");
|
||||||
|
puts("* NO PATCH WILL BE APPLIED! *");
|
||||||
|
puts("*******************************************************");
|
||||||
|
} else {
|
||||||
|
// save private key if not given
|
||||||
|
if (rsa_key_file.empty()) {
|
||||||
|
cipher.export_private_key_file(u8"RegPrivateKey.pem");
|
||||||
|
}
|
||||||
|
|
||||||
|
// detecting backups
|
||||||
|
if (solution0.is_valid()) {
|
||||||
|
detect_backup(libcc_filepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// make backup
|
||||||
|
if (solution0.is_valid()) {
|
||||||
|
make_backup(libcc_filepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// make patch
|
||||||
|
// no way to go back from here :-)
|
||||||
|
if (solution0.is_valid()) {
|
||||||
|
solution0->make_patch(cipher);
|
||||||
|
}
|
||||||
|
|
||||||
|
// print new key file path
|
||||||
|
if (rsa_key_file.empty()) {
|
||||||
|
printf("[*] New RSA-2048 private key has been saved to\n");
|
||||||
|
printf(" %s\n", (std::filesystem::current_path() / "RegPrivateKey.pem").c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
puts("");
|
||||||
|
puts("*******************************************************");
|
||||||
|
puts("* PATCH HAS BEEN DONE SUCCESSFULLY! *");
|
||||||
|
puts("* HAVE FUN AND ENJOY~ *");
|
||||||
|
puts("*******************************************************");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} catch (cxxopts::OptionException&) {
|
||||||
|
puts(cmd_parser.help().c_str());
|
||||||
|
return -1;
|
||||||
|
} catch (nkg::exception& e) {
|
||||||
|
printf("[-] %s:%d ->\n", e.source_file().c_str(), e.source_line());
|
||||||
|
printf(" %s\n", e.custom_message().c_str());
|
||||||
|
if (e.error_code_exists()) {
|
||||||
|
printf(" %s (0x%zx)\n", e.error_string().c_str(), e.error_code());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& hint : e.hints()) {
|
||||||
|
printf(" HINT: %s\n", hint.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef NKG_CURRENT_SOURCE_LINE
|
||||||
|
#undef NKG_CURRENT_SOURCE_FILE
|
||||||
136
navicat-patcher/memory_utility.hpp
Normal file
136
navicat-patcher/memory_utility.hpp
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
#pragma once
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
template<typename ptr1_t, typename ptr2_t>
|
||||||
|
[[nodiscard]]
|
||||||
|
inline ptrdiff_t address_delta(ptr1_t ptr1, ptr2_t ptr2) noexcept {
|
||||||
|
static_assert(std::is_pointer_v<ptr1_t> && std::is_pointer_v<ptr2_t>);
|
||||||
|
return reinterpret_cast<const volatile char*>(ptr1) - reinterpret_cast<const volatile char*>(ptr2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ptr_t>
|
||||||
|
[[nodiscard]]
|
||||||
|
inline ptr_t address_offset(ptr_t ptr, ptrdiff_t off) noexcept {
|
||||||
|
static_assert(std::is_pointer_v<ptr_t>);
|
||||||
|
return reinterpret_cast<ptr_t>(
|
||||||
|
const_cast<char*>(
|
||||||
|
reinterpret_cast<const volatile char*>(ptr) + off
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename return_ptr_t, typename ptr_t>
|
||||||
|
[[nodiscard]]
|
||||||
|
inline return_ptr_t address_offset_cast(ptr_t ptr, ptrdiff_t off) noexcept {
|
||||||
|
static_assert(std::is_pointer_v<return_ptr_t> && std::is_pointer_v<ptr_t>);
|
||||||
|
return reinterpret_cast<return_ptr_t>(address_offset(ptr, off));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ptr_t, typename begin_ptr_t, typename end_ptr_t>
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool is_address_in_range(ptr_t ptr, begin_ptr_t begin, end_ptr_t end) {
|
||||||
|
static_assert(std::is_pointer_v<ptr_t> && std::is_pointer_v<begin_ptr_t> && std::is_pointer_v<end_ptr_t>);
|
||||||
|
auto _ptr = reinterpret_cast<const volatile char*>(ptr);
|
||||||
|
auto _begin = reinterpret_cast<const volatile char*>(begin);
|
||||||
|
auto _end = reinterpret_cast<const volatile char*>(end);
|
||||||
|
return _begin <= _ptr && _ptr < _end;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ptr_t, typename base_ptr_t>
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool is_address_in_range(ptr_t ptr, base_ptr_t base, size_t size) {
|
||||||
|
static_assert(std::is_pointer_v<ptr_t> && std::is_pointer_v<base_ptr_t>);
|
||||||
|
return is_address_in_range(ptr, base, address_offset(base, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ptr1_t, typename ptr2_t, typename begin_ptr_t, typename end_ptr_t>
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool is_address_in_range(ptr1_t ptr1, ptr2_t ptr2, begin_ptr_t begin, end_ptr_t end) {
|
||||||
|
static_assert(std::is_pointer_v<ptr1_t> && std::is_pointer_v<ptr2_t> && std::is_pointer_v<begin_ptr_t> && std::is_pointer_v<end_ptr_t>);
|
||||||
|
auto _ptr1 = reinterpret_cast<const volatile char*>(ptr1);
|
||||||
|
auto _ptr2 = reinterpret_cast<const volatile char*>(ptr2);
|
||||||
|
auto _begin = reinterpret_cast<const volatile char*>(begin);
|
||||||
|
auto _end = reinterpret_cast<const volatile char*>(end);
|
||||||
|
return _begin <= _ptr1 && _ptr1 <= _ptr2 && _ptr2 <= _end;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ptr_t, typename begin_ptr_t, typename end_ptr_t>
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool is_address_in_range(ptr_t ptr, size_t size, begin_ptr_t begin, end_ptr_t end) {
|
||||||
|
static_assert(std::is_pointer_v<ptr_t> && std::is_pointer_v<begin_ptr_t> && std::is_pointer_v<end_ptr_t>);
|
||||||
|
return is_address_in_range(ptr, address_offset(ptr, size), begin, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ptr1_t, typename ptr2_t, typename base_ptr_t>
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool is_address_in_range(ptr1_t ptr1, ptr2_t ptr2, base_ptr_t base, size_t size) {
|
||||||
|
static_assert(std::is_pointer_v<ptr1_t> && std::is_pointer_v<ptr2_t> && std::is_pointer_v<base_ptr_t>);
|
||||||
|
return is_address_in_range(ptr1, ptr2, base, address_offset(base, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ptr_t, typename base_ptr_t>
|
||||||
|
[[nodiscard]]
|
||||||
|
inline bool is_address_in_range(ptr_t p, size_t s, base_ptr_t base, size_t size) {
|
||||||
|
static_assert(std::is_pointer_v<ptr_t>);
|
||||||
|
static_assert(std::is_pointer_v<base_ptr_t>);
|
||||||
|
return is_address_in_range(p, address_offset(p, s), base, address_offset(base, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
// template<typename __ReadType, typename __PtrType>
|
||||||
|
// [[nodiscard]]
|
||||||
|
// inline __ReadType AddressRead(__PtrType p) noexcept {
|
||||||
|
// static_assert(std::is_trivial_v<__ReadType> && std::is_standard_layout_v<__ReadType>);
|
||||||
|
// static_assert(std::is_pointer_v<__PtrType>);
|
||||||
|
// return *reinterpret_cast<const volatile __ReadType*>(p);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// template<typename __ReadType, typename __PtrType>
|
||||||
|
// [[nodiscard]]
|
||||||
|
// inline __ReadType AddressRead(__PtrType p, ptrdiff_t offset) noexcept {
|
||||||
|
// static_assert(std::is_trivial_v<__ReadType> && std::is_standard_layout_v<__ReadType>);
|
||||||
|
// static_assert(std::is_pointer_v<__PtrType>);
|
||||||
|
// return *reinterpret_cast<const volatile __ReadType*>(
|
||||||
|
// reinterpret_cast<const volatile char*>(p) + offset
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// template<typename __ReadType, typename __PtrType>
|
||||||
|
// [[nodiscard]]
|
||||||
|
// inline __ReadType AddressRead(__PtrType p, size_t scale, ptrdiff_t index) noexcept {
|
||||||
|
// static_assert(std::is_trivial_v<__ReadType> && std::is_standard_layout_v<__ReadType>);
|
||||||
|
// static_assert(std::is_pointer_v<__PtrType>);
|
||||||
|
// return *reinterpret_cast<const volatile __ReadType*>(
|
||||||
|
// reinterpret_cast<const volatile char*>(p) + scale * index
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// template<typename __WriteType, typename __PtrType>
|
||||||
|
// inline void AddressWrite(__PtrType p, const __WriteType& value) noexcept {
|
||||||
|
// static_assert(std::is_trivial_v<__WriteType> && std::is_standard_layout_v<__WriteType>);
|
||||||
|
// static_assert(std::is_pointer_v<__PtrType>);
|
||||||
|
// *reinterpret_cast<volatile __WriteType*>(p) = value;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// template<typename __WriteType, typename __PtrType>
|
||||||
|
// inline void AddressWrite(__PtrType p, ptrdiff_t offset, const __WriteType& value) noexcept {
|
||||||
|
// static_assert(std::is_trivial_v<__WriteType> && std::is_standard_layout_v<__WriteType>);
|
||||||
|
// static_assert(std::is_pointer_v<__PtrType>);
|
||||||
|
// *reinterpret_cast<volatile __WriteType*>(
|
||||||
|
// reinterpret_cast<volatile char*>(p) + offset
|
||||||
|
// ) = value;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// template<typename __WriteType, typename __PtrType>
|
||||||
|
// inline void AddressWrite(__PtrType p, size_t scale, ptrdiff_t index, const __WriteType& value) noexcept {
|
||||||
|
// static_assert(std::is_trivial_v<__WriteType> && std::is_standard_layout_v<__WriteType>);
|
||||||
|
// static_assert(std::is_pointer_v<__PtrType>);
|
||||||
|
// *reinterpret_cast<volatile __WriteType*>(
|
||||||
|
// reinterpret_cast<volatile char*>(p) + scale * index
|
||||||
|
// ) = value;
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
19
navicat-patcher/patch_solution.hpp
Normal file
19
navicat-patcher/patch_solution.hpp
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
#pragma once
|
||||||
|
#include "rsa_cipher.hpp"
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
class patch_solution {
|
||||||
|
public:
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual bool find_patch() = 0;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual bool check_rsa_privkey(const rsa_cipher& cipher) = 0;
|
||||||
|
|
||||||
|
virtual void make_patch(const rsa_cipher& cipher) = 0;
|
||||||
|
|
||||||
|
virtual ~patch_solution() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
9
navicat-patcher/patch_solution_since.hpp
Normal file
9
navicat-patcher/patch_solution_since.hpp
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#pragma once
|
||||||
|
#include "patch_solution.hpp"
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
template<int major_ver0, int major_ver1, int minor_ver0, int minor_ver1>
|
||||||
|
class patch_solution_since;
|
||||||
|
|
||||||
|
}
|
||||||
659
navicat-patcher/patch_solution_since_16.0.7.0.cpp
Normal file
659
navicat-patcher/patch_solution_since_16.0.7.0.cpp
Normal file
|
|
@ -0,0 +1,659 @@
|
||||||
|
#include "patch_solution_since_16.0.7.0.hpp"
|
||||||
|
#include <regex>
|
||||||
|
#include "keystone_assembler.hpp"
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
uint64_t patch_solution_since<16, 0, 7, 0>::_emulator_append_external_api_impl(amd64_emulator& x64_emulator, std::string_view api_name, const std::vector<uint8_t>& api_impl) {
|
||||||
|
auto& external_api_impl = x64_emulator.context_get<std::map<std::string, uint64_t>&>("external_api_impl");
|
||||||
|
auto& external_api_impl_area_base = x64_emulator.context_get<uint64_t&>("external_api_impl_area_base");
|
||||||
|
auto& external_api_impl_area_size = x64_emulator.context_get<size_t&>("external_api_impl_area_size");
|
||||||
|
auto& external_api_impl_append_address = x64_emulator.context_get<uint64_t&>("external_api_impl_append_address");
|
||||||
|
|
||||||
|
auto p = external_api_impl_append_address;
|
||||||
|
if (p + api_impl.size() > external_api_impl_area_base + external_api_impl_area_size) {
|
||||||
|
auto expand_size = ((p + api_impl.size()) - (external_api_impl_area_base + external_api_impl_area_size) + 0xfff) / 0x1000 * 0x1000;
|
||||||
|
x64_emulator.mem_map(external_api_impl_area_base + external_api_impl_area_size, expand_size, UC_PROT_READ | UC_PROT_EXEC);
|
||||||
|
external_api_impl_area_size += expand_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
x64_emulator.mem_write(p, api_impl);
|
||||||
|
|
||||||
|
external_api_impl[std::string(api_name)] = p;
|
||||||
|
external_api_impl_append_address = (p + api_impl.size() + 0xf) / 0x10 * 0x10;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t patch_solution_since<16, 0, 7, 0>::_emulator_malloc(amd64_emulator& x64_emulator, size_t alloc_size) {
|
||||||
|
auto& heap_records = x64_emulator.context_get<std::map<uint64_t, uint64_t>&>("heap_records");
|
||||||
|
|
||||||
|
auto predecessor_chunk =
|
||||||
|
std::adjacent_find(heap_records.begin(), heap_records.end(), [alloc_size](auto& chunk0, auto& chunk1) { return chunk1.first - (chunk0.first + chunk0.second) >= alloc_size; });
|
||||||
|
|
||||||
|
uint64_t alloc_p;
|
||||||
|
if (predecessor_chunk != heap_records.end()) {
|
||||||
|
alloc_p = predecessor_chunk->first + predecessor_chunk->second;
|
||||||
|
} else {
|
||||||
|
auto heap_base = x64_emulator.context_get<uint64_t>("heap_base");
|
||||||
|
auto heap_size = x64_emulator.context_get<uint64_t>("heap_size");
|
||||||
|
|
||||||
|
auto free_space_base = heap_records.empty() ? heap_base : heap_records.rbegin()->first + heap_records.rbegin()->second;
|
||||||
|
auto free_space_size = heap_base + heap_size - free_space_base;
|
||||||
|
|
||||||
|
if (free_space_size < alloc_size) {
|
||||||
|
auto heap_expand_base = heap_base + heap_size;
|
||||||
|
auto heap_expand_size = (alloc_size - free_space_size + 0xfff) / 0x1000 * 0x1000;
|
||||||
|
x64_emulator.mem_map(heap_expand_base, heap_expand_size, UC_PROT_READ | UC_PROT_WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
alloc_p = free_space_base;
|
||||||
|
}
|
||||||
|
|
||||||
|
heap_records[alloc_p] = alloc_size;
|
||||||
|
|
||||||
|
return alloc_p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void patch_solution_since<16, 0, 7, 0>::_emulator_free(amd64_emulator& x64_emulator, uint64_t alloc_p) {
|
||||||
|
auto& heap_records = x64_emulator.context_get<std::map<uint64_t, uint64_t>&>("heap_records");
|
||||||
|
|
||||||
|
auto chunk = heap_records.find(alloc_p);
|
||||||
|
if (chunk != heap_records.end()) {
|
||||||
|
heap_records.erase(chunk);
|
||||||
|
} else {
|
||||||
|
printf("[-] patch_solution_since<16, 0, 7, 0>: emulator tries to free 0x%016lx which is not allocated by malloc.\n", alloc_p);
|
||||||
|
x64_emulator.emu_stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool patch_solution_since<16, 0, 7, 0>::_emulator_page_fault_handler(amd64_emulator& x64_emulator, uc_mem_type access, uint64_t address, size_t size, int64_t value) {
|
||||||
|
try {
|
||||||
|
auto fault_segment = m_libcc_interpreter.elf_program_header_from_va(address);
|
||||||
|
|
||||||
|
auto page_base = address / 0x1000 * 0x1000;
|
||||||
|
auto page_size = 0x1000;
|
||||||
|
uint32_t page_perms = UC_PROT_NONE;
|
||||||
|
|
||||||
|
if (fault_segment->p_flags & PF_R) {
|
||||||
|
page_perms |= UC_PROT_READ;
|
||||||
|
}
|
||||||
|
if (fault_segment->p_flags & PF_W) {
|
||||||
|
page_perms |= UC_PROT_WRITE;
|
||||||
|
}
|
||||||
|
if (fault_segment->p_flags & PF_X) {
|
||||||
|
page_perms |= UC_PROT_EXEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
x64_emulator.mem_map(page_base, page_size, page_perms);
|
||||||
|
x64_emulator.mem_write(page_base, m_libcc_interpreter.convert_va_to_ptr<const void*>(page_base), page_size);
|
||||||
|
|
||||||
|
auto dynamic_pltgot = m_libcc_interpreter.elf_dynamic_pltgot();
|
||||||
|
if (dynamic_pltgot.has_value() && page_base <= dynamic_pltgot.value() + 0x8 && dynamic_pltgot.value() + 0x10 <= page_base + page_size) {
|
||||||
|
uint64_t dead_address = x64_emulator.context_get<uint64_t>("dead_address");
|
||||||
|
x64_emulator.mem_write(dynamic_pltgot.value() + 0x8, &dead_address, sizeof(dead_address));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dynamic_pltgot.has_value() && page_base <= dynamic_pltgot.value() + 0x10 && dynamic_pltgot.value() + 0x18 <= page_base + page_size) {
|
||||||
|
uint64_t dl_runtime_resolve = x64_emulator.context_get<std::map<std::string, uint64_t>&>("external_api_impl")["dl_runtime_resolve"];
|
||||||
|
x64_emulator.mem_write(dynamic_pltgot.value() + 0x10, &dl_runtime_resolve, sizeof(dl_runtime_resolve));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (::nkg::exception&) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void patch_solution_since<16, 0, 7, 0>::_emulator_dl_runtime_resolve_handler(amd64_emulator& x64_emulator, uint64_t address, size_t size) {
|
||||||
|
uint64_t rsp;
|
||||||
|
x64_emulator.reg_read(UC_X86_REG_RSP, &rsp);
|
||||||
|
|
||||||
|
uint64_t reloc_idx;
|
||||||
|
x64_emulator.mem_read(rsp + 0x8, &reloc_idx, sizeof(reloc_idx));
|
||||||
|
|
||||||
|
uint64_t reloc_va;
|
||||||
|
Elf64_Sym* reloc_sym;
|
||||||
|
uint32_t reloc_type;
|
||||||
|
char* reloc_sym_name;
|
||||||
|
if (m_libcc_interpreter.elf_dynamic_pltrel().value() == DT_REL) {
|
||||||
|
auto jmp_reloc_table = m_libcc_interpreter.convert_va_to_ptr<Elf64_Rel*>(m_libcc_interpreter.elf_dynamic_jmprel().value());
|
||||||
|
auto symbol_table = m_libcc_interpreter.convert_va_to_ptr<Elf64_Sym*>(m_libcc_interpreter.elf_dynamic_symtab().value());
|
||||||
|
auto string_table = m_libcc_interpreter.convert_va_to_ptr<char*>(m_libcc_interpreter.elf_dynamic_strtab().value());
|
||||||
|
reloc_va = jmp_reloc_table[reloc_idx].r_offset;
|
||||||
|
reloc_sym = &symbol_table[ELF64_R_SYM(jmp_reloc_table[reloc_idx].r_info)];
|
||||||
|
reloc_type = ELF64_R_TYPE(jmp_reloc_table[reloc_idx].r_info);
|
||||||
|
reloc_sym_name = &string_table[reloc_sym->st_name];
|
||||||
|
} else { // m_libcc_interpreter.elf_dynamic_pltrel().value() == DT_RELA
|
||||||
|
auto jmp_reloc_table = m_libcc_interpreter.convert_va_to_ptr<Elf64_Rela*>(m_libcc_interpreter.elf_dynamic_jmprel().value());
|
||||||
|
auto symbol_table = m_libcc_interpreter.convert_va_to_ptr<Elf64_Sym*>(m_libcc_interpreter.elf_dynamic_symtab().value());
|
||||||
|
auto string_table = m_libcc_interpreter.convert_va_to_ptr<char*>(m_libcc_interpreter.elf_dynamic_strtab().value());
|
||||||
|
reloc_va = jmp_reloc_table[reloc_idx].r_offset;
|
||||||
|
reloc_sym = &symbol_table[ELF64_R_SYM(jmp_reloc_table[reloc_idx].r_info)];
|
||||||
|
reloc_type = ELF64_R_TYPE(jmp_reloc_table[reloc_idx].r_info);
|
||||||
|
reloc_sym_name = &string_table[reloc_sym->st_name];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(reloc_sym_name, "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_appendEPKcm") == 0) {
|
||||||
|
// std::string::_M_append(char const*, unsigned long)
|
||||||
|
auto external_api_impl_va =
|
||||||
|
x64_emulator.context_get<std::map<std::string, uint64_t>&>("external_api_impl")["_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_appendEPKcm"];
|
||||||
|
|
||||||
|
x64_emulator.mem_write(reloc_va, &external_api_impl_va, sizeof(external_api_impl_va));
|
||||||
|
|
||||||
|
// external api address is resolved, set `qword ptr [rsp] = external_api_impl_va` in order to jump there
|
||||||
|
x64_emulator.mem_write(rsp, &external_api_impl_va, sizeof(external_api_impl_va));
|
||||||
|
} else if (strcmp(reloc_sym_name, "_ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE7compareEPKc") == 0) {
|
||||||
|
// std::string::compare(char const*) const
|
||||||
|
auto external_api_impl_va =
|
||||||
|
x64_emulator.context_get<std::map<std::string, uint64_t>&>("external_api_impl")["_ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE7compareEPKc"];
|
||||||
|
|
||||||
|
x64_emulator.mem_write(reloc_va, &external_api_impl_va, sizeof(external_api_impl_va));
|
||||||
|
|
||||||
|
// external api address is resolved, set `qword ptr [rsp] = external_api_impl_va` in order to jump there
|
||||||
|
x64_emulator.mem_write(rsp, &external_api_impl_va, sizeof(external_api_impl_va));
|
||||||
|
} else if (strcmp(reloc_sym_name, "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9push_backEc") == 0) {
|
||||||
|
// std::string::push_back(char)
|
||||||
|
auto external_api_impl_va =
|
||||||
|
x64_emulator.context_get<std::map<std::string, uint64_t>&>("external_api_impl")["_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9push_backEc"];
|
||||||
|
|
||||||
|
x64_emulator.mem_write(reloc_va, &external_api_impl_va, sizeof(external_api_impl_va));
|
||||||
|
|
||||||
|
// external api address is resolved, set `qword ptr [rsp] = external_api_impl_va` in order to jump there
|
||||||
|
x64_emulator.mem_write(rsp, &external_api_impl_va, sizeof(external_api_impl_va));
|
||||||
|
} else if (strcmp(reloc_sym_name, "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE6appendEPKc") == 0) {
|
||||||
|
// std::string::append(char const*)
|
||||||
|
m_va_pltgot_std_string_append = reloc_va;
|
||||||
|
|
||||||
|
auto external_api_impl_va =
|
||||||
|
x64_emulator.context_get<std::map<std::string, uint64_t>&>("external_api_impl")["_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE6appendEPKc"];
|
||||||
|
|
||||||
|
x64_emulator.mem_write(reloc_va, &external_api_impl_va, sizeof(external_api_impl_va));
|
||||||
|
|
||||||
|
// external api address is resolved, set `qword ptr [rsp] = external_api_impl_va` in order to jump there
|
||||||
|
x64_emulator.mem_write(rsp, &external_api_impl_va, sizeof(external_api_impl_va));
|
||||||
|
} else if (strcmp(reloc_sym_name, "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE6appendEPKcm") == 0) {
|
||||||
|
// std::string::append(char const*, unsigned long)
|
||||||
|
|
||||||
|
// redirect to `std::string::_M_append(char const*, unsigned long)`
|
||||||
|
auto external_api_impl_va =
|
||||||
|
x64_emulator.context_get<std::map<std::string, uint64_t>&>("external_api_impl")["_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_appendEPKcm"];
|
||||||
|
|
||||||
|
x64_emulator.mem_write(reloc_va, &external_api_impl_va, sizeof(external_api_impl_va));
|
||||||
|
|
||||||
|
// external api address is resolved, set `qword ptr [rsp] = external_api_impl_va` in order to jump there
|
||||||
|
x64_emulator.mem_write(rsp, &external_api_impl_va, sizeof(external_api_impl_va));
|
||||||
|
} else {
|
||||||
|
printf("[-] patch_solution_since<16, 0, 7, 0>: PLT GOT entry `%s` is not resolved.\n", reloc_sym_name);
|
||||||
|
x64_emulator.emu_stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void patch_solution_since<16, 0, 7, 0>::_emulator_malloc_handler(amd64_emulator& x64_emulator, uint64_t address, size_t size) {
|
||||||
|
uint64_t rdi;
|
||||||
|
x64_emulator.reg_read(UC_X86_REG_RDI, &rdi);
|
||||||
|
|
||||||
|
uint64_t rax = _emulator_malloc(x64_emulator, rdi);
|
||||||
|
x64_emulator.reg_write(UC_X86_REG_RAX, &rax);
|
||||||
|
}
|
||||||
|
|
||||||
|
void patch_solution_since<16, 0, 7, 0>::_emulator_free_handler(amd64_emulator& x64_emulator, uint64_t address, size_t size) {
|
||||||
|
uint64_t rdi;
|
||||||
|
x64_emulator.reg_read(UC_X86_REG_RDI, &rdi);
|
||||||
|
_emulator_free(x64_emulator, rdi);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string patch_solution_since<16, 0, 7, 0>::_build_encoded_key(const rsa_cipher& cipher) {
|
||||||
|
auto encoded_key = cipher.export_public_key_string_pem();
|
||||||
|
|
||||||
|
encoded_key = std::regex_replace(encoded_key, std::regex("-----BEGIN PUBLIC KEY-----"), "");
|
||||||
|
encoded_key = std::regex_replace(encoded_key, std::regex("-----END PUBLIC KEY-----"), "");
|
||||||
|
encoded_key = std::regex_replace(encoded_key, std::regex("\n"), "");
|
||||||
|
|
||||||
|
return encoded_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
patch_solution_since<16, 0, 7, 0>::patch_solution_since(elf64_interpreter& libcc_interpreter) :
|
||||||
|
m_libcc_interpreter(libcc_interpreter),
|
||||||
|
m_va_CSRegistrationInfoFetcher_LINUX_vtable(0),
|
||||||
|
m_va_CSRegistrationInfoFetcher_LINUX_GenerateRegistrationKey(0),
|
||||||
|
m_va_pltgot_std_string_append(0) {}
|
||||||
|
|
||||||
|
bool patch_solution_since<16, 0, 7, 0>::find_patch() {
|
||||||
|
if (m_libcc_interpreter.elf_header()->e_machine != EM_X86_64) {
|
||||||
|
printf("[-] patch_solution_since<16, 0, 7, 0>: Not x86-64 elf binary.\n");
|
||||||
|
printf("[-] patch_solution_since<16, 0, 7, 0>: This patch solution will be suppressed.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CSRegistrationInfoFetcher_LINUX_typeinfo_name =
|
||||||
|
m_libcc_interpreter.search_section<uint8_t*>(
|
||||||
|
".rodata",
|
||||||
|
[](const uint8_t* base, size_t off, size_t size) {
|
||||||
|
return (size - off) >= sizeof("31CSRegistrationInfoFetcher_LINUX") && strcmp(address_offset_cast<const char*>(base, off), "31CSRegistrationInfoFetcher_LINUX") == 0;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (CSRegistrationInfoFetcher_LINUX_typeinfo_name == nullptr) {
|
||||||
|
printf("[-] patch_solution_since<16, 0, 7, 0>: RTTI info for CSRegistrationInfoFetcher_LINUX is not found. (failure label 0)\n");
|
||||||
|
printf("[-] patch_solution_since<16, 0, 7, 0>: This patch solution will be suppressed.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CSRegistrationInfoFetcher_LINUX_typeinfo_name_va =
|
||||||
|
m_libcc_interpreter.convert_ptr_to_va(CSRegistrationInfoFetcher_LINUX_typeinfo_name);
|
||||||
|
|
||||||
|
auto CSRegistrationInfoFetcher_LINUX_typeinfo =
|
||||||
|
m_libcc_interpreter.search_section<uint8_t*>(
|
||||||
|
".data.rel.ro",
|
||||||
|
[CSRegistrationInfoFetcher_LINUX_typeinfo_name_va](const uint8_t* base, size_t off, size_t size) {
|
||||||
|
return off % 8 == 0 && (size - (off + 8)) >= 8 && *address_offset_cast<const uint64_t*>(base, off + 8) == CSRegistrationInfoFetcher_LINUX_typeinfo_name_va;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (CSRegistrationInfoFetcher_LINUX_typeinfo == nullptr) {
|
||||||
|
printf("[-] patch_solution_since<16, 0, 7, 0>: RTTI info for CSRegistrationInfoFetcher_LINUX is not found. (failure label 1)\n");
|
||||||
|
printf("[-] patch_solution_since<16, 0, 7, 0>: This patch solution will be suppressed.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CSRegistrationInfoFetcher_LINUX_typeinfo_va =
|
||||||
|
m_libcc_interpreter.convert_ptr_to_va(CSRegistrationInfoFetcher_LINUX_typeinfo);
|
||||||
|
|
||||||
|
auto CSRegistrationInfoFetcher_LINUX_vftable_before =
|
||||||
|
m_libcc_interpreter.search_section<uint64_t*>(
|
||||||
|
".data.rel.ro",
|
||||||
|
[CSRegistrationInfoFetcher_LINUX_typeinfo_va](const uint8_t* base, size_t off, size_t size) {
|
||||||
|
return off % 8 == 0 && (size - off) >= 8 && *address_offset_cast<const uint64_t*>(base, off) == CSRegistrationInfoFetcher_LINUX_typeinfo_va;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (CSRegistrationInfoFetcher_LINUX_vftable_before == nullptr) {
|
||||||
|
printf("[-] patch_solution_since<16, 0, 7, 0>: Vftable for CSRegistrationInfoFetcher_LINUX is not found.\n");
|
||||||
|
printf("[-] patch_solution_since<16, 0, 7, 0>: This patch solution will be suppressed.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CSRegistrationInfoFetcher_LINUX_vftable = CSRegistrationInfoFetcher_LINUX_vftable_before + 1;
|
||||||
|
|
||||||
|
m_va_CSRegistrationInfoFetcher_LINUX_vtable = m_libcc_interpreter.convert_ptr_to_va(CSRegistrationInfoFetcher_LINUX_vftable);
|
||||||
|
m_va_CSRegistrationInfoFetcher_LINUX_GenerateRegistrationKey = CSRegistrationInfoFetcher_LINUX_vftable[7];
|
||||||
|
printf("[*] patch_solution_since<16, 0, 7, 0>: m_va_CSRegistrationInfoFetcher_LINUX_vtable = 0x%016lx\n", m_va_CSRegistrationInfoFetcher_LINUX_vtable);
|
||||||
|
printf("[*] patch_solution_since<16, 0, 7, 0>: m_va_CSRegistrationInfoFetcher_LINUX_GenerateRegistrationKey = 0x%016lx\n", m_va_CSRegistrationInfoFetcher_LINUX_GenerateRegistrationKey);
|
||||||
|
|
||||||
|
amd64_emulator x64_emulator;
|
||||||
|
|
||||||
|
x64_emulator.context_set("heap_base", uint64_t{ 0x00007fff00000000 });
|
||||||
|
x64_emulator.context_set("heap_size", size_t{ 0x1000 * 32 });
|
||||||
|
x64_emulator.context_set("heap_records", std::map<uint64_t, uint64_t>{});
|
||||||
|
|
||||||
|
x64_emulator.context_set("stack_base", uint64_t{ 0x00007fffffff0000 });
|
||||||
|
x64_emulator.context_set("stack_size", size_t{ 0x1000 * 32 });
|
||||||
|
x64_emulator.context_set("stack_top", uint64_t{ x64_emulator.context_get<uint64_t>("stack_base") - x64_emulator.context_get<size_t>("stack_size") });
|
||||||
|
|
||||||
|
x64_emulator.context_set("dead_area_base", uint64_t{ 0xfffffffffffff000 });
|
||||||
|
x64_emulator.context_set("dead_area_size", size_t{ 0x1000 });
|
||||||
|
|
||||||
|
x64_emulator.context_set("external_api_impl", std::map<std::string, uint64_t>{});
|
||||||
|
x64_emulator.context_set("external_api_impl_area_base", uint64_t{ 0xffff900000000000 });
|
||||||
|
x64_emulator.context_set("external_api_impl_area_size", size_t{ 0 });
|
||||||
|
x64_emulator.context_set("external_api_impl_append_address", x64_emulator.context_get<uint64_t>("external_api_impl_area_base"));
|
||||||
|
|
||||||
|
x64_emulator.context_set("start_address", static_cast<uint64_t>(m_va_CSRegistrationInfoFetcher_LINUX_GenerateRegistrationKey));
|
||||||
|
x64_emulator.context_set("dead_address", x64_emulator.context_get<uint64_t>("dead_area_base"));
|
||||||
|
|
||||||
|
// allocate heap
|
||||||
|
x64_emulator.mem_map(x64_emulator.context_get<uint64_t>("heap_base"), x64_emulator.context_get<size_t>("heap_size"), UC_PROT_READ | UC_PROT_WRITE);
|
||||||
|
|
||||||
|
// allocate stack
|
||||||
|
x64_emulator.mem_map(x64_emulator.context_get<uint64_t>("stack_top"), x64_emulator.context_get<size_t>("stack_size"), UC_PROT_READ | UC_PROT_WRITE);
|
||||||
|
|
||||||
|
// allocate dead area
|
||||||
|
x64_emulator.mem_map(x64_emulator.context_get<uint64_t>("dead_area_base"), x64_emulator.context_get<size_t>("dead_area_size"), UC_PROT_READ | UC_PROT_EXEC);
|
||||||
|
|
||||||
|
// allocate and setup external api impl area
|
||||||
|
{
|
||||||
|
keystone_assembler x64_assembler{ KS_ARCH_X86, KS_MODE_64 };
|
||||||
|
|
||||||
|
auto& external_api_impl = x64_emulator.context_get<std::map<std::string, uint64_t>&>("external_api_impl");
|
||||||
|
auto& external_api_impl_append_address = x64_emulator.context_get<uint64_t&>("external_api_impl_append_address");
|
||||||
|
|
||||||
|
_emulator_append_external_api_impl
|
||||||
|
(
|
||||||
|
x64_emulator, "dl_runtime_resolve",
|
||||||
|
x64_assembler.assemble
|
||||||
|
(
|
||||||
|
" nop;"
|
||||||
|
" mov rax, qword ptr [rsp];"
|
||||||
|
" add rsp, 0x10;"
|
||||||
|
" cmp rax, 0;"
|
||||||
|
" je just_ret;"
|
||||||
|
" jmp rax;"
|
||||||
|
"just_ret:"
|
||||||
|
" ret;"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
_emulator_append_external_api_impl(x64_emulator, "malloc", x64_assembler.assemble("ret;"));
|
||||||
|
|
||||||
|
_emulator_append_external_api_impl(x64_emulator, "free", x64_assembler.assemble("ret;"));
|
||||||
|
|
||||||
|
_emulator_append_external_api_impl
|
||||||
|
(
|
||||||
|
x64_emulator, "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_appendEPKcm",
|
||||||
|
x64_assembler.assemble
|
||||||
|
(
|
||||||
|
fmt::format
|
||||||
|
(
|
||||||
|
" push r12;"
|
||||||
|
" push r13;"
|
||||||
|
" push r14;"
|
||||||
|
" push r15;"
|
||||||
|
" mov r12, qword ptr [rdi];"
|
||||||
|
" mov r13, qword ptr [rdi + 0x8];"
|
||||||
|
" mov r14, 15;"
|
||||||
|
" lea rax, qword ptr [rdi + 0x10];"
|
||||||
|
" cmp r12, rax;"
|
||||||
|
" cmovne r14, qword ptr [rdi + 0x10];"
|
||||||
|
" xor r15, r15;"
|
||||||
|
" lea rax, qword ptr [r13 + rdx];"
|
||||||
|
" cmp rax, r14;"
|
||||||
|
" jbe append_string;"
|
||||||
|
"reallocate:"
|
||||||
|
" push rdi;"
|
||||||
|
" push rsi;"
|
||||||
|
" push rdx;"
|
||||||
|
" lea r14, qword ptr [r13 + rdx];"
|
||||||
|
" lea rdi, qword ptr [r14 + 0x1];"
|
||||||
|
" mov rax, {malloc:#016x};"
|
||||||
|
" call rax;"
|
||||||
|
" mov rdi, rax;"
|
||||||
|
" mov rsi, r12;"
|
||||||
|
" mov rcx, r13;"
|
||||||
|
" rep movs byte ptr [rdi], byte ptr [rsi];"
|
||||||
|
" mov r12, rax;"
|
||||||
|
" mov rdi, qword ptr [rsp + 0x10];"
|
||||||
|
" lea rax, qword ptr [rdi + 0x10];"
|
||||||
|
" mov rdi, qword ptr [rdi];"
|
||||||
|
" cmp rdi, rax;"
|
||||||
|
" je label_0;"
|
||||||
|
" mov rax, {free:#016x};"
|
||||||
|
" call rax;"
|
||||||
|
"label_0:"
|
||||||
|
" pop rdx;"
|
||||||
|
" pop rsi;"
|
||||||
|
" pop rdi;"
|
||||||
|
" mov r15b, 0x1;"
|
||||||
|
"append_string:"
|
||||||
|
" push rdi;"
|
||||||
|
" push rsi;"
|
||||||
|
" lea rdi, qword ptr [r12 + r13];"
|
||||||
|
" mov rcx, rdx;"
|
||||||
|
" rep movs byte ptr [rdi], byte ptr [rsi];"
|
||||||
|
" mov byte ptr [rdi], 0;"
|
||||||
|
" pop rsi;"
|
||||||
|
" pop rdi;"
|
||||||
|
" add r13, rdx;"
|
||||||
|
"update_string_struct:"
|
||||||
|
" mov qword ptr [rdi + 0x8], r13;"
|
||||||
|
" test r15, r15;"
|
||||||
|
" jz final;"
|
||||||
|
" mov qword ptr [rdi], r12;"
|
||||||
|
" mov qword ptr [rdi + 0x10], r14;"
|
||||||
|
"final:"
|
||||||
|
" mov rax, rdi;"
|
||||||
|
" pop r15;"
|
||||||
|
" pop r14;"
|
||||||
|
" pop r13;"
|
||||||
|
" pop r12;"
|
||||||
|
" ret;",
|
||||||
|
fmt::arg("malloc", external_api_impl["malloc"]),
|
||||||
|
fmt::arg("free", external_api_impl["free"])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
_emulator_append_external_api_impl
|
||||||
|
(
|
||||||
|
x64_emulator, "_ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE7compareEPKc",
|
||||||
|
x64_assembler.assemble
|
||||||
|
(
|
||||||
|
// rcx = strlen(rsi);
|
||||||
|
" push rsi;"
|
||||||
|
" xor rcx, rcx;"
|
||||||
|
" dec rcx;"
|
||||||
|
"strlen_loop:"
|
||||||
|
" inc rcx;"
|
||||||
|
" cmp byte ptr [rsi + rcx], 0x00;"
|
||||||
|
" jne strlen_loop;"
|
||||||
|
" pop rsi;"
|
||||||
|
// rdx = rcx;
|
||||||
|
// rcx = min(this->_M_string_length, rcx);
|
||||||
|
" mov rdx, rcx;"
|
||||||
|
" cmp qword ptr [rdi + 0x8], rcx;"
|
||||||
|
" cmovb rcx, qword ptr [rdi + 0x8];"
|
||||||
|
// if (rcx == 0 || memcmp(this->_M_dataplus, rsi, rcx) == 0) goto compare_length;
|
||||||
|
" test rcx, rcx;"
|
||||||
|
" jz compare_length;"
|
||||||
|
"memcmp:"
|
||||||
|
" push rdi;"
|
||||||
|
" push rsi;"
|
||||||
|
" mov rdi, qword ptr [rdi];"
|
||||||
|
" xchg rsi, rdi;"
|
||||||
|
" repe cmps byte ptr [rsi], byte ptr [rdi];"
|
||||||
|
" pop rsi;"
|
||||||
|
" pop rdi;"
|
||||||
|
" jl return_negative;"
|
||||||
|
" jg return_positive;"
|
||||||
|
"compare_length:"
|
||||||
|
" cmp qword ptr [rdi + 0x8], rdx;"
|
||||||
|
" ja return_positive;"
|
||||||
|
" jb return_negative;"
|
||||||
|
"return_zero:"
|
||||||
|
" xor eax, eax;"
|
||||||
|
" ret;"
|
||||||
|
"return_positive:"
|
||||||
|
" mov eax, 1;"
|
||||||
|
" ret;"
|
||||||
|
"return_negative:"
|
||||||
|
" mov eax, 0xffffffff;"
|
||||||
|
" ret;"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
_emulator_append_external_api_impl
|
||||||
|
(
|
||||||
|
x64_emulator, "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9push_backEc",
|
||||||
|
x64_assembler.assemble
|
||||||
|
(
|
||||||
|
fmt::format
|
||||||
|
(
|
||||||
|
" push rsi;"
|
||||||
|
" mov rsi, rsp;"
|
||||||
|
" mov rdx, 0x1;"
|
||||||
|
" mov rax, {_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_appendEPKcm:#016x};"
|
||||||
|
" call rax;"
|
||||||
|
" pop rsi;"
|
||||||
|
" ret;",
|
||||||
|
fmt::arg("_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_appendEPKcm", external_api_impl["_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_appendEPKcm"])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
_emulator_append_external_api_impl
|
||||||
|
(
|
||||||
|
x64_emulator, "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE6appendEPKc",
|
||||||
|
x64_assembler.assemble
|
||||||
|
(
|
||||||
|
fmt::format
|
||||||
|
(
|
||||||
|
" push rsi;"
|
||||||
|
" xor rdx, rdx;"
|
||||||
|
" dec rdx;"
|
||||||
|
"strlen_loop:"
|
||||||
|
" inc rdx;"
|
||||||
|
" cmp byte ptr [rsi + rdx], 0x00;"
|
||||||
|
" jne strlen_loop;"
|
||||||
|
" pop rsi;"
|
||||||
|
" mov rax, {_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_appendEPKcm:#016x};"
|
||||||
|
" jmp rax;",
|
||||||
|
fmt::arg("_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_appendEPKcm", external_api_impl["_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE9_M_appendEPKcm"])
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
x64_emulator.hook_add<UC_HOOK_CODE>
|
||||||
|
(std::bind(&patch_solution_since::_emulator_dl_runtime_resolve_handler, this, std::ref(x64_emulator), std::placeholders::_1, std::placeholders::_2), external_api_impl["dl_runtime_resolve"], external_api_impl["dl_runtime_resolve"]);
|
||||||
|
|
||||||
|
x64_emulator.hook_add<UC_HOOK_CODE>
|
||||||
|
(std::bind(&patch_solution_since::_emulator_malloc_handler, this, std::ref(x64_emulator), std::placeholders::_1, std::placeholders::_2), external_api_impl["malloc"], external_api_impl["malloc"]);
|
||||||
|
|
||||||
|
x64_emulator.hook_add<UC_HOOK_CODE>
|
||||||
|
(std::bind(&patch_solution_since::_emulator_free_handler, this, std::ref(x64_emulator), std::placeholders::_1, std::placeholders::_2), external_api_impl["free"], external_api_impl["free"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set page fault handler
|
||||||
|
x64_emulator.hook_add<UC_HOOK_MEM_UNMAPPED>
|
||||||
|
(std::bind(&patch_solution_since::_emulator_page_fault_handler, this, std::ref(x64_emulator), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
|
||||||
|
|
||||||
|
// print all instructions' address
|
||||||
|
// x64_emulator.hook_add<UC_HOOK_CODE>([](uint64_t address, size_t size) { printf("code_trace, address = 0x%016lx\n", address); });
|
||||||
|
|
||||||
|
// set rbp, rsp
|
||||||
|
uint64_t init_rbp = x64_emulator.context_get<uint64_t>("stack_base") - x64_emulator.context_get<size_t>("stack_size") / 4;
|
||||||
|
uint64_t init_rsp = x64_emulator.context_get<uint64_t>("stack_base") - x64_emulator.context_get<size_t>("stack_size") / 2;
|
||||||
|
|
||||||
|
x64_emulator.reg_write(UC_X86_REG_RBP, &init_rbp);
|
||||||
|
x64_emulator.reg_write(UC_X86_REG_RSP, &init_rsp);
|
||||||
|
|
||||||
|
// set return address
|
||||||
|
auto retaddr = x64_emulator.context_get<uint64_t>("dead_address");
|
||||||
|
x64_emulator.mem_write(init_rsp, &retaddr, sizeof(retaddr));
|
||||||
|
|
||||||
|
// set argument registers
|
||||||
|
uint64_t init_rdi = init_rsp + 0x40; // a pointer to stack memory which stores return value
|
||||||
|
x64_emulator.reg_write(UC_X86_REG_RDI, &init_rdi);
|
||||||
|
|
||||||
|
// start emulate
|
||||||
|
try {
|
||||||
|
x64_emulator.emu_start(x64_emulator.context_get<uint64_t>("start_address"), x64_emulator.context_get<uint64_t>("dead_address"));
|
||||||
|
} catch (amd64_emulator::backend_error& e) {
|
||||||
|
printf("[-] patch_solution_since<16, 0, 7, 0>: Code emulation failed. %s\n", e.error_string().c_str());
|
||||||
|
printf("[-] patch_solution_since<16, 0, 7, 0>: This patch solution will be suppressed.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_va_pltgot_std_string_append) {
|
||||||
|
printf("[*] patch_solution_since<16, 0, 7, 0>: m_va_pltgot_std_string_append = 0x%016lx\n", m_va_pltgot_std_string_append);
|
||||||
|
} else {
|
||||||
|
printf("[*] patch_solution_since<16, 0, 7, 0>: std::string::append(const char*) is not found.\n");
|
||||||
|
printf("[-] patch_solution_since<16, 0, 7, 0>: This patch solution will be suppressed.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// get result
|
||||||
|
//
|
||||||
|
// on AMD64 platform, `std::string` has follow memory layout:
|
||||||
|
// ------------------------------
|
||||||
|
// | offset | size |
|
||||||
|
// ------------------------------
|
||||||
|
// | +0 | 0x8 | char*: raw string ptr
|
||||||
|
// ------------------------------
|
||||||
|
// | +0x8 | 0x8 | size_t: string length
|
||||||
|
// ------------------------------
|
||||||
|
// | +0x10 | 0x8 | `char[16]: a small string buffer` OR `size_t: capacity`
|
||||||
|
// ------------------------------
|
||||||
|
//
|
||||||
|
uint64_t encoded_key_length;
|
||||||
|
x64_emulator.mem_read(init_rdi + 0x8, &encoded_key_length, sizeof(encoded_key_length));
|
||||||
|
if (encoded_key_length != official_encoded_key.length()) {
|
||||||
|
printf("[-] patch_solution_since<16, 0, 7, 0>: unexpected encoded key length(%lu).\n", encoded_key_length);
|
||||||
|
printf("[-] patch_solution_since<16, 0, 7, 0>: This patch solution will be suppressed.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t encoded_key_ptr;
|
||||||
|
x64_emulator.mem_read(init_rdi, &encoded_key_ptr, sizeof(encoded_key_ptr));
|
||||||
|
|
||||||
|
auto encoded_key = x64_emulator.mem_read(encoded_key_ptr, encoded_key_length);
|
||||||
|
if (memcmp(encoded_key.data(), official_encoded_key.data(), encoded_key.size()) == 0) {
|
||||||
|
printf("[+] patch_solution_since<16, 0, 7, 0>: official encoded key is found.\n");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
printf("[-] patch_solution_since<16, 0, 7, 0>: official encoded key is not found.\n");
|
||||||
|
printf("[-] patch_solution_since<16, 0, 7, 0>: This patch solution will be suppressed.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool patch_solution_since<16, 0, 7, 0>::check_rsa_privkey(const rsa_cipher& cipher) {
|
||||||
|
return cipher.bits() == 2048;
|
||||||
|
}
|
||||||
|
|
||||||
|
void patch_solution_since<16, 0, 7, 0>::make_patch(const rsa_cipher& cipher) {
|
||||||
|
auto new_encoded_key = _build_encoded_key(cipher);
|
||||||
|
|
||||||
|
auto CSRegistrationInfoFetcher_LINUX_GenerateRegistrationKey =
|
||||||
|
m_libcc_interpreter.convert_va_to_ptr<uint8_t*>(m_va_CSRegistrationInfoFetcher_LINUX_GenerateRegistrationKey);
|
||||||
|
|
||||||
|
std::vector<std::string> patch_code_chunks;
|
||||||
|
patch_code_chunks.emplace_back("push rbp;");
|
||||||
|
patch_code_chunks.emplace_back("mov rbp, rsp;");
|
||||||
|
patch_code_chunks.emplace_back("lea rax, qword ptr [rdi + 0x10];");
|
||||||
|
patch_code_chunks.emplace_back("mov qword ptr [rdi], rax;");
|
||||||
|
patch_code_chunks.emplace_back("mov qword ptr [rdi + 0x8], 0;");
|
||||||
|
{
|
||||||
|
std::vector<uint64_t> push_values((new_encoded_key.length() + 1 + 7) / 8, 0);
|
||||||
|
memcpy(push_values.data(), new_encoded_key.data(), new_encoded_key.length());
|
||||||
|
|
||||||
|
std::for_each
|
||||||
|
(
|
||||||
|
push_values.crbegin(),
|
||||||
|
push_values.crend(),
|
||||||
|
[&patch_code_chunks](uint64_t x) {
|
||||||
|
patch_code_chunks.emplace_back(fmt::format("mov rax, {:#016x};", x));
|
||||||
|
patch_code_chunks.emplace_back("push rax;");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
patch_code_chunks.emplace_back("mov rsi, rsp;");
|
||||||
|
patch_code_chunks.emplace_back(fmt::format("call qword ptr [{:#016x}];", m_va_pltgot_std_string_append));
|
||||||
|
patch_code_chunks.emplace_back("leave;");
|
||||||
|
patch_code_chunks.emplace_back("ret;");
|
||||||
|
|
||||||
|
std::vector<uint8_t> assembled_patch_code;
|
||||||
|
{
|
||||||
|
keystone_assembler x86_assembler{ KS_ARCH_X86, KS_MODE_64 };
|
||||||
|
|
||||||
|
auto current_va = m_va_CSRegistrationInfoFetcher_LINUX_GenerateRegistrationKey;
|
||||||
|
auto next_reloc = m_libcc_interpreter.relocation_distribute().lower_bound(current_va);
|
||||||
|
for (const auto& patch_code_chunk : patch_code_chunks) {
|
||||||
|
auto assembled_patch_code_chunk = x86_assembler.assemble(patch_code_chunk, current_va);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
auto next_reloc_va = next_reloc != m_libcc_interpreter.relocation_distribute().end() ? next_reloc->first : 0xffffffffffffffff;
|
||||||
|
auto next_reloc_size = next_reloc != m_libcc_interpreter.relocation_distribute().end() ? next_reloc->second : 0;
|
||||||
|
|
||||||
|
if (current_va + assembled_patch_code_chunk.size() + 2 <= next_reloc_va) { // 2 -> size of machine code "jmp rel8"
|
||||||
|
assembled_patch_code.insert(assembled_patch_code.end(), assembled_patch_code_chunk.begin(), assembled_patch_code_chunk.end());
|
||||||
|
current_va += assembled_patch_code_chunk.size();
|
||||||
|
break;
|
||||||
|
} else if (current_va + 2 <= next_reloc_va) {
|
||||||
|
auto next_va = next_reloc_va + next_reloc_size;
|
||||||
|
auto assembled_jmp = x86_assembler.assemble(fmt::format("jmp {:#016x};", next_va), current_va);
|
||||||
|
auto assembled_padding = std::vector<uint8_t>(next_va - (current_va + assembled_jmp.size()), 0x90); // 0x90 -> nop
|
||||||
|
assembled_patch_code.insert(assembled_patch_code.end(), assembled_jmp.begin(), assembled_jmp.end());
|
||||||
|
assembled_patch_code.insert(assembled_patch_code.end(), assembled_padding.begin(), assembled_padding.end());
|
||||||
|
current_va = next_va;
|
||||||
|
++next_reloc;
|
||||||
|
} else {
|
||||||
|
__builtin_unreachable(); // impossible to reach here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(CSRegistrationInfoFetcher_LINUX_GenerateRegistrationKey, assembled_patch_code.data(), assembled_patch_code.size());
|
||||||
|
printf("[*] patch_solution_since<16, 0, 7, 0>: Patch has been done.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
48
navicat-patcher/patch_solution_since_16.0.7.0.hpp
Normal file
48
navicat-patcher/patch_solution_since_16.0.7.0.hpp
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include "patch_solution_since.hpp"
|
||||||
|
#include "elf64_interpreter.hpp"
|
||||||
|
#include "amd64_emulator.hpp"
|
||||||
|
|
||||||
|
namespace nkg {
|
||||||
|
|
||||||
|
template<>
|
||||||
|
class patch_solution_since<16, 0, 7, 0> final : public patch_solution {
|
||||||
|
private:
|
||||||
|
static inline std::string_view official_encoded_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889IqdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginva5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOFR0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmtYyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQawIDAQAB";
|
||||||
|
|
||||||
|
elf64_interpreter& m_libcc_interpreter;
|
||||||
|
elf64_interpreter::va_t m_va_CSRegistrationInfoFetcher_LINUX_vtable;
|
||||||
|
elf64_interpreter::va_t m_va_CSRegistrationInfoFetcher_LINUX_GenerateRegistrationKey;
|
||||||
|
elf64_interpreter::va_t m_va_pltgot_std_string_append;
|
||||||
|
|
||||||
|
uint64_t _emulator_append_external_api_impl(amd64_emulator& x64_emulator, std::string_view api_name, const std::vector<uint8_t>& api_impl);
|
||||||
|
|
||||||
|
uint64_t _emulator_malloc(amd64_emulator& x64_emulator, size_t alloc_size);
|
||||||
|
|
||||||
|
void _emulator_free(amd64_emulator& x64_emulator, uint64_t alloc_p);
|
||||||
|
|
||||||
|
bool _emulator_page_fault_handler(amd64_emulator& x64_emulator, uc_mem_type access, uint64_t address, size_t size, int64_t value);
|
||||||
|
|
||||||
|
void _emulator_dl_runtime_resolve_handler(amd64_emulator& x64_emulator, uint64_t address, size_t size);
|
||||||
|
|
||||||
|
void _emulator_malloc_handler(amd64_emulator& x64_emulator, uint64_t address, size_t size);
|
||||||
|
|
||||||
|
void _emulator_free_handler(amd64_emulator& x64_emulator, uint64_t address, size_t size);
|
||||||
|
|
||||||
|
static std::string _build_encoded_key(const rsa_cipher& cipher);
|
||||||
|
|
||||||
|
public:
|
||||||
|
patch_solution_since(elf64_interpreter& libcc_interpreter);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual bool find_patch() override;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
virtual bool check_rsa_privkey(const rsa_cipher& cipher) override;
|
||||||
|
|
||||||
|
virtual void make_patch(const rsa_cipher& cipher) override;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user