main.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. #include <errno.h>
  2. #include <unistd.h>
  3. #include <fcntl.h>
  4. #include <sys/stat.h>
  5. #include <sys/mman.h>
  6. #include <stdio.h>
  7. #include "Exception.hpp"
  8. #include "ExceptionSystem.hpp"
  9. #include "ResourceWrapper.hpp"
  10. #include "ResourceTraitsUnix.hpp"
  11. #include "Elf64Interpreter.hpp"
  12. #include "PatchSolutions.hpp"
  13. #include "Misc.hpp"
  14. static void Welcome(bool bWait) {
  15. puts("**********************************************************");
  16. puts("* Navicat Patcher (Linux) by @DoubleLabyrinth *");
  17. puts("* Version: 1.0 *");
  18. puts("**********************************************************");
  19. puts("");
  20. if (bWait) {
  21. puts("Press ENTER to continue or Ctrl + C to abort.");
  22. getchar();
  23. }
  24. }
  25. static void Help() {
  26. puts("Usage:");
  27. puts(" navicat-patcher [--dry-run] <Navicat installation path> [RSA-2048 Private Key File]");
  28. puts("");
  29. puts(" [--dry-run] Run patcher without applying any patches.");
  30. puts(" This parameter is optional.");
  31. puts("");
  32. puts(" <Navicat installation path> Path to directory where Navicat is installed.");
  33. puts(" This parameter must be specified.");
  34. puts("");
  35. puts(" [RSA-2048 Private Key File] Path to a PEM-format RSA-2048 private key file.");
  36. puts(" This parameter is optional.");
  37. puts("");
  38. }
  39. static bool ParseCommandLine(int argc, char* argv[], bool& bDryrun, std::string& szInstallPath, std::string& szKeyFilePath) {
  40. if (argc == 2) {
  41. bDryrun = false;
  42. szInstallPath = argv[1];
  43. szKeyFilePath.clear();
  44. return true;
  45. } else if (argc == 3) {
  46. if (strcasecmp(argv[1], "--dry-run") == 0) {
  47. bDryrun = true;
  48. szInstallPath = argv[2];
  49. szKeyFilePath.clear();
  50. return true;
  51. } else {
  52. bDryrun = false;
  53. szInstallPath = argv[1];
  54. szKeyFilePath = argv[2];
  55. return true;
  56. }
  57. } else if (argc == 4) {
  58. if (strcasecmp(argv[1], "--dry-run") == 0) {
  59. bDryrun = true;
  60. szInstallPath = argv[2];
  61. szKeyFilePath = argv[3];
  62. return true;
  63. } else {
  64. return false;
  65. }
  66. } else {
  67. return false;
  68. }
  69. }
  70. static void SelectPatchSolutions(ARL::ResourceWrapper<ARL::ResourceTraits::CppObject<nkg::PatchSolution>>& lpSolution0) {
  71. // pass
  72. }
  73. static void NavicatBackupDetect(std::string_view szFilePath) {
  74. std::string szBackupPath(szFilePath);
  75. szBackupPath += ".bak";
  76. if (nkg::Misc::FsIsExist(szBackupPath) == true) {
  77. while (true) {
  78. printf("[?] Previous backup %s is detected. Delete? (y/n)", szBackupPath.c_str());
  79. auto select = getchar();
  80. while (select != '\n' && getchar() != '\n') {}
  81. if (select == 'Y' || select == 'y') {
  82. nkg::Misc::FsDeleteFile(szBackupPath);
  83. break;
  84. } else if (select == 'N' || select == 'n') {
  85. throw ARL::Exception(__BASE_FILE__, __LINE__, "Backup file still existed. Patch abort!");
  86. } else {
  87. continue;
  88. }
  89. }
  90. printf("\n");
  91. }
  92. }
  93. static void NavicatBackupMake(std::string_view szFilePath) {
  94. std::string szBackupPath(szFilePath);
  95. szBackupPath += ".bak";
  96. nkg::Misc::FsCopyFile(szFilePath, szBackupPath);
  97. }
  98. static void LoadKey(nkg::RSACipher& Cipher,
  99. std::string_view szKeyFileName,
  100. nkg::PatchSolution* lpSolution0) {
  101. if (szKeyFileName.empty() == false) {
  102. printf("[*] Import RSA-2048 key from %s\n", szKeyFileName.data());
  103. Cipher.ImportKeyFromFile<nkg::RSAKeyType::PrivateKey, nkg::RSAKeyFormat::PEM>(szKeyFileName);
  104. if (lpSolution0 && lpSolution0->CheckKey(Cipher) == false) {
  105. throw ARL::Exception(__BASE_FILE__, __LINE__, "The RSA private key you provide cannot be used.");
  106. }
  107. } else {
  108. printf("[*] Generating new RSA private key, it may take a long time...\n");
  109. do {
  110. Cipher.GenerateKey(2048);
  111. } while (lpSolution0 && lpSolution0->CheckKey(Cipher) == false); // re-generate RSA key if CheckKey return false
  112. }
  113. printf("[*] Your RSA private key:\n");
  114. printf(" %s\n",
  115. [&Cipher]() -> std::string {
  116. auto szPrivateKey = Cipher.ExportKeyString<nkg::RSAKeyType::PrivateKey, nkg::RSAKeyFormat::PEM>();
  117. for (size_t pos = 0; (pos = szPrivateKey.find('\n', pos)) != std::string::npos; pos += strlen("\n ")) {
  118. szPrivateKey.replace(pos, 1, "\n ");
  119. }
  120. return szPrivateKey;
  121. }().c_str()
  122. );
  123. printf("[*] Your RSA public key:\n");
  124. printf(" %s\n",
  125. [&Cipher]() -> std::string {
  126. auto szPublicKey = Cipher.ExportKeyString<nkg::RSAKeyType::PublicKey, nkg::RSAKeyFormat::PEM>();
  127. for (size_t pos = 0; (pos = szPublicKey.find('\n', pos)) != std::string::npos; pos += strlen("\n ")) {
  128. szPublicKey.replace(pos, 1, "\n ");
  129. }
  130. return szPublicKey;
  131. }().c_str()
  132. );
  133. //printf("%s\n", Cipher.ExportKeyString<nkg::RSAKeyType::PublicKey, nkg::RSAKeyFormat::PEM>().c_str());
  134. printf("\n");
  135. }
  136. int main(int argc, char* argv[]) {
  137. bool bDryrun;
  138. std::string szInstallPath;
  139. std::string szKeyFilePath;
  140. if (ParseCommandLine(argc, argv, bDryrun, szInstallPath, szKeyFilePath) == false) {
  141. Welcome(false);
  142. Help();
  143. return -1;
  144. } else {
  145. Welcome(true);
  146. try {
  147. if (nkg::Misc::FsIsDirectory(szInstallPath) == false) {
  148. throw ARL::Exception(__BASE_FILE__, __LINE__, "Navicat installation path doesn't point to a directory.")
  149. .PushHint("Are you sure the path you specified is correct?")
  150. .PushFormatHint("The path you specified: %s", szInstallPath.c_str());
  151. }
  152. if (szKeyFilePath.empty() == false && nkg::Misc::FsIsFile(szKeyFilePath) == false) {
  153. throw ARL::Exception(__BASE_FILE__, __LINE__, "RSA key file path doesn't point to a file.")
  154. .PushHint("Are you sure the path you specified is correct?")
  155. .PushFormatHint("The path you specified: %s", szKeyFilePath.c_str());
  156. }
  157. while (szInstallPath.back() == '/') {
  158. szInstallPath.pop_back();
  159. }
  160. nkg::RSACipher Cipher;
  161. ARL::ResourceWrapper<ARL::ResourceTraits::CppObject<nkg::PatchSolution>> lpSolution0;
  162. std::string libcc_path;
  163. ARL::ResourceWrapper libcc_fd{ ARL::ResourceTraits::FileDescriptor{} };
  164. ARL::ResourceWrapper libcc_stat{ ARL::ResourceTraits::CppObject<struct stat>{} };
  165. ARL::ResourceWrapperEx libcc_mmap{ ARL::ResourceTraits::MapView{}, [&libcc_stat](void* p) {
  166. if (munmap(p, libcc_stat->st_size) < 0) {
  167. throw ARL::SystemError(__BASE_FILE__, __LINE__, errno, "munmap failed.");
  168. }
  169. } };
  170. ARL::ResourceWrapper libcc_interpreter{ ARL::ResourceTraits::CppObject<nkg::Elf64Interpreter>{} };
  171. //
  172. // try open libcc.so
  173. //
  174. libcc_path = szInstallPath + "/usr/lib/libcc.so";
  175. libcc_fd.TakeOver(open(libcc_path.c_str(), O_RDWR));
  176. if (libcc_fd.IsValid()) {
  177. printf("[+] Try to open libcc.so ... Ok!\n");
  178. } else {
  179. if (errno == ENOENT) {
  180. printf("[-] Try to open libcc.so ... Not found!\n");
  181. } else {
  182. throw ARL::SystemError(__BASE_FILE__, __LINE__, errno, "open failed.");
  183. }
  184. }
  185. puts("");
  186. //
  187. // try map libcc.so
  188. //
  189. if (libcc_fd.IsValid()) {
  190. libcc_stat.TakeOver(new struct stat());
  191. if (fstat(libcc_fd, libcc_stat) != 0) {
  192. throw ARL::SystemError(__BASE_FILE__, __LINE__, errno, "fstat failed.");
  193. }
  194. libcc_mmap.TakeOver(mmap(nullptr, libcc_stat->st_size, PROT_READ | PROT_WRITE, MAP_SHARED, libcc_fd, 0));
  195. if (libcc_mmap.IsValid() == false) {
  196. throw ARL::SystemError(__BASE_FILE__, __LINE__, errno, "mmap failed.");
  197. }
  198. libcc_interpreter.TakeOver(
  199. new nkg::Elf64Interpreter(nkg::Elf64Interpreter::Parse(libcc_mmap, libcc_stat->st_size))
  200. );
  201. lpSolution0.TakeOver(
  202. new nkg::PatchSolution0(*libcc_interpreter.Get())
  203. );
  204. }
  205. //
  206. // Make sure that there is one patch solution at least existing.
  207. //
  208. if (lpSolution0.IsValid() == false) {
  209. throw ARL::Exception(__BASE_FILE__, __LINE__, "No patch applied. Patch abort!")
  210. .PushHint("Are you sure your Navicat has not been patched/modified before?");
  211. }
  212. //
  213. // Finding patch offsets
  214. //
  215. if (lpSolution0.IsValid() && lpSolution0->FindPatchOffset() == false) {
  216. lpSolution0.Release();
  217. }
  218. printf("\n");
  219. //
  220. // decide which solutions will be applied
  221. //
  222. SelectPatchSolutions(lpSolution0);
  223. //
  224. // Make sure that there is one patch solution at least existing.
  225. //
  226. if (lpSolution0.IsValid() == false) {
  227. throw ARL::Exception(__BASE_FILE__, __LINE__, "No patch applied. Patch abort!")
  228. .PushHint("Are you sure your Navicat has not been patched/modified before?");
  229. }
  230. //
  231. // detecting backups
  232. //
  233. if (lpSolution0.IsValid()) {
  234. NavicatBackupDetect(libcc_path);
  235. }
  236. //
  237. //
  238. //
  239. LoadKey(Cipher, szKeyFilePath, lpSolution0);
  240. if (bDryrun == false) {
  241. //
  242. // Save private key if not given
  243. //
  244. if (szKeyFilePath.empty()) {
  245. Cipher.ExportKeyToFile<nkg::RSAKeyType::PrivateKey, nkg::RSAKeyFormat::PEM>("RegPrivateKey.pem");
  246. }
  247. //
  248. // Making backups
  249. //
  250. if (lpSolution0.IsValid()) {
  251. NavicatBackupMake(libcc_path);
  252. }
  253. //
  254. // Making patch. No way to go back here :-)
  255. //
  256. if (lpSolution0.IsValid()) {
  257. lpSolution0->MakePatch(Cipher);
  258. }
  259. if (szKeyFilePath.empty()) {
  260. printf("[*] New RSA-2048 private key has been saved to\n");
  261. printf(" %s/RegPrivateKey.pem\n", nkg::Misc::FsCurrentWorkingDirectory().c_str());
  262. printf("\n");
  263. }
  264. puts("*******************************************************");
  265. puts("* PATCH HAS BEEN DONE SUCCESSFULLY! *");
  266. puts("* HAVE FUN AND ENJOY~ *");
  267. puts("*******************************************************");
  268. } else {
  269. puts("*******************************************************");
  270. puts("* DRY-RUN MODE ENABLE! *");
  271. puts("* NO PATCH WILL BE APPLIED! *");
  272. puts("*******************************************************");
  273. }
  274. return 0;
  275. } catch (ARL::Exception& e) {
  276. printf("[-] %s:%zu ->\n", e.ExceptionFile(), e.ExceptionLine());
  277. printf(" %s\n", e.ExceptionMessage());
  278. if (e.HasErrorCode()) {
  279. printf(" %s (0x%zx)\n", e.ErrorString(), e.ErrorCode());
  280. }
  281. for (const auto& Hint : e.Hints()) {
  282. printf(" Hints: %s\n", Hint.c_str());
  283. }
  284. return -1;
  285. }
  286. }
  287. }