Refactor code + Add support for 12.1.15

This commit is contained in:
Double Sine 2019-02-08 02:23:00 +08:00
parent e4e433ec94
commit dd6d0713e6
No known key found for this signature in database
GPG Key ID: 44460E4F43EA8633
21 changed files with 1884 additions and 828 deletions

View 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);
}

View 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;
};

View 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();
}
};

View File

@ -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();
}
};

View File

@ -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));
}
}

View File

@ -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)

View 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);
}

View 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;
};

View 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("");
}

View 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("");
}

View 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("");
}

View 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;
};

View 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);
}

View File

@ -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;
}

View 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;
}
};

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
};
}

View 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);
};
};

View 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;
}
};

View File

@ -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;
}