navicat-keygen/navicat-patcher/main.cpp
2019-12-05 19:44:07 +08:00

336 lines
12 KiB
C++

#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <stdio.h>
#include "Exception.hpp"
#include "ExceptionSystem.hpp"
#include "ResourceWrapper.hpp"
#include "ResourceTraitsUnix.hpp"
#include "Elf64Interpreter.hpp"
#include "PatchSolutions.hpp"
#include "Misc.hpp"
static void Welcome(bool bWait) {
puts("**********************************************************");
puts("* Navicat Patcher (Linux) by @DoubleLabyrinth *");
puts("* Version: 1.0 *");
puts("**********************************************************");
puts("");
if (bWait) {
puts("Press ENTER to continue or Ctrl + C to abort.");
getchar();
}
}
static void Help() {
puts("Usage:");
puts(" navicat-patcher [--dry-run] <Navicat installation path> [RSA-2048 Private Key File]");
puts("");
puts(" [--dry-run] Run patcher without applying any patches.");
puts(" This parameter is optional.");
puts("");
puts(" <Navicat installation path> Path to directory where Navicat is installed.");
puts(" This parameter must be specified.");
puts("");
puts(" [RSA-2048 Private Key File] Path to a PEM-format RSA-2048 private key file.");
puts(" This parameter is optional.");
puts("");
}
static bool ParseCommandLine(int argc, char* argv[], bool& bDryrun, std::string& szInstallPath, std::string& szKeyFilePath) {
if (argc == 2) {
bDryrun = false;
szInstallPath = argv[1];
szKeyFilePath.clear();
return true;
} else if (argc == 3) {
if (strcasecmp(argv[1], "--dry-run") == 0) {
bDryrun = true;
szInstallPath = argv[2];
szKeyFilePath.clear();
return true;
} else {
bDryrun = false;
szInstallPath = argv[1];
szKeyFilePath = argv[2];
return true;
}
} else if (argc == 4) {
if (strcasecmp(argv[1], "--dry-run") == 0) {
bDryrun = true;
szInstallPath = argv[2];
szKeyFilePath = argv[3];
return true;
} else {
return false;
}
} else {
return false;
}
}
static void SelectPatchSolutions(ARL::ResourceWrapper<ARL::ResourceTraits::CppObject<nkg::PatchSolution>>& lpSolution0) {
// pass
}
static void NavicatBackupDetect(std::string_view szFilePath) {
std::string szBackupPath(szFilePath);
szBackupPath += ".bak";
if (nkg::Misc::FsIsExist(szBackupPath) == true) {
while (true) {
printf("[?] Previous backup %s is detected. Delete? (y/n)", szBackupPath.c_str());
auto select = getchar();
while (select != '\n' && getchar() != '\n') {}
if (select == 'Y' || select == 'y') {
nkg::Misc::FsDeleteFile(szBackupPath);
break;
} else if (select == 'N' || select == 'n') {
throw ARL::Exception(__BASE_FILE__, __LINE__, "Backup file still existed. Patch abort!");
} else {
continue;
}
}
printf("\n");
}
}
static void NavicatBackupMake(std::string_view szFilePath) {
std::string szBackupPath(szFilePath);
szBackupPath += ".bak";
nkg::Misc::FsCopyFile(szFilePath, szBackupPath);
}
static void LoadKey(nkg::RSACipher& Cipher,
std::string_view szKeyFileName,
nkg::PatchSolution* lpSolution0) {
if (szKeyFileName.empty() == false) {
printf("[*] Import RSA-2048 key from %s\n", szKeyFileName.data());
Cipher.ImportKeyFromFile<nkg::RSAKeyType::PrivateKey, nkg::RSAKeyFormat::PEM>(szKeyFileName);
if (lpSolution0 && lpSolution0->CheckKey(Cipher) == false) {
throw ARL::Exception(__BASE_FILE__, __LINE__, "The RSA private key you provide cannot be used.");
}
} else {
printf("[*] Generating new RSA private key, it may take a long time...\n");
do {
Cipher.GenerateKey(2048);
} while (lpSolution0 && lpSolution0->CheckKey(Cipher) == false); // re-generate RSA key if CheckKey return false
}
printf("[*] Your RSA private key:\n");
printf(" %s\n",
[&Cipher]() -> std::string {
auto szPrivateKey = Cipher.ExportKeyString<nkg::RSAKeyType::PrivateKey, nkg::RSAKeyFormat::PEM>();
for (size_t pos = 0; (pos = szPrivateKey.find('\n', pos)) != std::string::npos; pos += strlen("\n ")) {
szPrivateKey.replace(pos, 1, "\n ");
}
return szPrivateKey;
}().c_str()
);
printf("[*] Your RSA public key:\n");
printf(" %s\n",
[&Cipher]() -> std::string {
auto szPublicKey = Cipher.ExportKeyString<nkg::RSAKeyType::PublicKey, nkg::RSAKeyFormat::PEM>();
for (size_t pos = 0; (pos = szPublicKey.find('\n', pos)) != std::string::npos; pos += strlen("\n ")) {
szPublicKey.replace(pos, 1, "\n ");
}
return szPublicKey;
}().c_str()
);
//printf("%s\n", Cipher.ExportKeyString<nkg::RSAKeyType::PublicKey, nkg::RSAKeyFormat::PEM>().c_str());
printf("\n");
}
int main(int argc, char* argv[]) {
bool bDryrun;
std::string szInstallPath;
std::string szKeyFilePath;
if (ParseCommandLine(argc, argv, bDryrun, szInstallPath, szKeyFilePath) == false) {
Welcome(false);
Help();
return -1;
} else {
Welcome(true);
try {
if (nkg::Misc::FsIsDirectory(szInstallPath) == false) {
throw ARL::Exception(__BASE_FILE__, __LINE__, "Navicat installation path doesn't point to a directory.")
.PushHint("Are you sure the path you specified is correct?")
.PushFormatHint("The path you specified: %s", szInstallPath.c_str());
}
if (szKeyFilePath.empty() == false && nkg::Misc::FsIsFile(szKeyFilePath) == false) {
throw ARL::Exception(__BASE_FILE__, __LINE__, "RSA key file path doesn't point to a file.")
.PushHint("Are you sure the path you specified is correct?")
.PushFormatHint("The path you specified: %s", szKeyFilePath.c_str());
}
while (szInstallPath.back() == '/') {
szInstallPath.pop_back();
}
nkg::RSACipher Cipher;
ARL::ResourceWrapper<ARL::ResourceTraits::CppObject<nkg::PatchSolution>> lpSolution0;
std::string libcc_path;
ARL::ResourceWrapper libcc_fd{ ARL::ResourceTraits::FileDescriptor{} };
ARL::ResourceWrapper libcc_stat{ ARL::ResourceTraits::CppObject<struct stat>{} };
ARL::ResourceWrapperEx libcc_mmap{ ARL::ResourceTraits::MapView{}, [&libcc_stat](void* p) {
if (munmap(p, libcc_stat->st_size) < 0) {
throw ARL::SystemError(__BASE_FILE__, __LINE__, errno, "munmap failed.");
}
} };
ARL::ResourceWrapper libcc_interpreter{ ARL::ResourceTraits::CppObject<nkg::Elf64Interpreter>{} };
//
// try open libcc.so
//
libcc_path = szInstallPath + "/usr/lib/libcc.so";
libcc_fd.TakeOver(open(libcc_path.c_str(), O_RDWR));
if (libcc_fd.IsValid()) {
printf("[+] Try to open libcc.so ... Ok!\n");
} else {
if (errno == ENOENT) {
printf("[-] Try to open libcc.so ... Not found!\n");
} else {
throw ARL::SystemError(__BASE_FILE__, __LINE__, errno, "open failed.");
}
}
puts("");
//
// try map libcc.so
//
if (libcc_fd.IsValid()) {
libcc_stat.TakeOver(new struct stat());
if (fstat(libcc_fd, libcc_stat) != 0) {
throw ARL::SystemError(__BASE_FILE__, __LINE__, errno, "fstat failed.");
}
libcc_mmap.TakeOver(mmap(nullptr, libcc_stat->st_size, PROT_READ | PROT_WRITE, MAP_SHARED, libcc_fd, 0));
if (libcc_mmap.IsValid() == false) {
throw ARL::SystemError(__BASE_FILE__, __LINE__, errno, "mmap failed.");
}
libcc_interpreter.TakeOver(
new nkg::Elf64Interpreter(nkg::Elf64Interpreter::Parse(libcc_mmap, libcc_stat->st_size))
);
lpSolution0.TakeOver(
new nkg::PatchSolution0(*libcc_interpreter.Get())
);
}
//
// Make sure that there is one patch solution at least existing.
//
if (lpSolution0.IsValid() == false) {
throw ARL::Exception(__BASE_FILE__, __LINE__, "No patch applied. Patch abort!")
.PushHint("Are you sure your Navicat has not been patched/modified before?");
}
//
// Finding patch offsets
//
if (lpSolution0.IsValid() && lpSolution0->FindPatchOffset() == false) {
lpSolution0.Release();
}
printf("\n");
//
// decide which solutions will be applied
//
SelectPatchSolutions(lpSolution0);
//
// Make sure that there is one patch solution at least existing.
//
if (lpSolution0.IsValid() == false) {
throw ARL::Exception(__BASE_FILE__, __LINE__, "No patch applied. Patch abort!")
.PushHint("Are you sure your Navicat has not been patched/modified before?");
}
//
// detecting backups
//
if (lpSolution0.IsValid()) {
NavicatBackupDetect(libcc_path);
}
//
//
//
LoadKey(Cipher, szKeyFilePath, lpSolution0);
if (bDryrun == false) {
//
// Save private key if not given
//
if (szKeyFilePath.empty()) {
Cipher.ExportKeyToFile<nkg::RSAKeyType::PrivateKey, nkg::RSAKeyFormat::PEM>("RegPrivateKey.pem");
}
//
// Making backups
//
if (lpSolution0.IsValid()) {
NavicatBackupMake(libcc_path);
}
//
// Making patch. No way to go back here :-)
//
if (lpSolution0.IsValid()) {
lpSolution0->MakePatch(Cipher);
}
if (szKeyFilePath.empty()) {
printf("[*] New RSA-2048 private key has been saved to\n");
printf(" %s/RegPrivateKey.pem\n", nkg::Misc::FsCurrentWorkingDirectory().c_str());
printf("\n");
}
puts("*******************************************************");
puts("* PATCH HAS BEEN DONE SUCCESSFULLY! *");
puts("* HAVE FUN AND ENJOY~ *");
puts("*******************************************************");
} else {
puts("*******************************************************");
puts("* DRY-RUN MODE ENABLE! *");
puts("* NO PATCH WILL BE APPLIED! *");
puts("*******************************************************");
}
return 0;
} catch (ARL::Exception& e) {
printf("[-] %s:%zu ->\n", e.ExceptionFile(), e.ExceptionLine());
printf(" %s\n", e.ExceptionMessage());
if (e.HasErrorCode()) {
printf(" %s (0x%zx)\n", e.ErrorString(), e.ErrorCode());
}
for (const auto& Hint : e.Hints()) {
printf(" Hints: %s\n", Hint.c_str());
}
return -1;
}
}
}