main.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <unistd.h>
  4. #include <fcntl.h>
  5. #include <sys/stat.h>
  6. #include <sys/mman.h>
  7. #include <filesystem>
  8. #include <optional>
  9. #include <fmt/format.h>
  10. #include "resource_wrapper.hpp"
  11. #include "resource_traits/cxx_object_traits.hpp"
  12. #include "resource_traits/unix_os/file_descriptor.hpp"
  13. #include "resource_traits/unix_os/map_view.hpp"
  14. #include "rsa_cipher.hpp"
  15. #include "elf64_interpreter.hpp"
  16. #include "patch_solution.hpp"
  17. #include "patch_solution_since_16.0.7.0.hpp"
  18. #include "exception.hpp"
  19. #include "exceptions/unix_exception.hpp"
  20. #include "exceptions/operation_canceled_exception.hpp"
  21. #define NKG_CURRENT_SOURCE_FILE() ".\\navicat-patcher\\main.cpp"
  22. #define NKG_CURRENT_SOURCE_LINE() __LINE__
  23. void welcome() {
  24. puts("***************************************************");
  25. puts("* navicat-patcher by @DoubleLabyrinth *");
  26. puts("* version: 16.0.7.0 *");
  27. puts("***************************************************");
  28. puts("");
  29. }
  30. void help() {
  31. puts("Usage:");
  32. puts(" navicat-patcher [--dry-run] <Navicat root directory> [RSA-2048 private key file]");
  33. puts("");
  34. puts(" [--dry-run] Run patcher without applying any patches.");
  35. puts(" This parameter is optional.");
  36. puts("");
  37. puts(" <Navicat root directory> Path to a directory where Navicat locates.");
  38. puts(" This parameter is mandatory.");
  39. puts("");
  40. puts(" [RSA-2048 private key file] Path to an RSA-2048 private key file.");
  41. puts(" If not specified, an RSA-2048 private key file");
  42. puts(" named \"RegPrivateKey.pem\" will be generated.");
  43. puts(" This parameter is optional.");
  44. puts("");
  45. puts("Example:");
  46. puts(" ./navicat-patcher ~/navicat16-premium-en-patched");
  47. puts("");
  48. }
  49. bool parse_cmdline(int argc, char* argv[], bool& dry_run, std::filesystem::path& navicat_root, std::filesystem::path& rsa_keyfile) {
  50. if (argc == 2) {
  51. dry_run = false;
  52. navicat_root = argv[1];
  53. rsa_keyfile.clear();
  54. return true;
  55. } else if (argc == 3) {
  56. if (strcmp(argv[1], "--dry-run") == 0) {
  57. dry_run = true;
  58. navicat_root = argv[2];
  59. rsa_keyfile.clear();
  60. return true;
  61. } else {
  62. dry_run = false;
  63. navicat_root = argv[1];
  64. rsa_keyfile = argv[2];
  65. return true;
  66. }
  67. } else if (argc == 4) {
  68. if (strcmp(argv[1], "--dry-run") == 0) {
  69. dry_run = true;
  70. navicat_root = argv[2];
  71. rsa_keyfile = argv[3];
  72. return true;
  73. } else {
  74. return false;
  75. }
  76. } else {
  77. return false;
  78. }
  79. }
  80. void select_patch_solutions(nkg::resource_wrapper<nkg::resource_traits::cxx_object_traits<nkg::patch_solution>>& solution0) {
  81. return;
  82. }
  83. void load_rsa_privkey(nkg::rsa_cipher& cipher, std::filesystem::path& rsa_key_file, nkg::patch_solution* solution0) {
  84. if (!rsa_key_file.empty()) {
  85. printf("[*] Import RSA-2048 private key from\n");
  86. printf(" %s\n", rsa_key_file.native().c_str());
  87. cipher.import_private_key_file(rsa_key_file.native());
  88. if (solution0 && !solution0->check_rsa_privkey(cipher)) {
  89. throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "The RSA private key you provide cannot be used.");
  90. }
  91. } else {
  92. printf("[*] Generating new RSA private key, it may take a long time...\n");
  93. do {
  94. cipher.generate_key(2048);
  95. } while (solution0 && !solution0->check_rsa_privkey(cipher)); // re-generate RSA key if one of `check_rsa_privkey` returns false
  96. }
  97. printf("[*] Your RSA private key:\n%s\n", cipher.export_private_key_string().c_str());
  98. }
  99. template<typename... args_t>
  100. bool all_patch_solutions_are_suppressed(args_t&&... args) {
  101. return (!args.is_valid() && ...);
  102. }
  103. void detect_backup(const std::filesystem::path& file_path) {
  104. std::filesystem::path backup_path = file_path.native() + ".bak";
  105. if (std::filesystem::is_regular_file(backup_path)) {
  106. while (true) {
  107. printf("[*] Previous backup %s is detected. Delete? (y/n)", backup_path.native().c_str());
  108. auto select = getchar();
  109. while (select != '\n' && getchar() != '\n') {}
  110. if (select == 'Y' || select == 'y') {
  111. std::filesystem::remove(backup_path);
  112. break;
  113. } else if (select == 'N' || select == 'n') {
  114. throw nkg::exceptions::operation_canceled_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Backup file still exists. Patch abort!");
  115. } else {
  116. continue;
  117. }
  118. }
  119. }
  120. }
  121. void make_backup(const std::filesystem::path& file_path) {
  122. std::filesystem::path backup_path = file_path.native() + ".bak";
  123. if (std::filesystem::exists(backup_path)) {
  124. throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Previous backup is detected.")
  125. .push_hint(fmt::format("Please delete {} and try again.", backup_path.native()));
  126. } else {
  127. std::filesystem::copy_file(file_path, backup_path);
  128. }
  129. }
  130. int main(int argc, char* argv[]) {
  131. welcome();
  132. bool dry_run = false;
  133. std::filesystem::path navicat_root;
  134. std::filesystem::path rsa_key_file;
  135. if (!parse_cmdline(argc, argv, dry_run, navicat_root, rsa_key_file)) {
  136. help();
  137. return -1;
  138. }
  139. try {
  140. if (!std::filesystem::is_directory(navicat_root)) {
  141. throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "Navicat root directory path doesn't point to a directory.")
  142. .push_hint("Are you sure the path you specified is correct?")
  143. .push_hint(fmt::format("The path you specified: {}", navicat_root.native()));
  144. }
  145. if (!rsa_key_file.empty() && !std::filesystem::is_regular_file(rsa_key_file)) {
  146. throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "RSA private key file path doesn't point to a file.")
  147. .push_hint("Are you sure the path you specified is correct?")
  148. .push_hint(fmt::format("The path you specified: {}", rsa_key_file.native()));
  149. }
  150. nkg::rsa_cipher cipher;
  151. std::filesystem::path libcc_filepath = navicat_root / "usr" / "lib" / "libcc.so";
  152. nkg::resource_wrapper libcc_fd{ nkg::resource_traits::unix_os::file_descriptor{} };
  153. nkg::resource_wrapper libcc_stat{ nkg::resource_traits::cxx_object_traits<struct stat>{} };
  154. nkg::resource_wrapper libcc_map_view{ nkg::resource_traits::unix_os::map_view{}, [&libcc_stat](void* p) { munmap(p, libcc_stat->st_size); } };
  155. std::optional<nkg::elf64_interpreter> libcc_interpreter;
  156. nkg::resource_wrapper solution0{ nkg::resource_traits::cxx_object_traits<nkg::patch_solution>{} };
  157. // open libcc.dll
  158. libcc_fd.set(open(libcc_filepath.native().c_str(), O_RDWR));
  159. if (libcc_fd.is_valid()) {
  160. printf("[+] Try to open libcc.dll ... OK!\n");
  161. } else {
  162. if (errno == ENOENT) {
  163. printf("[-] Try to open libcc.dll ... NOT FOUND!\n");
  164. } else {
  165. throw nkg::exceptions::unix_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), errno, "open failed.");
  166. }
  167. }
  168. if (libcc_fd.is_valid()) {
  169. libcc_stat.set(new struct stat());
  170. if (fstat(libcc_fd.get(), libcc_stat.get()) != 0) {
  171. throw nkg::exceptions::unix_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), errno, "fstat failed.");
  172. }
  173. libcc_map_view.set(mmap(nullptr, libcc_stat->st_size, PROT_READ | PROT_WRITE, MAP_SHARED, libcc_fd.get(), 0));
  174. if (!libcc_map_view.is_valid()) {
  175. throw nkg::exceptions::unix_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), errno, "mmap failed.");
  176. }
  177. libcc_interpreter = nkg::elf64_interpreter::parse(libcc_map_view.get(), libcc_stat->st_size);
  178. solution0.set(new nkg::patch_solution_since<16, 0, 7, 0>(libcc_interpreter.value()));
  179. }
  180. puts("");
  181. // find patch and decide which solution will be applied
  182. if (solution0.is_valid()) {
  183. auto patch_found = solution0->find_patch();
  184. puts("");
  185. if (!patch_found) {
  186. solution0.release();
  187. }
  188. }
  189. select_patch_solutions(solution0);
  190. if (all_patch_solutions_are_suppressed(solution0)) {
  191. throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), "All patch solutions are suppressed. Patch abort!")
  192. .push_hint("Are you sure your navicat has not been patched/modified before?");
  193. }
  194. // load key
  195. load_rsa_privkey(cipher, rsa_key_file, solution0.get());
  196. // apply patch solutions
  197. if (dry_run) {
  198. puts("*******************************************************");
  199. puts("* DRY-RUN MODE ENABLE! *");
  200. puts("* NO PATCH WILL BE APPLIED! *");
  201. puts("*******************************************************");
  202. } else {
  203. // save private key if not given
  204. if (rsa_key_file.empty()) {
  205. cipher.export_private_key_file("RegPrivateKey.pem");
  206. }
  207. // detecting backups
  208. if (solution0.is_valid()) {
  209. detect_backup(libcc_filepath);
  210. }
  211. // make backup
  212. if (solution0.is_valid()) {
  213. make_backup(libcc_filepath);
  214. }
  215. // make patch
  216. // no way to go back from here :-)
  217. if (solution0.is_valid()) {
  218. solution0->make_patch(cipher);
  219. }
  220. // print new key file path
  221. if (rsa_key_file.empty()) {
  222. printf("[*] New RSA-2048 private key has been saved to\n");
  223. printf(" %s\n", (std::filesystem::current_path() / "RegPrivateKey.pem").c_str());
  224. }
  225. puts("");
  226. puts("*******************************************************");
  227. puts("* PATCH HAS BEEN DONE SUCCESSFULLY! *");
  228. puts("* HAVE FUN AND ENJOY~ *");
  229. puts("*******************************************************");
  230. }
  231. return 0;
  232. } catch (nkg::exception& e) {
  233. printf("[-] %s:%d ->\n", e.source_file().c_str(), e.source_line());
  234. printf(" %s\n", e.custom_message().c_str());
  235. if (e.error_code_exists()) {
  236. printf(" %s (0x%zx)\n", e.error_string().c_str(), e.error_code());
  237. }
  238. for (auto& hint : e.hints()) {
  239. printf(" HINT: %s\n", hint.c_str());
  240. }
  241. return -1;
  242. }
  243. }
  244. #undef NKG_CURRENT_SOURCE_LINE
  245. #undef NKG_CURRENT_SOURCE_FILE