Refactor code + Add support for 12.1.15
This commit is contained in:
parent
e4e433ec94
commit
dd6d0713e6
103
navicat-patcher/CapstoneDisassembler.cpp
Normal file
103
navicat-patcher/CapstoneDisassembler.cpp
Normal file
@ -0,0 +1,103 @@
|
||||
#include "CapstoneDisassembler.hpp"
|
||||
|
||||
CapstoneDisassembler::CapstoneDisassembler(const CapstoneEngine& Engine) :
|
||||
_$$_ConstRef_Engine(Engine),
|
||||
_$$_Context(InvalidContext),
|
||||
_$$_InstructionContext(InvalidContext),
|
||||
_$$_Instruction(nullptr)
|
||||
{
|
||||
cs_insn* insn;
|
||||
insn = cs_malloc(_$$_ConstRef_Engine.Handle());
|
||||
if (insn == nullptr)
|
||||
throw CapstoneError(__FILE__, __LINE__, cs_errno(_$$_ConstRef_Engine.Handle()),
|
||||
"cs_malloc fails.");
|
||||
else
|
||||
_$$_InstructionObj.TakeOver(insn);
|
||||
}
|
||||
|
||||
void CapstoneDisassembler::SetContext(const uint8_t* Opcodes, size_t Size, uint64_t Address) noexcept {
|
||||
_$$_Context.Opcodes.ConstPtr = Opcodes;
|
||||
_$$_Context.OpcodesSize = Size;
|
||||
_$$_Context.Address = Address;
|
||||
_$$_InstructionContext = InvalidContext;
|
||||
_$$_Instruction = nullptr;
|
||||
}
|
||||
|
||||
void CapstoneDisassembler::SetContext(const CapstoneDisassembler::Context& Context) noexcept {
|
||||
_$$_Context = Context;
|
||||
_$$_InstructionContext = InvalidContext;
|
||||
_$$_Instruction = nullptr;
|
||||
}
|
||||
|
||||
const CapstoneDisassembler::Context& CapstoneDisassembler::GetContext() const noexcept {
|
||||
return _$$_Context;
|
||||
}
|
||||
|
||||
bool CapstoneDisassembler::Next() noexcept {
|
||||
Context InstructionContext = _$$_Context;
|
||||
bool bSucceed = cs_disasm_iter(_$$_ConstRef_Engine.Handle(),
|
||||
&_$$_Context.Opcodes.ConstPtr,
|
||||
&_$$_Context.OpcodesSize,
|
||||
&_$$_Context.Address,
|
||||
_$$_InstructionObj);
|
||||
if (bSucceed) {
|
||||
_$$_InstructionContext = InstructionContext;
|
||||
if (_$$_Instruction == nullptr)
|
||||
_$$_Instruction = _$$_InstructionObj;
|
||||
} else {
|
||||
_$$_InstructionContext = InvalidContext;
|
||||
_$$_Instruction = nullptr;
|
||||
}
|
||||
return bSucceed;
|
||||
}
|
||||
|
||||
cs_insn* CapstoneDisassembler::GetInstruction() const noexcept {
|
||||
return _$$_Instruction;
|
||||
}
|
||||
|
||||
const CapstoneDisassembler::Context& CapstoneDisassembler::GetInstructionContext() const noexcept {
|
||||
return _$$_InstructionContext;
|
||||
}
|
||||
|
||||
CapstoneDisassembler::~CapstoneDisassembler() {
|
||||
_$$_Context = InvalidContext;
|
||||
_$$_InstructionContext = InvalidContext;
|
||||
_$$_Instruction = nullptr;
|
||||
}
|
||||
|
||||
CapstoneEngine::CapstoneEngine(cs_arch ArchType, cs_mode Mode) {
|
||||
cs_err status;
|
||||
csh handle;
|
||||
|
||||
status = cs_open(ArchType, Mode, &handle);
|
||||
if (status != CS_ERR_OK)
|
||||
throw CapstoneError(__FILE__, __LINE__, status,
|
||||
"cs_open fails.");
|
||||
else
|
||||
_$$_EngineObj.TakeOver(handle);
|
||||
}
|
||||
|
||||
CapstoneEngine::CapstoneEngine(CapstoneEngine&& Other) noexcept :
|
||||
_$$_EngineObj(std::move(Other._$$_EngineObj)) {}
|
||||
|
||||
CapstoneEngine& CapstoneEngine::operator=(CapstoneEngine&& Other) noexcept {
|
||||
_$$_EngineObj = std::move(Other._$$_EngineObj);
|
||||
return *this;
|
||||
}
|
||||
|
||||
csh CapstoneEngine::Handle() const noexcept {
|
||||
return _$$_EngineObj;
|
||||
}
|
||||
|
||||
void CapstoneEngine::Option(cs_opt_type Type, size_t Value) {
|
||||
cs_err status;
|
||||
status = cs_option(_$$_EngineObj, Type, Value);
|
||||
if (status != CS_ERR_OK)
|
||||
throw CapstoneError(__FILE__, __LINE__, status,
|
||||
"cs_open fails.");
|
||||
}
|
||||
|
||||
CapstoneDisassembler CapstoneEngine::CreateDisassembler() const {
|
||||
return CapstoneDisassembler(*this);
|
||||
}
|
||||
|
||||
121
navicat-patcher/CapstoneDisassembler.hpp
Normal file
121
navicat-patcher/CapstoneDisassembler.hpp
Normal file
@ -0,0 +1,121 @@
|
||||
#pragma once
|
||||
#include <capstone/capstone.h>
|
||||
#include "ResourceObject.hpp"
|
||||
#include "Exception.hpp"
|
||||
|
||||
struct CapstoneHandleTraits {
|
||||
using HandleType = csh;
|
||||
static inline const HandleType InvalidValue = 0;
|
||||
static inline void Releasor(HandleType Handle) {
|
||||
cs_close(&Handle);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename __Type>
|
||||
struct CapstoneMallocTraits {
|
||||
using HandleType = __Type*;
|
||||
static inline const HandleType InvalidValue = nullptr;
|
||||
static inline void Releasor(HandleType Handle) {
|
||||
cs_free(Handle, 1);
|
||||
}
|
||||
};
|
||||
|
||||
class CapstoneError : public Exception {
|
||||
private:
|
||||
const cs_err _ErrorCode;
|
||||
public:
|
||||
CapstoneError(const char* FileName,
|
||||
size_t Line,
|
||||
cs_err Code,
|
||||
const char* Message) noexcept :
|
||||
Exception(FileName, Line, Message),
|
||||
_ErrorCode(Code) {}
|
||||
|
||||
virtual bool HasErrorCode() const noexcept override {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual unsigned long ErrorCode() const noexcept override {
|
||||
return _ErrorCode;
|
||||
}
|
||||
|
||||
virtual const char* ErrorString() const noexcept override {
|
||||
return cs_strerror(_ErrorCode);
|
||||
}
|
||||
};
|
||||
|
||||
class CapstoneDisassembler;
|
||||
class CapstoneEngine;
|
||||
|
||||
class CapstoneDisassembler {
|
||||
friend class CapstoneEngine;
|
||||
public:
|
||||
|
||||
struct Context {
|
||||
union {
|
||||
const uint8_t* ConstPtr;
|
||||
uint8_t* Ptr;
|
||||
} Opcodes;
|
||||
size_t OpcodesSize;
|
||||
uint64_t Address;
|
||||
|
||||
bool operator==(const Context& Other) const noexcept {
|
||||
return memcmp(this, &Other, sizeof(Context)) == 0;
|
||||
}
|
||||
|
||||
bool operator!=(const Context& Other) const noexcept {
|
||||
return memcmp(this, &Other, sizeof(Context)) != 0;
|
||||
}
|
||||
};
|
||||
|
||||
static inline Context InvalidContext = {};
|
||||
|
||||
private:
|
||||
const CapstoneEngine& _$$_ConstRef_Engine;
|
||||
ResourceObject<CapstoneMallocTraits<cs_insn>> _$$_InstructionObj;
|
||||
Context _$$_Context;
|
||||
Context _$$_InstructionContext;
|
||||
cs_insn* _$$_Instruction;
|
||||
|
||||
explicit CapstoneDisassembler(const CapstoneEngine& Engine);
|
||||
public:
|
||||
|
||||
void SetContext(const uint8_t* Opcodes,
|
||||
size_t Size,
|
||||
uint64_t Address = 0) noexcept;
|
||||
|
||||
void SetContext(const Context& Context) noexcept;
|
||||
|
||||
const Context& GetContext() const noexcept;
|
||||
|
||||
bool Next() noexcept;
|
||||
|
||||
cs_insn* GetInstruction() const noexcept;
|
||||
|
||||
const Context& GetInstructionContext() const noexcept;
|
||||
|
||||
~CapstoneDisassembler();
|
||||
};
|
||||
|
||||
class CapstoneEngine {
|
||||
private:
|
||||
ResourceObject<CapstoneHandleTraits> _$$_EngineObj;
|
||||
public:
|
||||
|
||||
CapstoneEngine(cs_arch ArchType, cs_mode Mode);
|
||||
|
||||
CapstoneEngine(const CapstoneEngine&) = delete;
|
||||
|
||||
CapstoneEngine(CapstoneEngine&& Other) noexcept;
|
||||
|
||||
CapstoneEngine& operator=(const CapstoneEngine&) = delete;
|
||||
|
||||
CapstoneEngine& operator=(CapstoneEngine&& Other) noexcept;
|
||||
|
||||
csh Handle() const noexcept;
|
||||
|
||||
void Option(cs_opt_type Type, size_t Value);
|
||||
|
||||
CapstoneDisassembler CreateDisassembler() const;
|
||||
};
|
||||
|
||||
68
navicat-patcher/Exception.hpp
Normal file
68
navicat-patcher/Exception.hpp
Normal file
@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include <system_error>
|
||||
|
||||
class Exception {
|
||||
private:
|
||||
const char* const _$$_SourceFile;
|
||||
const size_t _$$_SourceLine;
|
||||
const char* const _$$_CustomMessage;
|
||||
public:
|
||||
|
||||
Exception(const char* FileName, size_t Line, const char* Message) noexcept :
|
||||
_$$_SourceFile(FileName),
|
||||
_$$_SourceLine(Line),
|
||||
_$$_CustomMessage(Message) {}
|
||||
|
||||
const char* SourceFile() const noexcept {
|
||||
return _$$_SourceFile;
|
||||
}
|
||||
|
||||
size_t SourceLine() const noexcept {
|
||||
return _$$_SourceLine;
|
||||
}
|
||||
|
||||
const char* CustomMessage() const noexcept {
|
||||
return _$$_CustomMessage;
|
||||
}
|
||||
|
||||
virtual bool HasErrorCode() const noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual unsigned long ErrorCode() const noexcept {
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual const char* ErrorString() const noexcept {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class SystemError : public Exception {
|
||||
private:
|
||||
const std::error_code _ErrorCode;
|
||||
const std::string _ErrorString;
|
||||
public:
|
||||
SystemError(const char* FileName,
|
||||
size_t Line,
|
||||
int Code,
|
||||
const char* Message) noexcept :
|
||||
Exception(FileName, Line, Message),
|
||||
_ErrorCode(Code, std::system_category()),
|
||||
_ErrorString(_ErrorCode.message()) {}
|
||||
|
||||
virtual bool HasErrorCode() const noexcept override {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual unsigned long ErrorCode() const noexcept override {
|
||||
return _ErrorCode.value();
|
||||
}
|
||||
|
||||
virtual const char* ErrorString() const noexcept override {
|
||||
return _ErrorString.c_str();
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,78 +0,0 @@
|
||||
#pragma once
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
class FileMapper {
|
||||
private:
|
||||
int _fd;
|
||||
size_t _MapSize;
|
||||
void* _pView;
|
||||
public:
|
||||
|
||||
class AlreadyInUseException {};
|
||||
|
||||
FileMapper() noexcept :
|
||||
_fd(-1),
|
||||
_MapSize(static_cast<size_t>(-1)),
|
||||
_pView(reinterpret_cast<void*>(-1)) { }
|
||||
|
||||
void Unmap() noexcept {
|
||||
if (_pView != reinterpret_cast<void*>(-1)) {
|
||||
munmap(_pView, _MapSize);
|
||||
_MapSize = static_cast<size_t>(-1);
|
||||
_pView = reinterpret_cast<void*>(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void Close() noexcept {
|
||||
if (_fd != -1) {
|
||||
close(_fd);
|
||||
_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool OpenFile(const char* FilePath) {
|
||||
if (_fd != -1)
|
||||
throw AlreadyInUseException();
|
||||
_fd = open(FilePath, O_RDWR, S_IRUSR | S_IWUSR);
|
||||
return _fd != -1;
|
||||
}
|
||||
|
||||
bool GetFileSize(off_t& refSize) noexcept {
|
||||
struct stat fd_stat = {};
|
||||
if (fstat(_fd, &fd_stat) != 0) {
|
||||
return false;
|
||||
} else {
|
||||
refSize = fd_stat.st_size;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Map(size_t Size) {
|
||||
if (_pView != reinterpret_cast<void*>(-1))
|
||||
throw AlreadyInUseException();
|
||||
|
||||
_pView = mmap(nullptr, Size, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0);
|
||||
|
||||
if (_pView != reinterpret_cast<void*>(-1)) {
|
||||
_MapSize = Size;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename _Type>
|
||||
_Type* GetView() const noexcept {
|
||||
return reinterpret_cast<_Type*>(_pView);
|
||||
}
|
||||
|
||||
~FileMapper() {
|
||||
Unmap();
|
||||
Close();
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,97 +0,0 @@
|
||||
#include "Helper.hpp"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <signal.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
namespace Helper {
|
||||
|
||||
static jmp_buf env;
|
||||
|
||||
static void SIGSEGV_Handler(int sig) {
|
||||
siglongjmp(env, 1);
|
||||
}
|
||||
|
||||
//
|
||||
// read byte(s) at address `p` as _Type to `out`
|
||||
// succeed if return true, otherwise return false
|
||||
//
|
||||
template<typename _Type>
|
||||
static inline bool ProbeForRead(const void* p, void* out) {
|
||||
int r = sigsetjmp(env, 1);
|
||||
if (r == 0) {
|
||||
*reinterpret_cast<_Type*>(out) = *reinterpret_cast<const _Type*>(p);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Print memory data in [from, to) at least
|
||||
// If `base` is not nullptr, print address as offset. Otherwise, as absolute address.
|
||||
// NOTICE:
|
||||
// `base` must >= `from`
|
||||
//
|
||||
void PrintMemory(const void* from, const void* to, const void* base) {
|
||||
const uint8_t* start = reinterpret_cast<const uint8_t*>(from);
|
||||
const uint8_t* end = reinterpret_cast<const uint8_t*>(to);
|
||||
const uint8_t* base_ptr = reinterpret_cast<const uint8_t*>(base);
|
||||
|
||||
if (start >= end)
|
||||
return;
|
||||
|
||||
while (reinterpret_cast<uintptr_t>(start) % 16)
|
||||
start--;
|
||||
|
||||
while (reinterpret_cast<uintptr_t>(start) % 16)
|
||||
end++;
|
||||
|
||||
void (*prev_handler)(int) = signal(SIGSEGV, SIGSEGV_Handler);
|
||||
while (start < end) {
|
||||
uint16_t value[16] = {};
|
||||
|
||||
if (base_ptr)
|
||||
printf("+0x%p ", reinterpret_cast<const void*>(start - base_ptr));
|
||||
else
|
||||
printf("0x%p ", start);
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
if (ProbeForRead<uint8_t>(start + i, value + i)) {
|
||||
printf("%02x ", value[i]);
|
||||
} else {
|
||||
value[i] = 0xffff;
|
||||
printf("?? ");
|
||||
}
|
||||
}
|
||||
|
||||
printf(" ");
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
if (value[i] < 0x20) {
|
||||
printf(".");
|
||||
} else if (value[i] > 0x7e) {
|
||||
printf(".");
|
||||
} else {
|
||||
printf("%c", value[i]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
start += 0x10;
|
||||
}
|
||||
signal(SIGSEGV, prev_handler);
|
||||
}
|
||||
|
||||
void ErrorReport(const char* at, size_t line, const char* msg) {
|
||||
printf("ERROR: @%s LINE: %zu\n", at, line);
|
||||
printf("%s\n", msg);
|
||||
}
|
||||
|
||||
void ErrorReport(const char* at, size_t line, const char* msg, int err_code) {
|
||||
printf("ERROR: @%s LINE: %zu\n", at, line);
|
||||
printf("%s CODE: 0x%08x\n", msg, err_code);
|
||||
printf("REASON: %s\n", strerror(err_code));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
namespace Helper {
|
||||
|
||||
//
|
||||
// Print memory data in [from, to) at least
|
||||
// If `base` is not nullptr, print address as offset. Otherwise, as absolute address.
|
||||
// NOTICE:
|
||||
// `base` must >= `from`
|
||||
//
|
||||
void PrintMemory(const void* from, const void* to, const void* base);
|
||||
|
||||
void ErrorReport(const char* at, size_t line, const char* msg);
|
||||
void ErrorReport(const char* at, size_t line, const char* msg, int err_code);
|
||||
|
||||
template<typename _Type>
|
||||
struct ResourceGuard {
|
||||
_Type* ptr;
|
||||
|
||||
explicit ResourceGuard(_Type* p) noexcept : ptr(p) {}
|
||||
|
||||
~ResourceGuard() {
|
||||
if (ptr) {
|
||||
delete ptr;
|
||||
ptr = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#define REPORT_ERROR(msg) Helper::ErrorReport(__FUNCTION__, __LINE__, msg)
|
||||
#define REPORT_ERROR_WITH_CODE(msg) Helper::ErrorReport(__FUNCTION__, __LINE__, msg, errno)
|
||||
#define PRINT_MESSAGE(msg) puts(msg)
|
||||
68
navicat-patcher/KeystoneAssembler.cpp
Normal file
68
navicat-patcher/KeystoneAssembler.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
#include "KeystoneAssembler.hpp"
|
||||
|
||||
KeystoneAssembler::KeystoneAssembler(const KeystoneEngine& Engine) :
|
||||
_$$_ConstRef_Engine(Engine) {}
|
||||
|
||||
std::vector<uint8_t> KeystoneAssembler::OpCodes(const char* AssemblyCode, uint64_t Address) const {
|
||||
std::vector<uint8_t> result;
|
||||
uint8_t* op_codes = nullptr;
|
||||
size_t op_codes_size = 0;
|
||||
size_t count;
|
||||
|
||||
if (ks_asm(_$$_ConstRef_Engine.Handle(),
|
||||
AssemblyCode,
|
||||
Address,
|
||||
&op_codes,
|
||||
&op_codes_size,
|
||||
&count) != 0) {
|
||||
throw KeystoneError(__FILE__, __LINE__, ks_errno(_$$_ConstRef_Engine.Handle()),
|
||||
"ks_asm fails.");
|
||||
}
|
||||
|
||||
result.assign(op_codes, op_codes + op_codes_size);
|
||||
|
||||
ks_free(op_codes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> KeystoneAssembler::OpCodes(const std::string& AssemblyCode, uint64_t Address) const {
|
||||
return OpCodes(AssemblyCode.c_str(), Address);
|
||||
}
|
||||
|
||||
KeystoneEngine::KeystoneEngine(ks_arch ArchType, ks_mode Mode) {
|
||||
ks_err status;
|
||||
ks_engine* handle = nullptr;
|
||||
|
||||
status = ks_open(ArchType, Mode, &handle);
|
||||
if (status != KS_ERR_OK)
|
||||
throw KeystoneError(__FILE__, __LINE__, status,
|
||||
"ks_open fails.");
|
||||
else
|
||||
_$$_EngineObj.TakeOver(handle);
|
||||
}
|
||||
|
||||
KeystoneEngine::KeystoneEngine(KeystoneEngine&& Other) noexcept :
|
||||
_$$_EngineObj(std::move(Other._$$_EngineObj)) {}
|
||||
|
||||
KeystoneEngine& KeystoneEngine::operator=(KeystoneEngine&& Other) noexcept {
|
||||
_$$_EngineObj = std::move(Other._$$_EngineObj);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ks_engine* KeystoneEngine::Handle() const noexcept {
|
||||
return _$$_EngineObj;
|
||||
}
|
||||
|
||||
void KeystoneEngine::Option(ks_opt_type Type, size_t Value) {
|
||||
ks_err status;
|
||||
status = ks_option(_$$_EngineObj, Type, Value);
|
||||
if (status != KS_ERR_OK)
|
||||
throw KeystoneError(__FILE__, __LINE__, status,
|
||||
"ks_open fails.");
|
||||
}
|
||||
|
||||
KeystoneAssembler KeystoneEngine::CreateAssembler() const {
|
||||
return KeystoneAssembler(*this);
|
||||
}
|
||||
|
||||
74
navicat-patcher/KeystoneAssembler.hpp
Normal file
74
navicat-patcher/KeystoneAssembler.hpp
Normal file
@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
#include <keystone/keystone.h>
|
||||
#include "ResourceObject.hpp"
|
||||
#include "Exception.hpp"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
struct KeystoneHandleTraits {
|
||||
using HandleType = ks_engine*;
|
||||
static inline const HandleType InvalidValue = nullptr;
|
||||
static constexpr auto& Releasor = ks_close;
|
||||
};
|
||||
|
||||
class KeystoneError : public Exception {
|
||||
private:
|
||||
const ks_err _ErrorCode;
|
||||
public:
|
||||
|
||||
KeystoneError(const char* FileName, size_t Line, ks_err Code, const char* Message) noexcept :
|
||||
Exception(FileName, Line, Message),
|
||||
_ErrorCode(Code) {}
|
||||
|
||||
virtual bool HasErrorCode() const noexcept override {
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual unsigned long ErrorCode() const noexcept override {
|
||||
return _ErrorCode;
|
||||
}
|
||||
|
||||
virtual const char* ErrorString() const noexcept override {
|
||||
return ks_strerror(_ErrorCode);
|
||||
}
|
||||
};
|
||||
|
||||
class KeystoneAssembler;
|
||||
class KeystoneEngine;
|
||||
|
||||
class KeystoneAssembler {
|
||||
friend class KeystoneEngine;
|
||||
private:
|
||||
const KeystoneEngine& _$$_ConstRef_Engine;
|
||||
|
||||
explicit KeystoneAssembler(const KeystoneEngine& Engine);
|
||||
public:
|
||||
|
||||
std::vector<uint8_t> OpCodes(const char* AssemblyCode, uint64_t Address = 0) const;
|
||||
|
||||
std::vector<uint8_t> OpCodes(const std::string& AssemblyCode, uint64_t Address = 0) const;
|
||||
|
||||
};
|
||||
|
||||
class KeystoneEngine {
|
||||
private:
|
||||
ResourceObject<KeystoneHandleTraits> _$$_EngineObj;
|
||||
public:
|
||||
|
||||
KeystoneEngine(ks_arch Arch, ks_mode Mode);
|
||||
|
||||
KeystoneEngine(const KeystoneEngine&) = delete;
|
||||
|
||||
KeystoneEngine(KeystoneEngine&& Other) noexcept;
|
||||
|
||||
KeystoneEngine& operator=(const KeystoneEngine&) = delete;
|
||||
|
||||
KeystoneEngine& operator=(KeystoneEngine&& Other) noexcept;
|
||||
|
||||
ks_engine* Handle() const noexcept;
|
||||
|
||||
void Option(ks_opt_type Type, size_t Value);
|
||||
|
||||
KeystoneAssembler CreateAssembler() const;
|
||||
};
|
||||
|
||||
96
navicat-patcher/PatchSolution0.cpp
Normal file
96
navicat-patcher/PatchSolution0.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
#include "PatchSolutions.hpp"
|
||||
#include <assert.h>
|
||||
|
||||
const char PatchSolution0::Keyword[452] =
|
||||
"-----BEGIN PUBLIC KEY-----\x00"
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I\x00"
|
||||
"qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv\x00"
|
||||
"a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF\x00"
|
||||
"R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2\x00"
|
||||
"WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt\x00"
|
||||
"YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ\x00"
|
||||
"awIDAQAB\x00"
|
||||
"-----END PUBLIC KEY-----\x00";
|
||||
|
||||
PatchSolution0::PatchSolution0() noexcept :
|
||||
_$$_FileViewHandle(MapViewTraits::InvalidValue),
|
||||
_$$_PatchOffset(InvalidOffset) {}
|
||||
|
||||
void PatchSolution0::SetFile(const MapViewTraits::HandleType& FileViewHandle) noexcept {
|
||||
_$$_FileViewHandle = FileViewHandle;
|
||||
_$$_PatchOffset = InvalidOffset;
|
||||
}
|
||||
|
||||
bool PatchSolution0::FindPatchOffset() noexcept {
|
||||
if (_$$_FileViewHandle == MapViewTraits::InvalidValue)
|
||||
return false;
|
||||
|
||||
auto ViewPtr = _$$_FileViewHandle.ConstView<uint8_t>();
|
||||
size_t ViewSize = _$$_FileViewHandle.Size();
|
||||
|
||||
if (ViewSize < KeywordLength)
|
||||
return false;
|
||||
|
||||
_$$_PatchOffset = InvalidOffset;
|
||||
|
||||
ViewSize -= KeywordLength;
|
||||
for (size_t i = 0; i < ViewSize; ++i) {
|
||||
if (ViewPtr[i] == Keyword[0] && memcmp(ViewPtr + i, Keyword, KeywordLength) == 0) {
|
||||
_$$_PatchOffset = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_$$_PatchOffset != InvalidOffset) {
|
||||
printf("PatchSolution0 ...... Ready to apply.\n");
|
||||
printf(" Info: Keyword offset = +0x%08zx\n", _$$_PatchOffset);
|
||||
return true;
|
||||
} else {
|
||||
printf("PatchSolution0 ...... Omitted.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// PatchSolution0 only requires a 2048-bit RSA key
|
||||
bool PatchSolution0::CheckKey(RSACipher* pCipher) const {
|
||||
std::string PublicKeyPEM =
|
||||
pCipher->ExportKeyString<RSAKeyType::PublicKey, RSAKeyFormat::PEM>();
|
||||
return PublicKeyPEM.length() == KeywordLength;
|
||||
}
|
||||
|
||||
void PatchSolution0::MakePatch(RSACipher* pCipher) const {
|
||||
if (_$$_FileViewHandle == MapViewTraits::InvalidValue || _$$_PatchOffset == InvalidOffset)
|
||||
throw Exception(__FILE__, __LINE__,
|
||||
"PatchSolution0::MakePatch is not ready.");
|
||||
|
||||
uint8_t* ViewPtr = _$$_FileViewHandle.View<uint8_t>();
|
||||
std::string PublicKeyPEM =
|
||||
pCipher->ExportKeyString<RSAKeyType::PublicKey, RSAKeyFormat::PEM>();
|
||||
|
||||
for (size_t i = 0; i < PublicKeyPEM.length(); ++i) {
|
||||
if (PublicKeyPEM[i] == '\n')
|
||||
PublicKeyPEM[i] = '\x00';
|
||||
}
|
||||
|
||||
assert(PublicKeyPEM.length() == KeywordLength);
|
||||
|
||||
puts("****************************");
|
||||
puts("* Begin PatchSolution0 *");
|
||||
puts("****************************");
|
||||
printf("@+0x%08zx\n", _$$_PatchOffset);
|
||||
puts("Previous:");
|
||||
PrintMemory(ViewPtr + _$$_PatchOffset,
|
||||
ViewPtr + _$$_PatchOffset + KeywordLength,
|
||||
ViewPtr);
|
||||
|
||||
memcpy(ViewPtr + _$$_PatchOffset,
|
||||
PublicKeyPEM.c_str(),
|
||||
KeywordLength);
|
||||
|
||||
puts("After:");
|
||||
PrintMemory(ViewPtr + _$$_PatchOffset,
|
||||
ViewPtr + _$$_PatchOffset + KeywordLength,
|
||||
ViewPtr);
|
||||
puts("");
|
||||
}
|
||||
|
||||
136
navicat-patcher/PatchSolution1.cpp
Normal file
136
navicat-patcher/PatchSolution1.cpp
Normal file
@ -0,0 +1,136 @@
|
||||
#include "PatchSolutions.hpp"
|
||||
#include <assert.h>
|
||||
|
||||
const uint8_t PatchSolution1::Keyword[KeywordLength] = {
|
||||
0xfe, 0xfd, 0xfc, 0xf4, 0xfe, 0xd2, 0xf8, 0xf4, 0xf1, 0xd3, 0xde, 0xc7, 0xdf, 0xd3, 0xd0, 0xfd,
|
||||
0x8a, 0xc3, 0x85, 0xf4, 0xf6, 0xe9, 0xfc, 0xfc, 0xf2, 0xf5, 0xfa, 0xf5, 0xf6, 0xe9, 0x81, 0xfb,
|
||||
0xfe, 0xfd, 0xfc, 0xf4, 0xf4, 0xdf, 0xf2, 0xf9, 0xf2, 0xe5, 0xf0, 0xf7, 0xc0, 0x89, 0xdd, 0xcb,
|
||||
0xf5, 0x87, 0xe6, 0xdd, 0xf4, 0xd9, 0xf8, 0xfb, 0xde, 0xf9, 0xcf, 0xc5, 0x8f, 0x80, 0x80, 0xf3,
|
||||
0xc2, 0xd0, 0xe2, 0x8f, 0xfa, 0x8a, 0xdd, 0xf3, 0xd7, 0xdc, 0x86, 0xdc, 0xf0, 0x81, 0xc0, 0xea,
|
||||
0xd0, 0xd9, 0xf9, 0xd8, 0xda, 0xf2, 0xd0, 0xfd, 0xc3, 0xf6, 0xf3, 0x82, 0xf2, 0x81, 0xef, 0xf2,
|
||||
0xe0, 0xf9, 0xf2, 0xd3, 0x8f, 0xd7, 0xe9, 0xfb, 0xca, 0x86, 0xde, 0xfc, 0xf3, 0xd5, 0xdd, 0xf4,
|
||||
0xc7, 0x80, 0xf7, 0xd5, 0xf2, 0xc1, 0xde, 0xcc, 0xc0, 0xc7, 0xf0, 0xd0, 0xd0, 0xd1, 0xd7, 0xcc,
|
||||
0xd2, 0x81, 0xc1, 0x83, 0xdd, 0xd5, 0x8a, 0x8f, 0x81, 0xe1, 0xf4, 0xd9, 0xf3, 0xd7, 0xca, 0xef,
|
||||
0xf9, 0xdf, 0xe1, 0xee, 0xf0, 0xe9, 0xd1, 0xca, 0xf2, 0xe3, 0xf8, 0xf0, 0x83, 0xde, 0xfb, 0xd7,
|
||||
0xf1, 0xc4, 0xfa, 0x85, 0xf2, 0xdd, 0xdd, 0xfd, 0x85, 0x86, 0xc7, 0xf9, 0xc4, 0xc9, 0xf4, 0xf8,
|
||||
0xd4, 0xd9, 0xe6, 0xd2, 0xf6, 0xc1, 0xc1, 0xf9, 0xe0, 0xe4, 0xf7, 0xe4, 0xfd, 0xf1, 0xf6, 0xfc,
|
||||
0xe1, 0x84, 0xe4, 0xd1, 0xed, 0xfe, 0xdb, 0xe8, 0xdd, 0xe1, 0x85, 0xd0, 0xc5, 0xd2, 0x8a, 0x8e,
|
||||
0xd5, 0xdd, 0xe3, 0xdb, 0xd0, 0xe1, 0xd0, 0xf6, 0xc6, 0xee, 0xe6, 0xf7, 0xda, 0xf1, 0xdb, 0xc9,
|
||||
0x8b, 0xee, 0xcd, 0xdf, 0xff, 0xe8, 0xdd, 0xca, 0x82, 0xdb, 0xf1, 0x82, 0xc3, 0xed, 0xc9, 0xcc,
|
||||
0xc0, 0xf2, 0xd6, 0xdf, 0x83, 0xe9, 0xf3, 0xce, 0xea, 0xfa, 0xdf, 0xf8, 0xd9, 0xff, 0xec, 0x88,
|
||||
0xe4, 0xe4, 0xfd, 0x80, 0xc5, 0xce, 0xfa, 0xd2, 0xf4, 0xd8, 0x84, 0xff, 0xe5, 0xf3, 0xcb, 0xc2,
|
||||
0xfe, 0xc0, 0xc4, 0xfa, 0xde, 0xdd, 0xd5, 0xc9, 0xc5, 0xd5, 0xdf, 0xe3, 0xdd, 0xc1, 0xcb, 0xdd,
|
||||
0xfc, 0xf7, 0x83, 0xf8, 0xda, 0xc1, 0xd4, 0xe3, 0xfe, 0xc2, 0xef, 0xf8, 0xf2, 0xea, 0x8a, 0xd2,
|
||||
0xc7, 0xf2, 0xf0, 0xc2, 0xfb, 0x89, 0xdc, 0xeb, 0xd1, 0xf7, 0xcc, 0xe2, 0xd1, 0xfc, 0xd4, 0xce,
|
||||
0xea, 0xcd, 0xe4, 0x87, 0xe0, 0xcc, 0x8d, 0xf5, 0xc7, 0x85, 0x87, 0xda, 0xcf, 0xde, 0x89, 0xcd,
|
||||
0xe5, 0xfd, 0xe7, 0x83, 0xda, 0xdb, 0xfe, 0xf4, 0x84, 0xec, 0xf6, 0xee, 0xfd, 0xea, 0xf1, 0xf5,
|
||||
0xf5, 0xfc, 0xe6, 0xd0, 0x86, 0xdf, 0xc3, 0xe2, 0xe4, 0xd5, 0xd7, 0xe4, 0xe4, 0xce, 0xd4, 0xce,
|
||||
0x82, 0xda, 0xc7, 0xda, 0x80, 0xcb, 0xee, 0x8c, 0xd0, 0xde, 0xcd, 0xda, 0xdd, 0xcd, 0xcc, 0xeb,
|
||||
0xd2, 0xc3, 0xfc, 0xf2, 0xf6, 0xe9, 0xf8, 0xf8
|
||||
};
|
||||
|
||||
PatchSolution1::PatchSolution1() noexcept :
|
||||
_$$_FileViewHandle(MapViewTraits::InvalidValue),
|
||||
_$$_PatchOffset(InvalidOffset) {}
|
||||
|
||||
void PatchSolution1::SetFile(const MapViewTraits::HandleType& FileViewHandle) noexcept {
|
||||
_$$_FileViewHandle = FileViewHandle;
|
||||
_$$_PatchOffset = InvalidOffset;
|
||||
}
|
||||
|
||||
bool PatchSolution1::FindPatchOffset() noexcept {
|
||||
if (_$$_FileViewHandle == MapViewTraits::InvalidValue)
|
||||
return false;
|
||||
|
||||
auto ViewPtr = _$$_FileViewHandle.ConstView<uint8_t>();
|
||||
size_t ViewSize = _$$_FileViewHandle.Size();
|
||||
|
||||
if (ViewSize < KeywordLength)
|
||||
return false;
|
||||
|
||||
_$$_PatchOffset = InvalidOffset;
|
||||
|
||||
ViewSize -= KeywordLength;
|
||||
for (size_t i = 0; i < ViewSize; ++i) {
|
||||
if (ViewPtr[i] == Keyword[0] && memcmp(ViewPtr + i, Keyword, KeywordLength) == 0) {
|
||||
_$$_PatchOffset = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_$$_PatchOffset != InvalidOffset) {
|
||||
printf("PatchSolution1 ...... Ready to apply.\n");
|
||||
printf(" Info: Keyword offset = +0x%08zx\n", _$$_PatchOffset);
|
||||
return true;
|
||||
} else {
|
||||
printf("PatchSolution1 ...... Omitted.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool PatchSolution1::CheckKey(RSACipher* pCipher) const {
|
||||
std::string PublicKeyPEM =
|
||||
pCipher->ExportKeyString<RSAKeyType::PublicKey, RSAKeyFormat::PEM>();
|
||||
|
||||
PublicKeyPEM.erase(PublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"), 26);
|
||||
PublicKeyPEM.erase(PublicKeyPEM.find("-----END PUBLIC KEY-----"), 24);
|
||||
{
|
||||
std::string::size_type pos = 0;
|
||||
while ((pos = PublicKeyPEM.find('\n', pos)) != std::string::npos) {
|
||||
PublicKeyPEM.erase(pos, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return PublicKeyPEM.length() == KeywordLength;
|
||||
}
|
||||
|
||||
void PatchSolution1::MakePatch(RSACipher* pCipher) const {
|
||||
if (_$$_FileViewHandle == MapViewTraits::InvalidValue || _$$_PatchOffset == InvalidOffset)
|
||||
throw Exception(__FILE__, __LINE__,
|
||||
"PatchSolution1::MakePatch is not ready.");
|
||||
|
||||
auto ViewPtr = _$$_FileViewHandle.View<uint8_t>();
|
||||
std::string PublicKeyPEM =
|
||||
pCipher->ExportKeyString<RSAKeyType::PublicKey, RSAKeyFormat::PEM>();
|
||||
|
||||
PublicKeyPEM.erase(PublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"), 26);
|
||||
PublicKeyPEM.erase(PublicKeyPEM.find("-----END PUBLIC KEY-----"), 24);
|
||||
{
|
||||
std::string::size_type pos = 0;
|
||||
while ((pos = PublicKeyPEM.find('\n', pos)) != std::string::npos) {
|
||||
PublicKeyPEM.erase(pos, 1);
|
||||
}
|
||||
}
|
||||
|
||||
assert(PublicKeyPEM.length() == KeywordLength);
|
||||
|
||||
{
|
||||
uint8_t key = 8;
|
||||
for (size_t i = 0; i < PublicKeyPEM.length(); ++i) {
|
||||
if (key == 0)
|
||||
key = 8;
|
||||
PublicKeyPEM[i] ^= 0xbb - key;
|
||||
--key;
|
||||
}
|
||||
}
|
||||
|
||||
puts("****************************");
|
||||
puts("* Begin PatchSolution1 *");
|
||||
puts("****************************");
|
||||
printf("@+0x%08zx\n", _$$_PatchOffset);
|
||||
puts("Previous:");
|
||||
PrintMemory(ViewPtr + _$$_PatchOffset,
|
||||
ViewPtr + _$$_PatchOffset + KeywordLength,
|
||||
ViewPtr);
|
||||
|
||||
memcpy(ViewPtr + _$$_PatchOffset,
|
||||
PublicKeyPEM.c_str(),
|
||||
KeywordLength);
|
||||
|
||||
puts("After:");
|
||||
PrintMemory(ViewPtr + _$$_PatchOffset,
|
||||
ViewPtr + _$$_PatchOffset + KeywordLength,
|
||||
ViewPtr);
|
||||
puts("");
|
||||
}
|
||||
|
||||
354
navicat-patcher/PatchSolution2.cpp
Normal file
354
navicat-patcher/PatchSolution2.cpp
Normal file
@ -0,0 +1,354 @@
|
||||
#include "PatchSolutions.hpp"
|
||||
#include "CapstoneDisassembler.hpp"
|
||||
|
||||
const char PatchSolution2::Keyword[1114] =
|
||||
"BIjWyoeRR0NBgkqnDZWxCgKCEAw1dqF3DTvOB91ZHwecJYFrdM1KEh"
|
||||
"1yVeRoGqSdLLGZGUlngig3OD5mMzs889IqWqqfHSeHMvzyg1p6UPCY"
|
||||
"nesxa9M2dDUrXHomRHOFHSfsbSXRFwt5GivtnJG9lLJHZ7XWeIQABi"
|
||||
"dKionYD3O6c9tvUAoDosUJAdQ1RaSXTzyETbHTRtnTPeLpO3EedGMs"
|
||||
"v3jG9yPcmmdYkddSeJRwn2raPJmnvdHScHUACw0sUNuosAqPaQbTQN"
|
||||
"PATDzcrnd1Sf8RIbUp4MQJFVJugPLVZbP53Gjtyyniqe5q75kva8Qm"
|
||||
"Hr1uOuXkVppe3cwECaGamupG43L1XfcpRjCMrxRep3s2VlbL01xmfz"
|
||||
"5cIhrj34iVmgZSAmIb8ZxiHPdp1oDMFkbNetZyWegqjAHQQ9eoSOTD"
|
||||
"bERbKEwZ5FLeLsbNAxfqsapB1XBvCavFHualx6bxVxuRQceh4z8kaZ"
|
||||
"iv2pOKbZQSJ2Dx5HEq0bYZ6y6b7sN9IaeDFNQwjzQn1K7k3XlYAPWC"
|
||||
"IvDe8Ln0FUe4yMNmuUhu5RTjxE05hUqtz1HjJvYQ9Es1VA6LflKQ87"
|
||||
"TwIXBNvfrcHaZ72QM4dQtDUyEMrLgMDkJBDM9wqIDps65gSlAz6eHD"
|
||||
"8tYWUttrWose0cH0yykVnqFzPtdRiZyZRfio6lGyK48mIC9z7T6MN3"
|
||||
"a7OaLZHZSwzcpQLcGi7M9q1wXLq4Ms1UvlwntB9FLHc63tHPpG8rhn"
|
||||
"XhZIk4QrSm4GYuEKQVHwku6ulw6wfggVL8FZPhoPCGsrb2rQGurBUL"
|
||||
"3lkVJ6RO9VGHcczDYomXqAJqlt4y9pkQIj9kgwTrxTzEZgMGdYZqsV"
|
||||
"4Bd5JjtrL7u3LA0N2Hq9Xvmmis2jDVhSQoUoGukNIoqng3SBsf0E7b"
|
||||
"4W0S1aZSSOJ90nQHQkQShE9YIMDBbNwIg2ncthwADYqibYUgIvJcK9"
|
||||
"89XHnYmZsdMWtt53lICsXE1vztR5WrQjSw4WXDiB31LXTrvudCB6vw"
|
||||
"kCQa4leutETpKLJ2bYaOYBdoiBFOwvf36YaSuRoY4SP2x1pWOwGFTg"
|
||||
"d90J2uYyCqUa3Q3iX52iigT4EKL2vJKdJ";
|
||||
|
||||
const uint8_t PatchSolution2::FunctionBeginByte[13] = {
|
||||
0x55, // push rbp
|
||||
0x48, 0x89, 0xe5, // mov rbp, rsp
|
||||
0x41, 0x57, // push r15
|
||||
0x41, 0x56, // push r14
|
||||
0x53, // push rbx
|
||||
0x48, 0x83, 0xec, 0x48 // sub rsp, 0x48
|
||||
};
|
||||
|
||||
const uint8_t PatchSolution2::FunctionHint[4] = {
|
||||
0x64, 0x77, 0x4b, 0x42
|
||||
};
|
||||
|
||||
bool PatchSolution2::IsStubHelperResolvedTo(const uint8_t* StubHelperProc, const char* Symbol) const {
|
||||
CapstoneDisassembler Disassembler = _$$_CapstoneEngine.CreateDisassembler();
|
||||
Disassembler.SetContext(StubHelperProc, 10);
|
||||
|
||||
if (!Disassembler.Next())
|
||||
return false;
|
||||
|
||||
cs_insn* ins = Disassembler.GetInstruction();
|
||||
|
||||
//
|
||||
// A stub helper proc must look like:
|
||||
// push xxxxxx; (xxxxx is a imm value)
|
||||
// jmp loc_xxxxx
|
||||
//
|
||||
if (strcasecmp(ins->mnemonic, "push") != 0 || ins->detail->x86.operands[0].type != X86_OP_IMM)
|
||||
return false;
|
||||
|
||||
auto bind_opcodes_offset =
|
||||
static_cast<uint32_t>(ins->detail->x86.operands[0].imm);
|
||||
auto bind_opcodes_ptr =
|
||||
_$$_FileViewHandle.ConstViewAtOffset<uint8_t>(
|
||||
_$$_ImageInterpreter.DyldInfoCommand()->lazy_bind_off + bind_opcodes_offset
|
||||
);
|
||||
|
||||
while ((*bind_opcodes_ptr & BIND_OPCODE_MASK) != BIND_OPCODE_DONE) {
|
||||
switch (*bind_opcodes_ptr & BIND_OPCODE_MASK) {
|
||||
case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: // 0x10
|
||||
case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: // 0x30
|
||||
case BIND_OPCODE_SET_TYPE_IMM: // 0x50
|
||||
case BIND_OPCODE_DO_BIND: // 0x90
|
||||
case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: // 0xB0
|
||||
++bind_opcodes_ptr;
|
||||
break;
|
||||
case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: // 0x20
|
||||
case BIND_OPCODE_SET_ADDEND_SLEB: // 0x60
|
||||
case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB: // 0x70
|
||||
case BIND_OPCODE_ADD_ADDR_ULEB: // 0x80
|
||||
case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: // 0xA0
|
||||
while(*(++bind_opcodes_ptr) & 0x80);
|
||||
++bind_opcodes_ptr;
|
||||
break;
|
||||
case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: // 0x40
|
||||
return strcmp(reinterpret_cast<const char*>(bind_opcodes_ptr + 1), Symbol) == 0;
|
||||
case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: // 0xC0
|
||||
//
|
||||
// This opcode is too rare to appear,
|
||||
// It is okay to dismiss this opcode
|
||||
//
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
PatchSolution2::PatchSolution2() :
|
||||
_$$_FileViewHandle(MapViewTraits::InvalidValue),
|
||||
_$$_CapstoneEngine(CS_ARCH_X86, CS_MODE_64),
|
||||
_$$_KeystoneEngine(KS_ARCH_X86, KS_MODE_64),
|
||||
_$$_FunctionOffset(InvalidOffset),
|
||||
_$$_KeywordOffset(InvalidOffset),
|
||||
_$$_std_string_append_stub_Offset(InvalidOffset)
|
||||
{
|
||||
_$$_CapstoneEngine.Option(CS_OPT_DETAIL, CS_OPT_ON);
|
||||
}
|
||||
|
||||
void PatchSolution2::SetFile(const MapViewTraits::HandleType& FileViewHandle) noexcept {
|
||||
_$$_FileViewHandle = FileViewHandle;
|
||||
_$$_ImageInterpreter.LoadImage(FileViewHandle.View<void>());
|
||||
_$$_FunctionOffset = InvalidOffset;
|
||||
_$$_KeywordOffset = InvalidOffset;
|
||||
_$$_std_string_append_stub_Offset = InvalidOffset;
|
||||
}
|
||||
|
||||
bool PatchSolution2::FindPatchOffset() noexcept {
|
||||
if (_$$_FileViewHandle == MapViewTraits::InvalidValue)
|
||||
return false;
|
||||
|
||||
size_t FunctionOffset = InvalidOffset;
|
||||
size_t KeywordOffset = InvalidOffset;
|
||||
size_t std_string_append_stub_Offset = InvalidOffset;
|
||||
|
||||
auto Sec__text = _$$_ImageInterpreter.SectionByName("__TEXT", "__text");
|
||||
auto Sec__const = _$$_ImageInterpreter.SectionByName("__TEXT", "__const");
|
||||
auto Sec__stubs = _$$_ImageInterpreter.SectionByName("__TEXT", "__stubs");
|
||||
if (Sec__text == nullptr || Sec__const == nullptr || Sec__stubs == nullptr)
|
||||
return false;
|
||||
auto SecView__text = _$$_FileViewHandle.ConstView<uint8_t>() + Sec__text->offset;
|
||||
auto SecView__const = _$$_FileViewHandle.ConstView<uint8_t>() + Sec__const->offset;
|
||||
auto SecView__stubs = _$$_FileViewHandle.ConstView<uint8_t>() + Sec__stubs->offset;
|
||||
|
||||
for (uint64_t i = 0; i < Sec__text->size; ++i) {
|
||||
if (memcmp(SecView__text + i, FunctionHint, sizeof(FunctionHint)) == 0) {
|
||||
// we only allow that deviation is +-0x20
|
||||
for (uint64_t j = i - FunctionHintOffset - 0x20; j < i - FunctionHintOffset + 0x20; ++j) {
|
||||
if (memcmp(SecView__text + j,
|
||||
FunctionBeginByte,
|
||||
sizeof(FunctionBeginByte)) == 0) {
|
||||
FunctionOffset = static_cast<size_t>(Sec__text->offset + j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (FunctionOffset != InvalidOffset)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (FunctionOffset == InvalidOffset) {
|
||||
printf("PatchSolution2 ...... Omitted.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint64_t i = 0; i < Sec__const->size; ++i) {
|
||||
if (memcmp(SecView__const + i, Keyword, KeywordLength) == 0) {
|
||||
KeywordOffset = static_cast<size_t>(Sec__const->offset + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (KeywordOffset == InvalidOffset) {
|
||||
printf("PatchSolution2 ...... Omitted.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
CapstoneDisassembler Disassembler = _$$_CapstoneEngine.CreateDisassembler();
|
||||
Disassembler.SetContext(SecView__stubs, Sec__stubs->size, Sec__stubs->addr);
|
||||
|
||||
while (Disassembler.Next()) {
|
||||
auto ins = Disassembler.GetInstruction();
|
||||
//
|
||||
// As far as I know, all stub functions have a pattern looking like:
|
||||
// jmp qword ptr [RIP + xxxx]
|
||||
//
|
||||
if (strcasecmp(ins->mnemonic, "jmp") == 0 && ins->detail->x86.operands[0].type == X86_OP_MEM && ins->detail->x86.operands[0].mem.base == X86_REG_RIP) {
|
||||
uint64_t la_symbol_ptr_rva = Disassembler.GetContext().Address + ins->detail->x86.operands[0].mem.disp;
|
||||
uint32_t la_symbol_ptr_offset = _$$_ImageInterpreter.AddressToOffset(la_symbol_ptr_rva);
|
||||
if (la_symbol_ptr_offset == X64ImageInterpreter::InvalidOffset)
|
||||
continue;
|
||||
|
||||
uint64_t stub_helper_rva = *_$$_FileViewHandle.ConstViewAtOffset<uint64_t>(la_symbol_ptr_offset);
|
||||
uint32_t stub_helper_offset = _$$_ImageInterpreter.AddressToOffset(stub_helper_rva);
|
||||
if (stub_helper_offset == X64ImageInterpreter::InvalidOffset)
|
||||
continue;
|
||||
|
||||
//
|
||||
// __ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc
|
||||
// is the mangled name of "std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::append(char const*)",
|
||||
// which is, as known as, "std::string::append(const char*)"
|
||||
// You can demangle it by c++flit
|
||||
// e.g.
|
||||
// c++filt -_ '__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc'
|
||||
//
|
||||
if (IsStubHelperResolvedTo(_$$_FileViewHandle.ConstViewAtOffset<uint8_t>(stub_helper_offset),
|
||||
"__ZNSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6appendEPKc")) {
|
||||
std_string_append_stub_Offset =
|
||||
Disassembler.GetInstructionContext().Opcodes.ConstPtr - _$$_FileViewHandle.ConstView<uint8_t>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (std_string_append_stub_Offset == InvalidOffset) {
|
||||
printf("PatchSolution2 ...... Omitted.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
_$$_FunctionOffset = FunctionOffset;
|
||||
_$$_KeywordOffset = KeywordOffset;
|
||||
_$$_std_string_append_stub_Offset = std_string_append_stub_Offset;
|
||||
printf("PatchSolution1 ...... Ready to apply.\n");
|
||||
printf(" Info: Target function offset = +0x%08zx\n", _$$_FunctionOffset);
|
||||
printf(" Info: Keyword offset = +0x%08zx\n", _$$_KeywordOffset);
|
||||
printf(" Info: std::string::append(const char*) offset = +%08zx\n", _$$_std_string_append_stub_Offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PatchSolution2::CheckKey(RSACipher* pCipher) const {
|
||||
if (_$$_FileViewHandle == MapViewTraits::InvalidValue ||
|
||||
_$$_FunctionOffset == InvalidOffset ||
|
||||
_$$_KeywordOffset == InvalidOffset)
|
||||
throw Exception(__FILE__, __LINE__,
|
||||
"PatchSolution2::MakePatch is not ready.");
|
||||
|
||||
std::string PublicKeyPEM =
|
||||
pCipher->ExportKeyString<RSAKeyType::PublicKey, RSAKeyFormat::PEM>();
|
||||
|
||||
PublicKeyPEM.erase(PublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"), 26);
|
||||
PublicKeyPEM.erase(PublicKeyPEM.find("-----END PUBLIC KEY-----"), 24);
|
||||
{
|
||||
std::string::size_type pos = 0;
|
||||
while ((pos = PublicKeyPEM.find('\n', pos)) != std::string::npos) {
|
||||
PublicKeyPEM.erase(pos, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return PublicKeyPEM.length() == 0x188;
|
||||
}
|
||||
|
||||
void PatchSolution2::MakePatch(RSACipher* pCipher) const {
|
||||
if (_$$_FileViewHandle == MapViewTraits::InvalidValue ||
|
||||
_$$_FunctionOffset == InvalidOffset ||
|
||||
_$$_KeywordOffset == InvalidOffset ||
|
||||
_$$_std_string_append_stub_Offset == InvalidOffset)
|
||||
{
|
||||
throw Exception(__FILE__, __LINE__,
|
||||
"Invalid patch offset.");
|
||||
}
|
||||
|
||||
auto ViewPtr = _$$_FileViewHandle.View<uint8_t>();
|
||||
|
||||
//
|
||||
// Prepare public key string
|
||||
//
|
||||
std::string PublicKeyPEM =
|
||||
pCipher->ExportKeyString<RSAKeyType::PublicKey, RSAKeyFormat::PEM>();
|
||||
|
||||
PublicKeyPEM.erase(PublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"), 26);
|
||||
PublicKeyPEM.erase(PublicKeyPEM.find("-----END PUBLIC KEY-----"), 24);
|
||||
{
|
||||
std::string::size_type pos = 0;
|
||||
while ((pos = PublicKeyPEM.find('\n', pos)) != std::string::npos) {
|
||||
PublicKeyPEM.erase(pos, 1);
|
||||
}
|
||||
}
|
||||
|
||||
assert(PublicKeyPEM.length() == 0x188);
|
||||
|
||||
//
|
||||
// Prepare new function opcodes
|
||||
//
|
||||
KeystoneAssembler Assembler = _$$_KeystoneEngine.CreateAssembler();
|
||||
|
||||
uint64_t FunctionRVA = _$$_ImageInterpreter.OffsetToAddress(static_cast<uint32_t>(_$$_FunctionOffset));
|
||||
uint64_t KeywordRVA = _$$_ImageInterpreter.OffsetToAddress(static_cast<uint32_t>(_$$_KeywordOffset));
|
||||
uint64_t std_string_append_stub_RVA = _$$_ImageInterpreter.OffsetToAddress(static_cast<uint32_t>(_$$_std_string_append_stub_Offset));
|
||||
assert(FunctionRVA != X64ImageInterpreter::InvalidAddress);
|
||||
assert(KeywordRVA != X64ImageInterpreter::InvalidAddress);
|
||||
assert(std_string_append_stub_RVA != X64ImageInterpreter::InvalidAddress);
|
||||
|
||||
char AssemblyCode[512] = {};
|
||||
sprintf(AssemblyCode,
|
||||
"push rbp;"
|
||||
"mov rbp, rsp;"
|
||||
"push r15;"
|
||||
"push r14;"
|
||||
"push rbx;"
|
||||
"sub rsp, 0x48;"
|
||||
|
||||
"mov rbx, rdi;"
|
||||
|
||||
"xor rax, rax;"
|
||||
"mov qword ptr[rsp], rax;"
|
||||
"mov qword ptr[rsp + 0x8], rax;"
|
||||
"mov qword ptr[rsp + 0x10], rax;"
|
||||
|
||||
"lea rdi, qword ptr[rsp];"
|
||||
"lea rsi, qword ptr[0x%016llx];" // filled with address to Keyword
|
||||
"call 0x%016llx;" // filled with address to std::string::append(const char*)
|
||||
|
||||
"mov rax, qword ptr[rsp];"
|
||||
"mov qword ptr[rbx], rax;"
|
||||
"mov rax, qword ptr[rsp + 0x8];"
|
||||
"mov qword ptr[rbx + 0x8], rax;"
|
||||
"mov rax, qword ptr[rsp + 0x10];"
|
||||
"mov qword ptr[rbx + 0x10], rax;"
|
||||
|
||||
"mov rax, rbx;"
|
||||
"add rsp, 0x48;"
|
||||
"pop rbx;"
|
||||
"pop r14;"
|
||||
"pop r15;"
|
||||
"pop rbp;"
|
||||
"ret;",
|
||||
KeywordRVA,
|
||||
std_string_append_stub_RVA);
|
||||
|
||||
auto NewFunctionOpcodes = Assembler.OpCodes(AssemblyCode, FunctionRVA);
|
||||
|
||||
puts("****************************");
|
||||
puts("* Begin PatchSolution2 *");
|
||||
puts("****************************");
|
||||
printf("@+0x%08zx\n", _$$_KeywordOffset);
|
||||
puts("Previous:");
|
||||
PrintMemory(ViewPtr + _$$_KeywordOffset,
|
||||
ViewPtr + _$$_KeywordOffset + KeywordLength,
|
||||
ViewPtr);
|
||||
memcpy(ViewPtr + _$$_KeywordOffset,
|
||||
PublicKeyPEM.c_str(),
|
||||
PublicKeyPEM.length() + 1); // with null-terminator
|
||||
|
||||
puts("After:");
|
||||
PrintMemory(ViewPtr + _$$_KeywordOffset,
|
||||
ViewPtr + _$$_KeywordOffset + KeywordLength,
|
||||
ViewPtr);
|
||||
puts("");
|
||||
|
||||
printf("@+0x%08zx\n", _$$_FunctionOffset);
|
||||
puts("Previous:");
|
||||
PrintMemory(ViewPtr + _$$_FunctionOffset,
|
||||
ViewPtr + _$$_FunctionOffset + NewFunctionOpcodes.size(),
|
||||
ViewPtr);
|
||||
|
||||
memcpy(ViewPtr + _$$_FunctionOffset,
|
||||
NewFunctionOpcodes.data(),
|
||||
NewFunctionOpcodes.size());
|
||||
|
||||
puts("After:");
|
||||
PrintMemory(ViewPtr + _$$_FunctionOffset,
|
||||
ViewPtr + _$$_FunctionOffset + NewFunctionOpcodes.size(),
|
||||
ViewPtr);
|
||||
puts("");
|
||||
}
|
||||
|
||||
84
navicat-patcher/PatchSolutions.hpp
Normal file
84
navicat-patcher/PatchSolutions.hpp
Normal file
@ -0,0 +1,84 @@
|
||||
#pragma once
|
||||
#include "RSACipher.hpp"
|
||||
#include "SystemObjectTraits.hpp"
|
||||
#include "X64ImageInterpreter.hpp"
|
||||
#include "CapstoneDisassembler.hpp"
|
||||
#include "KeystoneAssembler.hpp"
|
||||
|
||||
//
|
||||
// Print memory data in [from, to) at least
|
||||
// If `base` is not nullptr, print address as offset. Otherwise, as absolute address.
|
||||
// NOTICE:
|
||||
// `base` must >= `from`
|
||||
//
|
||||
void PrintMemory(const void* from, const void* to, const void* base = nullptr);
|
||||
|
||||
class PatchSolution {
|
||||
public:
|
||||
virtual void SetFile(const MapViewTraits::HandleType& FileViewHandle) = 0;
|
||||
virtual bool FindPatchOffset() = 0;
|
||||
virtual bool CheckKey(RSACipher* pCipher) const = 0;
|
||||
virtual void MakePatch(RSACipher* pCipher) const = 0;
|
||||
virtual ~PatchSolution() = default;
|
||||
};
|
||||
|
||||
class PatchSolution0 : public PatchSolution {
|
||||
private:
|
||||
static constexpr size_t InvalidOffset = static_cast<size_t>(-1);
|
||||
static const char Keyword[452];
|
||||
static constexpr size_t KeywordLength = 451;
|
||||
|
||||
MapViewTraits::HandleType _$$_FileViewHandle;
|
||||
size_t _$$_PatchOffset;
|
||||
public:
|
||||
PatchSolution0() noexcept;
|
||||
virtual void SetFile(const MapViewTraits::HandleType& FileViewHandle) noexcept override;
|
||||
virtual bool FindPatchOffset() noexcept override;
|
||||
virtual bool CheckKey(RSACipher* pCipher) const override;
|
||||
virtual void MakePatch(RSACipher* pCipher) const override;
|
||||
};
|
||||
|
||||
class PatchSolution1 : public PatchSolution {
|
||||
private:
|
||||
static constexpr size_t InvalidOffset = static_cast<size_t>(-1);
|
||||
static const uint8_t Keyword[0x188];
|
||||
static constexpr size_t KeywordLength = 0x188;
|
||||
|
||||
MapViewTraits::HandleType _$$_FileViewHandle;
|
||||
size_t _$$_PatchOffset;
|
||||
public:
|
||||
PatchSolution1() noexcept;
|
||||
virtual void SetFile(const MapViewTraits::HandleType& FileViewHandle) noexcept override;
|
||||
virtual bool FindPatchOffset() noexcept override;
|
||||
virtual bool CheckKey(RSACipher* cipher) const override;
|
||||
virtual void MakePatch(RSACipher* pCipher) const override;
|
||||
};
|
||||
|
||||
class PatchSolution2 : public PatchSolution {
|
||||
private:
|
||||
static constexpr size_t InvalidOffset = static_cast<size_t>(-1);
|
||||
static const char Keyword[1114];
|
||||
static constexpr size_t KeywordLength = 1114;
|
||||
static const uint8_t FunctionBeginByte[13];
|
||||
static const uint8_t FunctionHint[4];
|
||||
static constexpr size_t FunctionHintOffset = 0x2d2;
|
||||
static constexpr size_t FunctionSize = 0x938e;
|
||||
|
||||
MapViewTraits::HandleType _$$_FileViewHandle;
|
||||
X64ImageInterpreter _$$_ImageInterpreter;
|
||||
CapstoneEngine _$$_CapstoneEngine;
|
||||
KeystoneEngine _$$_KeystoneEngine;
|
||||
|
||||
size_t _$$_FunctionOffset;
|
||||
size_t _$$_KeywordOffset;
|
||||
size_t _$$_std_string_append_stub_Offset;
|
||||
|
||||
bool IsStubHelperResolvedTo(const uint8_t* StubHelperProc, const char* Symbol) const;
|
||||
|
||||
public:
|
||||
PatchSolution2();
|
||||
virtual void SetFile(const MapViewTraits::HandleType& FileMapViewHandle) noexcept override;
|
||||
virtual bool FindPatchOffset() noexcept override;
|
||||
virtual bool CheckKey(RSACipher* pCipher) const override;
|
||||
virtual void MakePatch(RSACipher* pCipher) const override;
|
||||
};
|
||||
83
navicat-patcher/PrintMemory.cpp
Normal file
83
navicat-patcher/PrintMemory.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
static jmp_buf env;
|
||||
|
||||
static void SIGSEGV_Handler(int sig) {
|
||||
siglongjmp(env, 1);
|
||||
}
|
||||
|
||||
//
|
||||
// read byte(s) at address `p` as _Type to `out`
|
||||
// succeed if return true, otherwise return false
|
||||
//
|
||||
template<typename _Type>
|
||||
static inline bool ProbeForRead(const void* p, void* out) {
|
||||
int r = sigsetjmp(env, 1);
|
||||
if (r == 0) {
|
||||
*reinterpret_cast<_Type*>(out) = *reinterpret_cast<const _Type*>(p);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Print memory data in [from, to) at least
|
||||
// If `base` is not nullptr, print address as offset. Otherwise, as absolute address.
|
||||
// NOTICE:
|
||||
// `base` must >= `from`
|
||||
//
|
||||
void PrintMemory(const void* from, const void* to, const void* base) {
|
||||
const uint8_t* start = reinterpret_cast<const uint8_t*>(from);
|
||||
const uint8_t* end = reinterpret_cast<const uint8_t*>(to);
|
||||
const uint8_t* base_ptr = reinterpret_cast<const uint8_t*>(base);
|
||||
|
||||
if (start >= end)
|
||||
return;
|
||||
|
||||
while (reinterpret_cast<uintptr_t>(start) % 16)
|
||||
start--;
|
||||
|
||||
while (reinterpret_cast<uintptr_t>(start) % 16)
|
||||
end++;
|
||||
|
||||
void (*prev_handler)(int) = signal(SIGSEGV, SIGSEGV_Handler);
|
||||
while (start < end) {
|
||||
uint16_t value[16] = {};
|
||||
|
||||
if (base_ptr)
|
||||
printf("+0x%p ", reinterpret_cast<const void*>(start - base_ptr));
|
||||
else
|
||||
printf("0x%p ", start);
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
if (ProbeForRead<uint8_t>(start + i, value + i)) {
|
||||
printf("%02x ", value[i]);
|
||||
} else {
|
||||
value[i] = 0xffff;
|
||||
printf("?? ");
|
||||
}
|
||||
}
|
||||
|
||||
printf(" ");
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
if (value[i] < 0x20) {
|
||||
printf(".");
|
||||
} else if (value[i] > 0x7e) {
|
||||
printf(".");
|
||||
} else {
|
||||
printf("%c", value[i]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
start += 0x10;
|
||||
}
|
||||
signal(SIGSEGV, prev_handler);
|
||||
}
|
||||
|
||||
@ -1,278 +1,288 @@
|
||||
#pragma once
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <string>
|
||||
#include <memory.h>
|
||||
#include "ResourceObject.hpp"
|
||||
#include "Exception.hpp"
|
||||
|
||||
class OpenSSLException {
|
||||
private:
|
||||
const unsigned long ErrorCode;
|
||||
public:
|
||||
explicit OpenSSLException(unsigned long code) : ErrorCode(code) {}
|
||||
unsigned long GetErrorCode() const noexcept { return ErrorCode; }
|
||||
const char* GetErrorString() const noexcept { return ERR_error_string(ErrorCode, nullptr); }
|
||||
struct OpensslBIOTraits {
|
||||
using HandleType = BIO*;
|
||||
static inline const HandleType InvalidValue = nullptr;
|
||||
static constexpr auto& Releasor = BIO_free;
|
||||
};
|
||||
|
||||
template<typename _Type, void(_Type_free)(_Type*)>
|
||||
class OpenSSLObject {
|
||||
protected:
|
||||
_Type* _pObj;
|
||||
struct OpensslBIOChainTraits {
|
||||
using HandleType = BIO*;
|
||||
static inline const HandleType InvalidValue = nullptr;
|
||||
static constexpr auto& Releasor = BIO_free_all;
|
||||
};
|
||||
|
||||
struct OpensslBNTraits {
|
||||
using HandleType = BIGNUM*;
|
||||
static inline const HandleType InvalidValue = nullptr;
|
||||
static constexpr auto& Releasor = BN_free;
|
||||
};
|
||||
|
||||
struct OpensslRSATraits {
|
||||
using HandleType = RSA*;
|
||||
static inline const HandleType InvalidValue = nullptr;
|
||||
static constexpr auto& Releasor = RSA_free;
|
||||
};
|
||||
|
||||
class OpensslError : public Exception {
|
||||
private:
|
||||
const unsigned long _$$_ErrorCode;
|
||||
public:
|
||||
// take over the object passed in
|
||||
explicit OpenSSLObject(_Type* pObj) noexcept : _pObj(pObj) {}
|
||||
|
||||
OpenSSLObject(const OpenSSLObject<_Type, _Type_free>& other) = delete;
|
||||
OpensslError(const char* FileName,
|
||||
size_t Line,
|
||||
unsigned long Code,
|
||||
const char* Message) noexcept :
|
||||
Exception(FileName, Line, Message),
|
||||
_$$_ErrorCode(Code) {}
|
||||
|
||||
OpenSSLObject(OpenSSLObject<_Type, _Type_free>&& other) noexcept : _pObj(other._pObj) {
|
||||
other._pObj = nullptr;
|
||||
virtual bool HasErrorCode() const noexcept override {
|
||||
return true;
|
||||
}
|
||||
|
||||
OpenSSLObject<_Type, _Type_free>&
|
||||
operator=(const OpenSSLObject<_Type, _Type_free>& other) = delete;
|
||||
|
||||
OpenSSLObject<_Type, _Type_free>&
|
||||
operator=(_Type* other) noexcept {
|
||||
if (_pObj != other) {
|
||||
_Type_free(_pObj);
|
||||
}
|
||||
_pObj = other;
|
||||
return *this;
|
||||
virtual unsigned long ErrorCode() const noexcept override {
|
||||
return _$$_ErrorCode;
|
||||
}
|
||||
|
||||
OpenSSLObject<_Type, _Type_free>&
|
||||
operator=(OpenSSLObject<_Type, _Type_free>&& other) noexcept {
|
||||
if (&other != this) {
|
||||
if (_pObj)
|
||||
_Type_free(_pObj);
|
||||
_pObj = other._pObj;
|
||||
other._pObj = nullptr;
|
||||
}
|
||||
return *this;
|
||||
virtual const char* ErrorString() const noexcept override {
|
||||
return ERR_error_string(_$$_ErrorCode, nullptr);
|
||||
}
|
||||
|
||||
_Type* GetPointer() const noexcept {
|
||||
return _pObj;
|
||||
}
|
||||
};
|
||||
|
||||
~OpenSSLObject() {
|
||||
if (_pObj) {
|
||||
_Type_free(_pObj);
|
||||
_pObj = nullptr;
|
||||
}
|
||||
}
|
||||
enum class RSAKeyType {
|
||||
PrivateKey,
|
||||
PublicKey
|
||||
};
|
||||
|
||||
enum class RSAKeyFormat {
|
||||
NotSpecified,
|
||||
PEM,
|
||||
PKCS1
|
||||
};
|
||||
|
||||
class RSACipher {
|
||||
private:
|
||||
OpenSSLObject<RSA, RSA_free> _RsaObj;
|
||||
ResourceObject<OpensslRSATraits> _$$_RsaObj;
|
||||
|
||||
RSACipher(RSA* pRsa) : _$$_RsaObj(pRsa) {}
|
||||
|
||||
// Copy constructor is not allowed
|
||||
RSACipher(const RSACipher&) = delete;
|
||||
|
||||
// Copy assignment is not allowed
|
||||
RSACipher& operator=(const RSACipher&) = delete;
|
||||
|
||||
template<RSAKeyType __Type, RSAKeyFormat __Format = RSAKeyFormat::NotSpecified>
|
||||
static void _RSAToBIO(RSA* pRsaObject, BIO* pBioObject) {
|
||||
if constexpr (__Type == RSAKeyType::PrivateKey) {
|
||||
if (!PEM_write_bio_RSAPrivateKey(pBioObject, pRsaObject, nullptr, nullptr, 0, nullptr, nullptr))
|
||||
throw Exception(__FILE__, __LINE__,
|
||||
"PEM_write_bio_RSAPrivateKey fails.");
|
||||
} else {
|
||||
if constexpr (__Format == RSAKeyFormat::PEM) {
|
||||
if (!PEM_write_bio_RSA_PUBKEY(pBioObject, pRsaObject))
|
||||
throw Exception(__FILE__, __LINE__,
|
||||
"PEM_write_bio_RSA_PUBKEY fails.");
|
||||
} else if constexpr (__Format == RSAKeyFormat::PKCS1) {
|
||||
if (!PEM_write_bio_RSAPublicKey(pBioObject, pRsaObject))
|
||||
throw Exception(__FILE__, __LINE__,
|
||||
"PEM_write_bio_RSAPublicKey fails.");
|
||||
} else {
|
||||
static_assert(__Format == RSAKeyFormat::PEM || __Format == RSAKeyFormat::PKCS1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<RSAKeyType _Type, RSAKeyFormat _Format = RSAKeyFormat::NotSpecified>
|
||||
static RSA* _BIOToRSA(BIO* pBioObject) {
|
||||
RSA* pNewRsaObject;
|
||||
|
||||
if constexpr (_Type == RSAKeyType::PrivateKey) {
|
||||
pNewRsaObject = PEM_read_bio_RSAPrivateKey(pBioObject, nullptr, nullptr, nullptr);
|
||||
if (pNewRsaObject == nullptr)
|
||||
throw Exception(__FILE__, __LINE__,
|
||||
"PEM_read_bio_RSAPrivateKey fails.");
|
||||
} else {
|
||||
if constexpr (_Format == RSAKeyFormat::PEM) {
|
||||
pNewRsaObject = PEM_read_bio_RSA_PUBKEY(pBioObject, nullptr, nullptr, nullptr);
|
||||
if (pNewRsaObject == nullptr)
|
||||
throw Exception(__FILE__, __LINE__,
|
||||
"PEM_read_bio_RSA_PUBKEY fails.");
|
||||
} else if constexpr (_Format == RSAKeyFormat::PKCS1) {
|
||||
pNewRsaObject = PEM_read_bio_RSAPublicKey(pBioObject, nullptr, nullptr, nullptr);
|
||||
if (pNewRsaObject == nullptr)
|
||||
throw Exception(__FILE__, __LINE__,
|
||||
"PEM_read_bio_RSAPublicKey fails.");
|
||||
} else {
|
||||
static_assert(_Format == RSAKeyFormat::PEM || _Format == RSAKeyFormat::PKCS1);
|
||||
}
|
||||
}
|
||||
|
||||
return pNewRsaObject;
|
||||
}
|
||||
|
||||
explicit RSACipher(RSA* lpRsa) : _RsaObj(lpRsa) {}
|
||||
public:
|
||||
|
||||
RSACipher(const RSACipher&) = delete;
|
||||
RSACipher(RSACipher&&) = delete;
|
||||
RSACipher& operator=(const RSACipher&) = delete;
|
||||
RSACipher& operator=(RSACipher&&) = delete;
|
||||
|
||||
enum class KeyType {
|
||||
PrivateKey,
|
||||
PublicKey
|
||||
};
|
||||
|
||||
enum class KeyFormat {
|
||||
NotSpecified,
|
||||
PEM,
|
||||
PKCS1
|
||||
};
|
||||
|
||||
static RSACipher* Create() {
|
||||
RSA* pObj = RSA_new();
|
||||
return pObj ? new RSACipher(pObj) : nullptr;
|
||||
}
|
||||
|
||||
bool GenerateKey(int bits, unsigned int e = RSA_F4) {
|
||||
bool bSuccess = false;
|
||||
OpenSSLObject<BIGNUM, BN_free> bn_e(BN_new());
|
||||
|
||||
if (!bn_e.GetPointer())
|
||||
return false;
|
||||
|
||||
if (!BN_set_word(bn_e.GetPointer(), e))
|
||||
return false;
|
||||
|
||||
if (!RSA_generate_key_ex(_RsaObj.GetPointer(), bits, bn_e.GetPointer(), nullptr))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<KeyType _Type, KeyFormat _Format = KeyFormat::NotSpecified>
|
||||
bool ExportKeyToFile(const std::string& filename) {
|
||||
static_assert(
|
||||
_Type == KeyType::PrivateKey || (_Format == KeyFormat::PEM || _Format == KeyFormat::PKCS1),
|
||||
"Not supported format."
|
||||
);
|
||||
|
||||
bool bSuccess = false;
|
||||
OpenSSLObject<BIO, BIO_free_all> bio_file(BIO_new_file(filename.c_str(), "w"));
|
||||
|
||||
if (bio_file.GetPointer() == nullptr)
|
||||
return false;
|
||||
|
||||
if (_Type == KeyType::PrivateKey) {
|
||||
bSuccess = PEM_write_bio_RSAPrivateKey(bio_file.GetPointer(), _RsaObj.GetPointer(), nullptr, nullptr, 0, nullptr, nullptr) != 0;
|
||||
} else {
|
||||
if (_Format == KeyFormat::PEM)
|
||||
bSuccess = PEM_write_bio_RSA_PUBKEY(bio_file.GetPointer(), _RsaObj.GetPointer()) != 0;
|
||||
if (_Format == KeyFormat::PKCS1)
|
||||
bSuccess = PEM_write_bio_RSAPublicKey(bio_file.GetPointer(), _RsaObj.GetPointer()) != 0;
|
||||
RSACipher* aCipher = new RSACipher(RSA_new());
|
||||
if (aCipher->_$$_RsaObj.IsValid() == false) {
|
||||
delete aCipher;
|
||||
aCipher = nullptr;
|
||||
}
|
||||
|
||||
return bSuccess;
|
||||
return aCipher;
|
||||
}
|
||||
|
||||
template<KeyType _Type, KeyFormat _Format = KeyFormat::NotSpecified>
|
||||
RSACipher() : _$$_RsaObj(RSA_new()) {
|
||||
if (_$$_RsaObj.IsValid() == false)
|
||||
throw OpensslError(__FILE__, __LINE__, ERR_get_error(),
|
||||
"RSA_new fails.");
|
||||
}
|
||||
|
||||
void GenerateKey(int bits, unsigned int e = RSA_F4) {
|
||||
ResourceObject<OpensslBNTraits> bn_e;
|
||||
|
||||
bn_e.TakeOver(BN_new());
|
||||
if (bn_e.IsValid() == false)
|
||||
throw OpensslError(__FILE__, __LINE__, ERR_get_error(),
|
||||
"BN_new fails.");
|
||||
|
||||
if (!BN_set_word(bn_e, e))
|
||||
throw Exception(__FILE__, __LINE__,
|
||||
"BN_set_word fails.");
|
||||
|
||||
if (!RSA_generate_key_ex(_$$_RsaObj, bits, bn_e, nullptr))
|
||||
throw OpensslError(__FILE__, __LINE__, ERR_get_error(),
|
||||
"RSA_generate_key_ex fails.");
|
||||
}
|
||||
|
||||
template<RSAKeyType _Type, RSAKeyFormat _Format = RSAKeyFormat::NotSpecified>
|
||||
void ExportKeyToFile(const std::string& FileName) {
|
||||
ResourceObject<OpensslBIOTraits> bio_file;
|
||||
|
||||
bio_file.TakeOver(BIO_new_file(FileName.c_str(), "w"));
|
||||
if (bio_file.IsValid() == false)
|
||||
throw Exception(__FILE__, __LINE__,
|
||||
"BIO_new_file fails.");
|
||||
|
||||
_RSAToBIO<_Type, _Format>(_$$_RsaObj, bio_file);
|
||||
}
|
||||
|
||||
template<RSAKeyType _Type, RSAKeyFormat _Format = RSAKeyFormat::NotSpecified>
|
||||
std::string ExportKeyString() {
|
||||
static_assert(
|
||||
_Type == KeyType::PrivateKey || (_Format == KeyFormat::PEM || _Format == KeyFormat::PKCS1),
|
||||
"Not supported format."
|
||||
);
|
||||
std::string result;
|
||||
ResourceObject<OpensslBIOTraits> bio_mem;
|
||||
long DataSize;
|
||||
const char* pData = nullptr;
|
||||
|
||||
std::string KeyString;
|
||||
OpenSSLObject<BIO, BIO_free_all> bio_mem(BIO_new(BIO_s_mem()));
|
||||
long len = 0;
|
||||
const char* lpdata = nullptr;
|
||||
bio_mem.TakeOver(BIO_new(BIO_s_mem()));
|
||||
if (bio_mem.IsValid() == false)
|
||||
throw Exception(__FILE__, __LINE__,
|
||||
"BIO_new fails.");
|
||||
|
||||
if (bio_mem.GetPointer() == nullptr)
|
||||
return KeyString;
|
||||
_RSAToBIO<_Type, _Format>(_$$_RsaObj, bio_mem);
|
||||
|
||||
if (_Type == KeyType::PrivateKey) {
|
||||
if (!PEM_write_bio_RSAPrivateKey(bio_mem.GetPointer(), _RsaObj.GetPointer(), nullptr, nullptr, 0, nullptr, nullptr))
|
||||
return KeyString;
|
||||
} else {
|
||||
if (_Format == KeyFormat::PEM) {
|
||||
if (!PEM_write_bio_RSA_PUBKEY(bio_mem.GetPointer(), _RsaObj.GetPointer()))
|
||||
return KeyString;
|
||||
}
|
||||
if (_Format == KeyFormat::PKCS1) {
|
||||
if (!PEM_write_bio_RSAPublicKey(bio_mem.GetPointer(), _RsaObj.GetPointer()))
|
||||
return KeyString;
|
||||
}
|
||||
}
|
||||
DataSize = BIO_get_mem_data(bio_mem, &pData);
|
||||
result.resize(DataSize);
|
||||
memcpy(result.data(), pData, DataSize);
|
||||
|
||||
len = BIO_get_mem_data(bio_mem.GetPointer(), &lpdata);
|
||||
KeyString.assign(lpdata, static_cast<size_t>(len));
|
||||
return KeyString;
|
||||
return result;
|
||||
}
|
||||
|
||||
template<KeyType _Type, KeyFormat _Format = KeyFormat::NotSpecified>
|
||||
bool ImportKeyFromFile(const std::string& filename) {
|
||||
static_assert(
|
||||
_Type == KeyType::PrivateKey || (_Format == KeyFormat::PEM || _Format == KeyFormat::PKCS1),
|
||||
"Not supported format."
|
||||
);
|
||||
|
||||
template<RSAKeyType _Type, RSAKeyFormat _Format = RSAKeyFormat::NotSpecified>
|
||||
void ImportKeyFromFile(const std::string& FileName) {
|
||||
bool bSuccess = false;
|
||||
OpenSSLObject<BIO, BIO_free_all> bio_file(BIO_new_file(filename.c_str(), "r"));
|
||||
OpenSSLObject<RSA, RSA_free> newRsaObj(nullptr);
|
||||
ResourceObject<OpensslBIOTraits> bio_file;
|
||||
RSA* NewRsaObj;
|
||||
|
||||
if (bio_file.GetPointer() == nullptr)
|
||||
return false;
|
||||
bio_file.TakeOver(BIO_new_file(FileName.c_str(), "r"));
|
||||
if (bio_file.IsValid() == false)
|
||||
throw Exception(__FILE__, __LINE__,
|
||||
"BIO_new_file fails.");
|
||||
|
||||
if (_Type == KeyType::PrivateKey) {
|
||||
newRsaObj = PEM_read_bio_RSAPrivateKey(bio_file.GetPointer(), nullptr, nullptr, nullptr);
|
||||
} else {
|
||||
if (_Format == KeyFormat::PEM)
|
||||
newRsaObj = PEM_read_bio_RSA_PUBKEY(bio_file.GetPointer(), nullptr, nullptr, nullptr);
|
||||
if (_Format == KeyFormat::PKCS1)
|
||||
newRsaObj = PEM_read_bio_RSAPublicKey(bio_file.GetPointer(), nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
if (newRsaObj.GetPointer()) {
|
||||
_RsaObj = static_cast<OpenSSLObject<RSA, RSA_free>&&>(newRsaObj);
|
||||
bSuccess = true;
|
||||
}
|
||||
|
||||
return bSuccess;
|
||||
NewRsaObj = _BIOToRSA<_Type, _Format>(bio_file);
|
||||
_$$_RsaObj.Release();
|
||||
_$$_RsaObj.TakeOver(NewRsaObj);
|
||||
}
|
||||
|
||||
template<KeyType _Type, KeyFormat _Format = KeyFormat::NotSpecified>
|
||||
bool ImportKeyString(const std::string& KeyString) {
|
||||
static_assert(
|
||||
_Type == KeyType::PrivateKey || (_Format == KeyFormat::PEM || _Format == KeyFormat::PKCS1),
|
||||
"Not supported format."
|
||||
);
|
||||
template<RSAKeyType _Type, RSAKeyFormat _Format = RSAKeyFormat::NotSpecified>
|
||||
void ImportKeyString(const std::string& KeyString) {
|
||||
ResourceObject<OpensslBIOTraits> bio_mem;
|
||||
RSA* NewRsaObj;
|
||||
|
||||
bool bSuccess = false;
|
||||
OpenSSLObject<BIO, BIO_free_all> bio_mem(BIO_new(BIO_s_mem()));
|
||||
OpenSSLObject<RSA, RSA_free> newRsaObj(nullptr);
|
||||
bio_mem = BIO_new(BIO_s_mem());
|
||||
if (bio_mem == nullptr)
|
||||
throw Exception(__FILE__, __LINE__,
|
||||
"BIO_new fails.");
|
||||
|
||||
if (bio_mem.GetPointer() == nullptr)
|
||||
return false;
|
||||
if (BIO_puts(bio_mem, KeyString.c_str()) <= 0)
|
||||
throw Exception(__FILE__, __LINE__,
|
||||
"BIO_puts fails.");
|
||||
|
||||
BIO_puts(bio_mem.GetPointer(), KeyString.c_str());
|
||||
|
||||
if (_Type == KeyType::PrivateKey) {
|
||||
newRsaObj = PEM_read_bio_RSAPrivateKey(bio_mem.GetPointer(), nullptr, nullptr, nullptr);
|
||||
} else {
|
||||
if (_Format == KeyFormat::PEM)
|
||||
newRsaObj = PEM_read_bio_RSA_PUBKEY(bio_mem.GetPointer(), nullptr, nullptr, nullptr);
|
||||
if (_Format == KeyFormat::PKCS1)
|
||||
newRsaObj = PEM_read_bio_RSAPublicKey(bio_mem.GetPointer(), nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
if (newRsaObj.GetPointer()) {
|
||||
_RsaObj = static_cast<OpenSSLObject<RSA, RSA_free>&&>(newRsaObj);
|
||||
bSuccess = true;
|
||||
}
|
||||
|
||||
return bSuccess;
|
||||
NewRsaObj = _BIOToRSA<_Type, _Format>(bio_mem);
|
||||
_$$_RsaObj.Release();
|
||||
_$$_RsaObj.TakeOver(NewRsaObj);
|
||||
}
|
||||
|
||||
template<KeyType _Type = KeyType::PublicKey>
|
||||
template<RSAKeyType _Type = RSAKeyType::PublicKey>
|
||||
int Encrypt(const void* from, int len, void* to, int padding) {
|
||||
int write_bytes = 0;
|
||||
int write_bytes;
|
||||
|
||||
if (_Type == KeyType::PrivateKey) {
|
||||
if constexpr (_Type == RSAKeyType::PrivateKey) {
|
||||
write_bytes = RSA_private_encrypt(len,
|
||||
reinterpret_cast<const unsigned char*>(from),
|
||||
reinterpret_cast<unsigned char*>(to),
|
||||
_RsaObj.GetPointer(),
|
||||
_$$_RsaObj,
|
||||
padding);
|
||||
if (write_bytes == -1)
|
||||
throw OpensslError(__FILE__, __LINE__, ERR_get_error(),
|
||||
"RSA_private_encrypt fails.");
|
||||
} else {
|
||||
write_bytes = RSA_public_encrypt(len,
|
||||
reinterpret_cast<const unsigned char*>(from),
|
||||
reinterpret_cast<unsigned char*>(to),
|
||||
_RsaObj.GetPointer(),
|
||||
_$$_RsaObj,
|
||||
padding);
|
||||
if (write_bytes == -1)
|
||||
throw OpensslError(__FILE__, __LINE__, ERR_get_error(),
|
||||
"RSA_public_encrypt fails.");
|
||||
}
|
||||
|
||||
if (write_bytes == -1)
|
||||
write_bytes = 0;
|
||||
return write_bytes;
|
||||
}
|
||||
|
||||
template<KeyType _Type = KeyType::PrivateKey>
|
||||
template<RSAKeyType _Type = RSAKeyType::PrivateKey>
|
||||
int Decrypt(const void* from, int len, void* to, int padding) {
|
||||
int write_bytes = 0;
|
||||
int write_bytes;
|
||||
|
||||
if (_Type == KeyType::PrivateKey) {
|
||||
if constexpr (_Type == RSAKeyType::PrivateKey) {
|
||||
write_bytes = RSA_private_decrypt(len,
|
||||
reinterpret_cast<const unsigned char*>(from),
|
||||
reinterpret_cast<unsigned char*>(to),
|
||||
_RsaObj.GetPointer(),
|
||||
_$$_RsaObj,
|
||||
padding);
|
||||
if (write_bytes == -1)
|
||||
throw OpensslError(__FILE__, __LINE__, ERR_get_error(),
|
||||
"RSA_private_decrypt fails.");
|
||||
} else {
|
||||
write_bytes = RSA_public_decrypt(len,
|
||||
reinterpret_cast<const unsigned char*>(from),
|
||||
reinterpret_cast<unsigned char*>(to),
|
||||
_RsaObj.GetPointer(),
|
||||
_$$_RsaObj,
|
||||
padding);
|
||||
if (write_bytes == -1)
|
||||
throw OpensslError(__FILE__, __LINE__, ERR_get_error(),
|
||||
"RSA_public_decrypt fails.");
|
||||
}
|
||||
|
||||
if (write_bytes == -1)
|
||||
write_bytes = 0;
|
||||
return write_bytes;
|
||||
}
|
||||
|
||||
|
||||
96
navicat-patcher/ResourceObject.hpp
Normal file
96
navicat-patcher/ResourceObject.hpp
Normal file
@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
#include <type_traits>
|
||||
|
||||
template<typename __ResourceTraits>
|
||||
class ResourceObject {
|
||||
public:
|
||||
using HandleType = typename __ResourceTraits::HandleType;
|
||||
static inline const HandleType& InvalidValue = __ResourceTraits::InvalidValue;
|
||||
protected:
|
||||
HandleType _$_Handle;
|
||||
public:
|
||||
|
||||
ResourceObject() noexcept :
|
||||
_$_Handle(InvalidValue) {}
|
||||
|
||||
ResourceObject(const HandleType& Handle) noexcept :
|
||||
_$_Handle(Handle) {}
|
||||
|
||||
ResourceObject(const ResourceObject<__ResourceTraits>&) = delete;
|
||||
|
||||
ResourceObject(ResourceObject<__ResourceTraits>&& Other) noexcept :
|
||||
_$_Handle(Other._$_Handle)
|
||||
{
|
||||
Other._$_Handle = InvalidValue;
|
||||
}
|
||||
|
||||
ResourceObject<__ResourceTraits>&
|
||||
operator=(const ResourceObject<__ResourceTraits>& Other) = delete;
|
||||
|
||||
ResourceObject<__ResourceTraits>&
|
||||
operator=(ResourceObject<__ResourceTraits>&& Other) noexcept {
|
||||
_$_Handle = Other._$_Handle;
|
||||
Other._$_Handle = InvalidValue;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename __DummyType = int,
|
||||
typename = typename std::enable_if<std::is_pointer<HandleType>::value, __DummyType>::type>
|
||||
HandleType operator->() const noexcept {
|
||||
return _$_Handle;
|
||||
}
|
||||
|
||||
operator HandleType() const noexcept {
|
||||
return _$_Handle;
|
||||
}
|
||||
|
||||
// Check if handle is a valid handle
|
||||
bool IsValid() const noexcept {
|
||||
return _$_Handle != InvalidValue;
|
||||
}
|
||||
|
||||
HandleType RetrieveHandle() const noexcept {
|
||||
return _$_Handle;
|
||||
}
|
||||
|
||||
void Abandon() noexcept {
|
||||
_$_Handle = InvalidValue;
|
||||
}
|
||||
|
||||
void TakeOver(const HandleType& Handle) {
|
||||
if (_$_Handle != InvalidValue)
|
||||
Release();
|
||||
_$_Handle = Handle;
|
||||
}
|
||||
|
||||
// Force release
|
||||
void Release() {
|
||||
if (_$_Handle != InvalidValue) {
|
||||
__ResourceTraits::Releasor(_$_Handle);
|
||||
_$_Handle = InvalidValue;
|
||||
}
|
||||
}
|
||||
|
||||
~ResourceObject() {
|
||||
Release();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename __ClassType>
|
||||
struct CppObjectTraits {
|
||||
using HandleType = __ClassType*;
|
||||
static inline const HandleType InvalidValue = nullptr;
|
||||
static inline void Releasor(HandleType pObject) {
|
||||
delete pObject;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename __ClassType>
|
||||
struct CppDynamicArrayTraits {
|
||||
using HandleType = __ClassType*;
|
||||
static inline const HandleType InvalidValue = nullptr;
|
||||
static inline void Releasor(HandleType pArray) {
|
||||
delete[] pArray;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,84 +0,0 @@
|
||||
#include "Solutions.hpp"
|
||||
#include "Helper.hpp"
|
||||
|
||||
namespace Patcher {
|
||||
|
||||
const char Solution0::Keyword[452] =
|
||||
"-----BEGIN PUBLIC KEY-----\x00"
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw1dqF3SkCaAAmMzs889I\x00"
|
||||
"qdW9M2dIdh3jG9yPcmLnmJiGpBF4E9VHSMGe8oPAy2kJDmdNt4BcEygvssEfginv\x00"
|
||||
"a5t5jm352UAoDosUJkTXGQhpAWMF4fBmBpO3EedG62rOsqMBgmSdAyxCSPBRJIOF\x00"
|
||||
"R0QgZFbRnU0frj34fiVmgYiLuZSAmIbs8ZxiHPdp1oD4tUpvsFci4QJtYNjNnGU2\x00"
|
||||
"WPH6rvChGl1IRKrxMtqLielsvajUjyrgOC6NmymYMvZNER3htFEtL1eQbCyTfDmt\x00"
|
||||
"YyQ1Wt4Ot12lxf0wVIR5mcGN7XCXJRHOFHSf1gzXWabRSvmt1nrl7sW6cjxljuuQ\x00"
|
||||
"awIDAQAB\x00"
|
||||
"-----END PUBLIC KEY-----\x00";
|
||||
|
||||
bool Solution0::FindPatchOffset() noexcept {
|
||||
bool bFound = false;
|
||||
|
||||
uint8_t* pFileView = pTargetFile->GetView<uint8_t>();
|
||||
off_t FileSize;
|
||||
|
||||
if (pFileView == nullptr)
|
||||
return false;
|
||||
|
||||
if (!pTargetFile->GetFileSize(FileSize))
|
||||
return false;
|
||||
|
||||
if (FileSize < KeywordLength)
|
||||
return false;
|
||||
|
||||
FileSize -= KeywordLength;
|
||||
for (off_t i = 0; i < FileSize; ++i) {
|
||||
if (pFileView[i] == Keyword[0] && memcmp(pFileView + i, Keyword, KeywordLength) == 0) {
|
||||
PatchOffset = i;
|
||||
bFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bFound)
|
||||
printf("MESSAGE: [Solution0] Keyword has been found: offset = +0x%08lx.\n",
|
||||
static_cast<unsigned long>(PatchOffset));
|
||||
return bFound;
|
||||
}
|
||||
|
||||
bool Solution0::MakePatch(RSACipher* cipher) const {
|
||||
uint8_t* lpTargetFileView = pTargetFile->GetView<uint8_t>();
|
||||
std::string RSAPublicKeyPEM =
|
||||
cipher->ExportKeyString<RSACipher::KeyType::PublicKey, RSACipher::KeyFormat::PEM>();
|
||||
|
||||
if (RSAPublicKeyPEM.empty()) {
|
||||
REPORT_ERROR("ERROR: cipher->ExportKeyString failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < RSAPublicKeyPEM.length(); ++i) {
|
||||
if (RSAPublicKeyPEM[i] == '\n')
|
||||
RSAPublicKeyPEM[i] = '\x00';
|
||||
}
|
||||
|
||||
if (RSAPublicKeyPEM.length() != KeywordLength) {
|
||||
REPORT_ERROR("ERROR: Public key length does not match.");
|
||||
return false;
|
||||
}
|
||||
|
||||
PRINT_MESSAGE("//");
|
||||
PRINT_MESSAGE("// Begin Solution0");
|
||||
PRINT_MESSAGE("//");
|
||||
printf("@+0x%08llX\nPrevious:\n", PatchOffset);
|
||||
Helper::PrintMemory(lpTargetFileView + PatchOffset,
|
||||
lpTargetFileView + PatchOffset + KeywordLength,
|
||||
lpTargetFileView);
|
||||
|
||||
memcpy(lpTargetFileView + PatchOffset, RSAPublicKeyPEM.c_str(), KeywordLength);
|
||||
|
||||
PRINT_MESSAGE("After:");
|
||||
Helper::PrintMemory(lpTargetFileView + PatchOffset,
|
||||
lpTargetFileView + PatchOffset + KeywordLength,
|
||||
lpTargetFileView);
|
||||
PRINT_MESSAGE("");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1,116 +0,0 @@
|
||||
#include "Solutions.hpp"
|
||||
#include "Helper.hpp"
|
||||
|
||||
namespace Patcher {
|
||||
|
||||
const uint8_t Solution1::Keyword[KeywordLength] = {
|
||||
0xfe, 0xfd, 0xfc, 0xf4, 0xfe, 0xd2, 0xf8, 0xf4, 0xf1, 0xd3, 0xde, 0xc7, 0xdf, 0xd3, 0xd0, 0xfd,
|
||||
0x8a, 0xc3, 0x85, 0xf4, 0xf6, 0xe9, 0xfc, 0xfc, 0xf2, 0xf5, 0xfa, 0xf5, 0xf6, 0xe9, 0x81, 0xfb,
|
||||
0xfe, 0xfd, 0xfc, 0xf4, 0xf4, 0xdf, 0xf2, 0xf9, 0xf2, 0xe5, 0xf0, 0xf7, 0xc0, 0x89, 0xdd, 0xcb,
|
||||
0xf5, 0x87, 0xe6, 0xdd, 0xf4, 0xd9, 0xf8, 0xfb, 0xde, 0xf9, 0xcf, 0xc5, 0x8f, 0x80, 0x80, 0xf3,
|
||||
0xc2, 0xd0, 0xe2, 0x8f, 0xfa, 0x8a, 0xdd, 0xf3, 0xd7, 0xdc, 0x86, 0xdc, 0xf0, 0x81, 0xc0, 0xea,
|
||||
0xd0, 0xd9, 0xf9, 0xd8, 0xda, 0xf2, 0xd0, 0xfd, 0xc3, 0xf6, 0xf3, 0x82, 0xf2, 0x81, 0xef, 0xf2,
|
||||
0xe0, 0xf9, 0xf2, 0xd3, 0x8f, 0xd7, 0xe9, 0xfb, 0xca, 0x86, 0xde, 0xfc, 0xf3, 0xd5, 0xdd, 0xf4,
|
||||
0xc7, 0x80, 0xf7, 0xd5, 0xf2, 0xc1, 0xde, 0xcc, 0xc0, 0xc7, 0xf0, 0xd0, 0xd0, 0xd1, 0xd7, 0xcc,
|
||||
0xd2, 0x81, 0xc1, 0x83, 0xdd, 0xd5, 0x8a, 0x8f, 0x81, 0xe1, 0xf4, 0xd9, 0xf3, 0xd7, 0xca, 0xef,
|
||||
0xf9, 0xdf, 0xe1, 0xee, 0xf0, 0xe9, 0xd1, 0xca, 0xf2, 0xe3, 0xf8, 0xf0, 0x83, 0xde, 0xfb, 0xd7,
|
||||
0xf1, 0xc4, 0xfa, 0x85, 0xf2, 0xdd, 0xdd, 0xfd, 0x85, 0x86, 0xc7, 0xf9, 0xc4, 0xc9, 0xf4, 0xf8,
|
||||
0xd4, 0xd9, 0xe6, 0xd2, 0xf6, 0xc1, 0xc1, 0xf9, 0xe0, 0xe4, 0xf7, 0xe4, 0xfd, 0xf1, 0xf6, 0xfc,
|
||||
0xe1, 0x84, 0xe4, 0xd1, 0xed, 0xfe, 0xdb, 0xe8, 0xdd, 0xe1, 0x85, 0xd0, 0xc5, 0xd2, 0x8a, 0x8e,
|
||||
0xd5, 0xdd, 0xe3, 0xdb, 0xd0, 0xe1, 0xd0, 0xf6, 0xc6, 0xee, 0xe6, 0xf7, 0xda, 0xf1, 0xdb, 0xc9,
|
||||
0x8b, 0xee, 0xcd, 0xdf, 0xff, 0xe8, 0xdd, 0xca, 0x82, 0xdb, 0xf1, 0x82, 0xc3, 0xed, 0xc9, 0xcc,
|
||||
0xc0, 0xf2, 0xd6, 0xdf, 0x83, 0xe9, 0xf3, 0xce, 0xea, 0xfa, 0xdf, 0xf8, 0xd9, 0xff, 0xec, 0x88,
|
||||
0xe4, 0xe4, 0xfd, 0x80, 0xc5, 0xce, 0xfa, 0xd2, 0xf4, 0xd8, 0x84, 0xff, 0xe5, 0xf3, 0xcb, 0xc2,
|
||||
0xfe, 0xc0, 0xc4, 0xfa, 0xde, 0xdd, 0xd5, 0xc9, 0xc5, 0xd5, 0xdf, 0xe3, 0xdd, 0xc1, 0xcb, 0xdd,
|
||||
0xfc, 0xf7, 0x83, 0xf8, 0xda, 0xc1, 0xd4, 0xe3, 0xfe, 0xc2, 0xef, 0xf8, 0xf2, 0xea, 0x8a, 0xd2,
|
||||
0xc7, 0xf2, 0xf0, 0xc2, 0xfb, 0x89, 0xdc, 0xeb, 0xd1, 0xf7, 0xcc, 0xe2, 0xd1, 0xfc, 0xd4, 0xce,
|
||||
0xea, 0xcd, 0xe4, 0x87, 0xe0, 0xcc, 0x8d, 0xf5, 0xc7, 0x85, 0x87, 0xda, 0xcf, 0xde, 0x89, 0xcd,
|
||||
0xe5, 0xfd, 0xe7, 0x83, 0xda, 0xdb, 0xfe, 0xf4, 0x84, 0xec, 0xf6, 0xee, 0xfd, 0xea, 0xf1, 0xf5,
|
||||
0xf5, 0xfc, 0xe6, 0xd0, 0x86, 0xdf, 0xc3, 0xe2, 0xe4, 0xd5, 0xd7, 0xe4, 0xe4, 0xce, 0xd4, 0xce,
|
||||
0x82, 0xda, 0xc7, 0xda, 0x80, 0xcb, 0xee, 0x8c, 0xd0, 0xde, 0xcd, 0xda, 0xdd, 0xcd, 0xcc, 0xeb,
|
||||
0xd2, 0xc3, 0xfc, 0xf2, 0xf6, 0xe9, 0xf8, 0xf8
|
||||
};
|
||||
|
||||
bool Solution1::FindPatchOffset() noexcept {
|
||||
bool bFound = false;
|
||||
|
||||
uint8_t* pFileView = pTargetFile->GetView<uint8_t>();
|
||||
off_t FileSize;
|
||||
|
||||
if (pFileView == nullptr)
|
||||
return false;
|
||||
|
||||
if (!pTargetFile->GetFileSize(FileSize))
|
||||
return false;
|
||||
|
||||
if (FileSize < KeywordLength)
|
||||
return false;
|
||||
|
||||
FileSize -= KeywordLength;
|
||||
for (off_t i = 0; i < FileSize; ++i) {
|
||||
if (pFileView[i] == Keyword[0] && memcmp(pFileView + i, Keyword, KeywordLength) == 0) {
|
||||
PatchOffset = i;
|
||||
bFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bFound)
|
||||
printf("MESSAGE: [Solution1] Keyword has been found: offset = +0x%08lx.\n",
|
||||
static_cast<unsigned long>(PatchOffset));
|
||||
return bFound;
|
||||
}
|
||||
|
||||
bool Solution1::MakePatch(RSACipher* cipher) const {
|
||||
uint8_t* lpTargetFileView = pTargetFile->GetView<uint8_t>();
|
||||
std::string RSAPublicKeyPEM =
|
||||
cipher->ExportKeyString<RSACipher::KeyType::PublicKey, RSACipher::KeyFormat::PEM>();
|
||||
|
||||
if (RSAPublicKeyPEM.empty()) {
|
||||
REPORT_ERROR("ERROR: cipher->ExportKeyString failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
RSAPublicKeyPEM.erase(RSAPublicKeyPEM.find("-----BEGIN PUBLIC KEY-----"), 26);
|
||||
RSAPublicKeyPEM.erase(RSAPublicKeyPEM.find("-----END PUBLIC KEY-----"), 24);
|
||||
{
|
||||
std::string::size_type pos = 0;
|
||||
while ((pos = RSAPublicKeyPEM.find("\n", pos)) != std::string::npos) {
|
||||
RSAPublicKeyPEM.erase(pos, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (RSAPublicKeyPEM.length() != KeywordLength) {
|
||||
REPORT_ERROR("ERROR: Public key length does not match.");
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
uint8_t key = 8;
|
||||
for (size_t i = 0; i < RSAPublicKeyPEM.length(); ++i) {
|
||||
if (key == 0)
|
||||
key = 8;
|
||||
RSAPublicKeyPEM[i] ^= 0xbb - key;
|
||||
--key;
|
||||
}
|
||||
}
|
||||
|
||||
PRINT_MESSAGE("//");
|
||||
PRINT_MESSAGE("// Begin Solution1");
|
||||
PRINT_MESSAGE("//");
|
||||
printf("@+0x%08llX\nPrevious:\n", PatchOffset);
|
||||
Helper::PrintMemory(lpTargetFileView + PatchOffset,
|
||||
lpTargetFileView + PatchOffset + KeywordLength,
|
||||
lpTargetFileView);
|
||||
|
||||
memcpy(lpTargetFileView + PatchOffset, RSAPublicKeyPEM.c_str(), KeywordLength);
|
||||
|
||||
PRINT_MESSAGE("After:");
|
||||
Helper::PrintMemory(lpTargetFileView + PatchOffset,
|
||||
lpTargetFileView + PatchOffset + KeywordLength,
|
||||
lpTargetFileView);
|
||||
PRINT_MESSAGE("");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,78 +0,0 @@
|
||||
#pragma once
|
||||
#include "FileMapper.hpp"
|
||||
#include "RSACipher.hpp"
|
||||
|
||||
namespace Patcher {
|
||||
|
||||
class Solution {
|
||||
public:
|
||||
virtual void SetFile(FileMapper* pFile) = 0;
|
||||
virtual bool CheckKey(RSACipher* cipher) const = 0;
|
||||
virtual bool FindPatchOffset() = 0;
|
||||
virtual bool MakePatch(RSACipher* cipher) const = 0;
|
||||
virtual ~Solution() = default;
|
||||
};
|
||||
|
||||
// Solution0 will replace the RSA public key stored in main application.
|
||||
class Solution0 : public Solution {
|
||||
private:
|
||||
static constexpr size_t KeywordLength = 451;
|
||||
static const char Keyword[KeywordLength + 1];
|
||||
|
||||
FileMapper* pTargetFile;
|
||||
off_t PatchOffset;
|
||||
public:
|
||||
|
||||
Solution0() noexcept :
|
||||
pTargetFile(nullptr),
|
||||
PatchOffset(-1) {}
|
||||
|
||||
virtual void SetFile(FileMapper* pMainApp) noexcept override {
|
||||
pTargetFile = pMainApp;
|
||||
}
|
||||
|
||||
// Solution0 does not have any requirements for an RSA-2048 key
|
||||
virtual bool CheckKey(RSACipher* cipher) const noexcept override {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return true if found, other return false
|
||||
virtual bool FindPatchOffset() noexcept override;
|
||||
|
||||
// Make a patch based on an RSA private key given
|
||||
// Return true if success, otherwise return false
|
||||
virtual bool MakePatch(RSACipher* cipher) const override;
|
||||
};
|
||||
|
||||
// Solution1 will replace the RSA public key stored in main application.
|
||||
class Solution1 : public Solution {
|
||||
private:
|
||||
static constexpr size_t KeywordLength = 0x188;
|
||||
static const uint8_t Keyword[KeywordLength];
|
||||
|
||||
FileMapper* pTargetFile;
|
||||
off_t PatchOffset;
|
||||
public:
|
||||
Solution1() :
|
||||
pTargetFile(nullptr),
|
||||
PatchOffset(-1) {}
|
||||
|
||||
virtual void SetFile(FileMapper* pLibccFile) noexcept override {
|
||||
pTargetFile = pLibccFile;
|
||||
}
|
||||
|
||||
// Solution1 has no requirements for an RSA-2048 key
|
||||
virtual bool CheckKey(RSACipher* cipher) const noexcept override {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return true if found, otherwise return false
|
||||
virtual bool FindPatchOffset() noexcept override;
|
||||
|
||||
// Make a patch based on an RSA private key given
|
||||
// Return true if success, otherwise return false
|
||||
virtual bool MakePatch(RSACipher* cipher) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
64
navicat-patcher/SystemObjectTraits.hpp
Normal file
64
navicat-patcher/SystemObjectTraits.hpp
Normal file
@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
struct FileHandleTraits {
|
||||
using HandleType = int;
|
||||
static inline const HandleType InvalidValue = -1;
|
||||
static constexpr auto& Releasor = close;
|
||||
};
|
||||
|
||||
struct MapViewTraits {
|
||||
|
||||
struct HandleType {
|
||||
void* _Ptr;
|
||||
size_t _Size;
|
||||
|
||||
bool operator==(const HandleType& Other) const noexcept {
|
||||
if (_Ptr == MAP_FAILED && Other._Ptr == MAP_FAILED)
|
||||
return true;
|
||||
else
|
||||
return _Ptr == Other._Ptr && _Size == Other._Size;
|
||||
}
|
||||
|
||||
bool operator!=(const HandleType& Other) const noexcept {
|
||||
return !(*this == Other);
|
||||
}
|
||||
|
||||
operator void*() const noexcept {
|
||||
return _Ptr;
|
||||
}
|
||||
|
||||
template<typename __Type>
|
||||
__Type* View() const noexcept {
|
||||
|
||||
return reinterpret_cast<__Type*>(_Ptr);
|
||||
}
|
||||
|
||||
template<typename __Type>
|
||||
const __Type* ConstView() const noexcept {
|
||||
return reinterpret_cast<__Type*>(_Ptr);
|
||||
}
|
||||
|
||||
template<typename __Type>
|
||||
__Type* ViewAtOffset(size_t Offset) const noexcept {
|
||||
return reinterpret_cast<__Type*>(reinterpret_cast<char*>(_Ptr) + Offset);
|
||||
}
|
||||
|
||||
template<typename __Type>
|
||||
const __Type* ConstViewAtOffset(size_t Offset) const noexcept {
|
||||
return reinterpret_cast<__Type*>(reinterpret_cast<char*>(_Ptr) + Offset);
|
||||
}
|
||||
|
||||
size_t Size() const noexcept {
|
||||
return _Size;
|
||||
}
|
||||
};
|
||||
|
||||
static inline const HandleType InvalidValue = { MAP_FAILED, 0 };
|
||||
|
||||
static inline void Releasor(const HandleType& RefHandle) {
|
||||
munmap(RefHandle._Ptr, RefHandle._Size);
|
||||
};
|
||||
};
|
||||
|
||||
191
navicat-patcher/X64ImageInterpreter.hpp
Normal file
191
navicat-patcher/X64ImageInterpreter.hpp
Normal file
@ -0,0 +1,191 @@
|
||||
#pragma once
|
||||
#include <string.h>
|
||||
#include <mach-o/loader.h>
|
||||
#include <mach-o/nlist.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "Exception.hpp"
|
||||
|
||||
class X64ImageInterpreter {
|
||||
public:
|
||||
static constexpr uint64_t InvalidAddress = static_cast<uint64_t>(-1);
|
||||
static constexpr uint32_t InvalidOffset = static_cast<uint32_t>(-1);
|
||||
private:
|
||||
mach_header_64* _$$_MachHeader;
|
||||
std::vector<segment_command_64*> _$$_SegmentCommands;
|
||||
std::map<size_t, section_64*> _$$_SectionIndexTable;
|
||||
std::map<uint64_t, section_64*> _$$_SectionMapTable;
|
||||
std::map<uint32_t, section_64*> _$$_SectionOffsetTable;
|
||||
dysymtab_command* _$$_DySymTabCommand;
|
||||
symtab_command* _$$_SymTabCommand;
|
||||
char* _$$_StringTable;
|
||||
nlist_64* _$$_SymbolTable;
|
||||
dyld_info_command* _$$_DyldInfoCommand;
|
||||
public:
|
||||
|
||||
X64ImageInterpreter() :
|
||||
_$$_MachHeader(nullptr),
|
||||
_$$_DySymTabCommand(nullptr),
|
||||
_$$_SymTabCommand(nullptr),
|
||||
_$$_StringTable(nullptr),
|
||||
_$$_SymbolTable(nullptr),
|
||||
_$$_DyldInfoCommand(nullptr) {}
|
||||
|
||||
void LoadImage(void* ImageBase) {
|
||||
mach_header_64* MachHeader = nullptr;
|
||||
std::vector<segment_command_64*> SegmentComamnds;
|
||||
std::map<size_t, section_64*> SectionIndexTable;
|
||||
std::map<uint64_t, section_64*> SectionMapTable;
|
||||
std::map<uint32_t, section_64*> SectionOffsetTable;
|
||||
dysymtab_command* DySymTabCommand = nullptr;
|
||||
symtab_command* SymTabCommand = nullptr;
|
||||
char* StringTable = nullptr;
|
||||
nlist_64* SymbolTable = nullptr;
|
||||
dyld_info_command* DyldInfoCommand = nullptr;
|
||||
|
||||
MachHeader = reinterpret_cast<mach_header_64*>(ImageBase);
|
||||
if (MachHeader->magic != MH_MAGIC_64) {
|
||||
throw Exception(__FILE__, __LINE__,
|
||||
"Bad Image");
|
||||
}
|
||||
|
||||
auto cmd_p = reinterpret_cast<load_command*>(MachHeader + 1);
|
||||
for (uint32_t i = 0; i < MachHeader->ncmds; ++i) {
|
||||
if (cmd_p->cmd == LC_SEGMENT_64) {
|
||||
auto segcmd_p = reinterpret_cast<segment_command_64*>(cmd_p);
|
||||
auto sec_p = reinterpret_cast<section_64*>(segcmd_p + 1);
|
||||
|
||||
SegmentComamnds.emplace_back(segcmd_p);
|
||||
|
||||
for (uint32_t j = 0; j < segcmd_p->nsects; ++j) {
|
||||
SectionIndexTable[SectionIndexTable.size()] = &sec_p[j];
|
||||
SectionMapTable[sec_p[j].addr] = &sec_p[j];
|
||||
SectionOffsetTable[sec_p[j].offset] = &sec_p[j];
|
||||
}
|
||||
} else if (cmd_p->cmd == LC_DYSYMTAB) {
|
||||
DySymTabCommand = reinterpret_cast<dysymtab_command*>(cmd_p);
|
||||
} else if (cmd_p->cmd == LC_SYMTAB) {
|
||||
SymTabCommand = reinterpret_cast<symtab_command*>(cmd_p);
|
||||
StringTable = reinterpret_cast<char*>(ImageBase) + SymTabCommand->stroff;
|
||||
SymbolTable = reinterpret_cast<nlist_64*>(reinterpret_cast<uint8_t*>(ImageBase) + SymTabCommand->symoff);
|
||||
} else if (cmd_p->cmd == LC_DYLD_INFO_ONLY) {
|
||||
DyldInfoCommand = reinterpret_cast<dyld_info_command*>(cmd_p);
|
||||
}
|
||||
|
||||
cmd_p = reinterpret_cast<load_command*>(
|
||||
reinterpret_cast<uint8_t*>(cmd_p) + cmd_p->cmdsize
|
||||
);
|
||||
}
|
||||
|
||||
std::swap(_$$_MachHeader, MachHeader);
|
||||
std::swap(_$$_SegmentCommands, SegmentComamnds);
|
||||
std::swap(_$$_SectionIndexTable, SectionIndexTable);
|
||||
std::swap(_$$_SectionMapTable, SectionMapTable);
|
||||
std::swap(_$$_SectionOffsetTable, SectionOffsetTable);
|
||||
std::swap(_$$_DySymTabCommand, DySymTabCommand);
|
||||
std::swap(_$$_SymTabCommand, SymTabCommand);
|
||||
std::swap(_$$_StringTable, StringTable);
|
||||
std::swap(_$$_SymbolTable, SymbolTable);
|
||||
std::swap(_$$_DyldInfoCommand, DyldInfoCommand);
|
||||
}
|
||||
|
||||
dysymtab_command* DySymTabCommand() const noexcept {
|
||||
return _$$_DySymTabCommand;
|
||||
}
|
||||
|
||||
symtab_command* SymTabCommand() const noexcept {
|
||||
return _$$_SymTabCommand;
|
||||
}
|
||||
|
||||
dyld_info_command* DyldInfoCommand() const noexcept {
|
||||
return _$$_DyldInfoCommand;
|
||||
}
|
||||
|
||||
uint64_t OffsetToAddress(uint32_t Offset) const {
|
||||
auto sec_p = SectionByOffset(Offset);
|
||||
if (sec_p) {
|
||||
return sec_p->addr + (Offset - sec_p->offset);
|
||||
} else {
|
||||
return InvalidAddress;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t AddressToOffset(uint64_t Address) const {
|
||||
auto sec_p = SectionByAddress(Address);
|
||||
if (sec_p) {
|
||||
return sec_p->offset + static_cast<uint32_t>(Address - sec_p->addr);
|
||||
} else {
|
||||
return InvalidOffset;
|
||||
}
|
||||
}
|
||||
|
||||
section_64* SectionByIndex(size_t Index) const {
|
||||
auto it = _$$_SectionIndexTable.find(Index);
|
||||
return it != _$$_SectionIndexTable.cend() ? it->second : nullptr;
|
||||
}
|
||||
|
||||
section_64* SectionByName(const char* SegmentName, const char* SectionName) const {
|
||||
if (_$$_MachHeader == nullptr)
|
||||
return nullptr;
|
||||
|
||||
for (auto segcmd_p : _$$_SegmentCommands) {
|
||||
if (strncmp(SegmentName, segcmd_p->segname, 16) == 0) {
|
||||
auto sec_p = reinterpret_cast<section_64*>(segcmd_p + 1);
|
||||
for (uint32_t j = 0; j < segcmd_p->nsects; ++j) {
|
||||
if (strncmp(SectionName, sec_p[j].sectname, 16) == 0)
|
||||
return &sec_p[j];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
section_64* SectionByOffset(uint32_t Offset) const {
|
||||
if (!_$$_SectionOffsetTable.empty()) {
|
||||
auto upper = _$$_SectionOffsetTable.upper_bound(Offset);
|
||||
if (upper == _$$_SectionOffsetTable.cbegin())
|
||||
return nullptr;
|
||||
auto lower = std::prev(upper);
|
||||
|
||||
if (lower->second->offset <= Offset && Offset < lower->second->offset + lower->second->size) {
|
||||
return lower->second;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
section_64* SectionByAddress(uint64_t Address) const {
|
||||
if (!_$$_SectionMapTable.empty()) {
|
||||
auto upper = _$$_SectionMapTable.upper_bound(Address);
|
||||
if (upper == _$$_SectionMapTable.cbegin())
|
||||
return nullptr;
|
||||
auto lower = std::prev(upper);
|
||||
|
||||
if (lower->second->addr <= Address && Address < lower->second->addr + lower->second->size) {
|
||||
return lower->second;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nlist_64* SymbolTable() const noexcept {
|
||||
return _$$_SymbolTable;
|
||||
}
|
||||
|
||||
char* LookupStringTable(size_t Offset) const noexcept {
|
||||
return _$$_StringTable + Offset;
|
||||
}
|
||||
|
||||
~X64ImageInterpreter() {
|
||||
_$$_MachHeader = nullptr;
|
||||
}
|
||||
};
|
||||
@ -1,156 +1,152 @@
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include "PatchSolutions.hpp"
|
||||
#include "KeystoneAssembler.hpp"
|
||||
|
||||
#include "Helper.hpp"
|
||||
#include "FileMapper.hpp"
|
||||
#include "RSACipher.hpp"
|
||||
#include "Solutions.hpp"
|
||||
|
||||
#define SAFE_DELETE(x) { delete x; x = nullptr; }
|
||||
|
||||
void help() {
|
||||
puts("Usage:");
|
||||
puts(" ./navicat-patcher <navicat executable file> [RSA-2048 PrivateKey(PEM file)]");
|
||||
static void Welcome() {
|
||||
puts("***************************************************");
|
||||
puts("* Navicat Patcher by @DoubleLabyrinth *");
|
||||
printf("* Release date: %-24s*\n", __DATE__);
|
||||
puts("***************************************************");
|
||||
puts("");
|
||||
puts("Press Enter to continue or Ctrl + C to abort.");
|
||||
getchar();
|
||||
}
|
||||
|
||||
bool LoadKey(RSACipher* cipher, const char* filename,
|
||||
Patcher::Solution* pSolution0,
|
||||
Patcher::Solution* pSolution1) {
|
||||
if (filename) {
|
||||
if (!cipher->ImportKeyFromFile<RSACipher::KeyType::PrivateKey, RSACipher::KeyFormat::PEM>(filename)) {
|
||||
REPORT_ERROR("ERROR: cipher->ImportKeyFromFile failed.");
|
||||
return false;
|
||||
}
|
||||
static void Help() {
|
||||
puts("***************************************************");
|
||||
puts("* Navicat Patcher by @DoubleLabyrinth *");
|
||||
printf("* Release date: %-24s*\n", __DATE__);
|
||||
puts("***************************************************");
|
||||
puts("");
|
||||
puts("Usage:");
|
||||
puts(" navicat-patcher.exe <Navicat installation path> [RSA-2048 PEM file]");
|
||||
}
|
||||
|
||||
if ((pSolution0 && !pSolution0->CheckKey(cipher)) ||
|
||||
(pSolution1 && !pSolution1->CheckKey(cipher))) {
|
||||
REPORT_ERROR("ERROR: The RSA private key you provided cannot be used.");
|
||||
return false;
|
||||
}
|
||||
static void LoadKey(RSACipher* pCipher, const char* FileName,
|
||||
PatchSolution* pSolution0,
|
||||
PatchSolution* pSolution1,
|
||||
PatchSolution* pSolution2) {
|
||||
if (FileName) {
|
||||
pCipher->ImportKeyFromFile<RSAKeyType::PrivateKey, RSAKeyFormat::PEM>(FileName);
|
||||
|
||||
if ((pSolution0 && !pSolution0->CheckKey(pCipher)) ||
|
||||
(pSolution1 && !pSolution1->CheckKey(pCipher)) ||
|
||||
(pSolution2 && !pSolution2->CheckKey(pCipher)))
|
||||
throw Exception(__FILE__, __LINE__,
|
||||
"The RSA private key you provide cannot be used.");
|
||||
} else {
|
||||
PRINT_MESSAGE("");
|
||||
PRINT_MESSAGE("MESSAGE: Generating new RSA private key, it may take a long time.");
|
||||
puts("MESSAGE: Generating new RSA private key, it may take a long time.");
|
||||
|
||||
do {
|
||||
cipher->GenerateKey(2048);
|
||||
} while ((pSolution0 && !pSolution0->CheckKey(cipher)) ||
|
||||
(pSolution1 && !pSolution1->CheckKey(cipher))); // re-generate RSA key if one of CheckKey return false
|
||||
pCipher->GenerateKey(2048);
|
||||
} while ((pSolution0 && !pSolution0->CheckKey(pCipher)) ||
|
||||
(pSolution1 && !pSolution1->CheckKey(pCipher)) ||
|
||||
(pSolution2 && !pSolution2->CheckKey(pCipher))); // re-generate RSA key if CheckKey return false
|
||||
|
||||
if (!cipher->ExportKeyToFile<RSACipher::KeyType::PrivateKey, RSACipher::KeyFormat::NotSpecified>("RegPrivateKey.pem")) {
|
||||
REPORT_ERROR("ERROR: Failed to save RSA private key.");
|
||||
return false;
|
||||
pCipher->ExportKeyToFile<RSAKeyType::PrivateKey, RSAKeyFormat::NotSpecified>("RegPrivateKey.pem");
|
||||
|
||||
puts("MESSAGE: New RSA private key has been saved to RegPrivateKey.pem.");
|
||||
}
|
||||
|
||||
std::string PublicKeyPEM =
|
||||
pCipher->ExportKeyString<RSAKeyType::PublicKey, RSAKeyFormat::PEM>();
|
||||
|
||||
puts("");
|
||||
puts("Your RSA public key:");
|
||||
puts(PublicKeyPEM.c_str());
|
||||
}
|
||||
|
||||
static void ExceptionReport(const Exception& e) noexcept {
|
||||
printf("ERROR: FileName %s - Line %zu\n", e.SourceFile(), e.SourceLine());
|
||||
if (e.HasErrorCode()) {
|
||||
const char* aa = e.ErrorString();
|
||||
printf("ErrorCode = 0x%08lx\n", e.ErrorCode());
|
||||
printf("ErrorString: %s\n", e.ErrorString());
|
||||
} else {
|
||||
printf("%s\n", e.CustomMessage());
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc != 2 && argc != 3) {
|
||||
Help();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Welcome();
|
||||
|
||||
ResourceObject<FileHandleTraits> MainAppFile;
|
||||
ResourceObject<MapViewTraits> MainAppMapView;
|
||||
ResourceObject<CppObjectTraits<RSACipher>> pCipher;
|
||||
ResourceObject<CppObjectTraits<PatchSolution>> pSolution0;
|
||||
ResourceObject<CppObjectTraits<PatchSolution>> pSolution1;
|
||||
ResourceObject<CppObjectTraits<PatchSolution>> pSolution2;
|
||||
|
||||
try {
|
||||
MainAppFile.TakeOver(open(argv[1], O_RDWR));
|
||||
if (!MainAppFile.IsValid())
|
||||
throw SystemError(__FILE__, __LINE__, errno,
|
||||
"open fails.");
|
||||
|
||||
struct stat stat_buf = {};
|
||||
if (fstat(MainAppFile, &stat_buf) != 0)
|
||||
throw SystemError(__FILE__, __LINE__, errno,
|
||||
"fstat fails.");
|
||||
|
||||
MainAppMapView.TakeOver({
|
||||
mmap(nullptr, static_cast<size_t>(stat_buf.st_size), PROT_READ | PROT_WRITE, MAP_SHARED, MainAppFile, 0),
|
||||
static_cast<size_t>(stat_buf.st_size)
|
||||
});
|
||||
if (!MainAppMapView.IsValid())
|
||||
throw SystemError(__FILE__, __LINE__, errno,
|
||||
"mmap fails.");
|
||||
|
||||
pCipher.TakeOver(new RSACipher());
|
||||
pSolution0.TakeOver(new PatchSolution0());
|
||||
pSolution1.TakeOver(new PatchSolution1());
|
||||
pSolution2.TakeOver(new PatchSolution2());
|
||||
|
||||
pSolution0->SetFile(MainAppMapView);
|
||||
pSolution1->SetFile(MainAppMapView);
|
||||
pSolution2->SetFile(MainAppMapView);
|
||||
|
||||
if (!pSolution0->FindPatchOffset())
|
||||
pSolution0.Release();
|
||||
if (!pSolution1->FindPatchOffset())
|
||||
pSolution1.Release();
|
||||
if (!pSolution2->FindPatchOffset())
|
||||
pSolution2.Release();
|
||||
|
||||
if (!pSolution0.IsValid() && !pSolution1.IsValid() && !pSolution2.IsValid()) {
|
||||
puts("MESSAGE: Patch abort. None of PatchSolutions applied.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
PRINT_MESSAGE("MESSAGE: New RSA private key has been saved to RegPrivateKey.pem.");
|
||||
}
|
||||
LoadKey(pCipher, argc == 3 ? argv[2] : nullptr, pSolution0, pSolution1, pSolution2);
|
||||
|
||||
std::string PublicKeyString = cipher->ExportKeyString<RSACipher::KeyType::PublicKey, RSACipher::KeyFormat::PEM>();
|
||||
if (PublicKeyString.empty()) {
|
||||
REPORT_ERROR("ERROR: cipher->ExportKeyString failed.");
|
||||
return false;
|
||||
}
|
||||
if (pSolution0.IsValid())
|
||||
pSolution0->MakePatch(pCipher);
|
||||
if (pSolution1.IsValid())
|
||||
pSolution1->MakePatch(pCipher);
|
||||
if (pSolution2.IsValid())
|
||||
pSolution2->MakePatch(pCipher);
|
||||
|
||||
PRINT_MESSAGE("");
|
||||
PRINT_MESSAGE("Your RSA public key:");
|
||||
PRINT_MESSAGE(PublicKeyString.c_str());
|
||||
return true;
|
||||
if (pSolution0.IsValid())
|
||||
puts("MESSAGE: PatchSolution0 has been applied.");
|
||||
if (pSolution1.IsValid())
|
||||
puts("MESSAGE: PatchSolution1 has been applied.");
|
||||
if (pSolution2.IsValid())
|
||||
puts("MESSAGE: PatchSolution2 has been applied.");
|
||||
|
||||
puts("MESSAGE: Patch has been done successfully. Have fun and enjoy~");
|
||||
|
||||
return 0;
|
||||
} catch (Exception& e) {
|
||||
ExceptionReport(e);
|
||||
return static_cast<int>(e.ErrorCode());
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[], char* envp[]) {
|
||||
int status = 0;
|
||||
FileMapper MainApp;
|
||||
off_t MainAppSize;
|
||||
Helper::ResourceGuard<RSACipher> cipher(nullptr);
|
||||
Helper::ResourceGuard<Patcher::Solution> pSolution0(nullptr);
|
||||
Helper::ResourceGuard<Patcher::Solution> pSolution1(nullptr);
|
||||
|
||||
if (argc != 2 && argc != 3) {
|
||||
help();
|
||||
return status;
|
||||
}
|
||||
|
||||
PRINT_MESSAGE("NOTICE:");
|
||||
printf("This patcher will modify the file: %s\n", argv[1]);
|
||||
PRINT_MESSAGE("Please make a backup by your own if you care. Otherwise just ignore this notice.");
|
||||
PRINT_MESSAGE("Press Enter to continue OR Ctrl+C to abort...");
|
||||
getchar();
|
||||
|
||||
cipher.ptr = RSACipher::Create();
|
||||
if (cipher.ptr == nullptr) {
|
||||
REPORT_ERROR("ERROR: RSACipher::Create failed.");
|
||||
return status;
|
||||
}
|
||||
pSolution0.ptr = new Patcher::Solution0();
|
||||
pSolution1.ptr = new Patcher::Solution1();
|
||||
|
||||
//
|
||||
// Map file
|
||||
//
|
||||
if (!MainApp.OpenFile(argv[1])) {
|
||||
status = errno;
|
||||
REPORT_ERROR_WITH_CODE("Failed to open file.");
|
||||
return status;
|
||||
} else {
|
||||
PRINT_MESSAGE("MESSAGE: Open file successfully.");
|
||||
}
|
||||
|
||||
if (!MainApp.GetFileSize(MainAppSize)) {
|
||||
status = errno;
|
||||
REPORT_ERROR_WITH_CODE("Failed to get file size.");
|
||||
return status;
|
||||
} else {
|
||||
printf("MESSAGE: Get file size successfully: %lld\n", MainAppSize);
|
||||
}
|
||||
|
||||
if (!MainApp.Map(static_cast<size_t>(MainAppSize))) {
|
||||
status = errno;
|
||||
REPORT_ERROR_WITH_CODE("Failed to map file.");
|
||||
return status;
|
||||
} else {
|
||||
PRINT_MESSAGE("MESSAGE: Map file successfully.");
|
||||
}
|
||||
|
||||
pSolution0.ptr->SetFile(&MainApp);
|
||||
pSolution1.ptr->SetFile(&MainApp);
|
||||
|
||||
//
|
||||
// Find patch offsets
|
||||
//
|
||||
if (!pSolution0.ptr->FindPatchOffset()) {
|
||||
PRINT_MESSAGE("MESSAGE: Solution0: Cannot find public key. Solution0 will be omitted.");
|
||||
pSolution0.ptr->SetFile(nullptr);
|
||||
SAFE_DELETE(pSolution0.ptr);
|
||||
}
|
||||
|
||||
if (!pSolution1.ptr->FindPatchOffset()) {
|
||||
PRINT_MESSAGE("MESSAGE: Solution1: Cannot find public key. Solution1 will be omitted.");
|
||||
pSolution1.ptr->SetFile(nullptr);
|
||||
SAFE_DELETE(pSolution1.ptr);
|
||||
}
|
||||
|
||||
//
|
||||
// Load or generate RSA-2048 key
|
||||
//
|
||||
if (!LoadKey(cipher.ptr, argc == 3 ? argv[2] : nullptr, pSolution0.ptr, pSolution1.ptr))
|
||||
return status;
|
||||
|
||||
//
|
||||
// Making patch
|
||||
//
|
||||
if (pSolution0.ptr && !pSolution0.ptr->MakePatch(cipher.ptr))
|
||||
return status;
|
||||
if (pSolution1.ptr && !pSolution1.ptr->MakePatch(cipher.ptr))
|
||||
return status;
|
||||
|
||||
//
|
||||
// Report result
|
||||
//
|
||||
if (pSolution0.ptr)
|
||||
PRINT_MESSAGE("Solution0 has been done successfully.");
|
||||
if (pSolution1.ptr)
|
||||
PRINT_MESSAGE("Solution1 has been done successfully.");
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user