wmain.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. #include <stdio.h>
  2. #include <windows.h>
  3. #include <fmt/format.h>
  4. #include <filesystem>
  5. #include <optional>
  6. #include "cp_converter.hpp"
  7. #include "resource_wrapper.hpp"
  8. #include "resource_traits/cxx_object_traits.hpp"
  9. #include "resource_traits/win32/file_handle.hpp"
  10. #include "resource_traits/win32/generic_handle.hpp"
  11. #include "resource_traits/win32/map_view_ptr.hpp"
  12. #include "rsa_cipher.hpp"
  13. #include "image_interpreter.hpp"
  14. #include "patch_solution.hpp"
  15. #include "patch_solution_since_16.0.7.0.hpp"
  16. #include "exception.hpp"
  17. #include "exceptions/operation_canceled_exception.hpp"
  18. #include "exceptions/win32_exception.hpp"
  19. #define NKG_CURRENT_SOURCE_FILE() u8".\\navicat-patcher\\wmain.cpp"
  20. #define NKG_CURRENT_SOURCE_LINE() __LINE__
  21. void welcome() {
  22. _putws(L"***************************************************");
  23. _putws(L"* navicat-patcher by @DoubleLabyrinth *");
  24. _putws(L"* version: 16.0.7.0-2 *");
  25. _putws(L"***************************************************");
  26. _putws(L"");
  27. }
  28. void help() {
  29. _putws(L"Usage:");
  30. _putws(L" navicat-patcher.exe [-dry-run] <Navicat Install Path> [RSA-2048 PEM File Path]");
  31. _putws(L"");
  32. _putws(L" [-dry-run] Run patcher without applying any patches.");
  33. _putws(L" This parameter is optional.");
  34. _putws(L"");
  35. _putws(L" <Navicat Install Path> Path to a directory where Navicat is installed.");
  36. _putws(L" This parameter is mandatory.");
  37. _putws(L"");
  38. _putws(L" [RSA-2048 PEM File Path] Path to an RSA-2048 private key file.");
  39. _putws(L" If not specified, an RSA-2048 private key file");
  40. _putws(L" named \"RegPrivateKey.pem\" will be generated.");
  41. _putws(L" This parameter is optional.");
  42. _putws(L"");
  43. _putws(L"Example:");
  44. _putws(L" navicat-patcher.exe \"C:\\Program Files\\PremiumSoft\\Navicat Premium 12\"");
  45. _putws(L"");
  46. }
  47. bool parse_cmdline(int argc, wchar_t* argv[], bool& dry_run, std::filesystem::path& navicat_install_path, std::filesystem::path& rsa_privkey_filepath) {
  48. if (argc == 2) {
  49. dry_run = false;
  50. navicat_install_path = argv[1];
  51. rsa_privkey_filepath.clear();
  52. return true;
  53. } else if (argc == 3) {
  54. if (_wcsicmp(argv[1], L"-dry-run") == 0) {
  55. dry_run = true;
  56. navicat_install_path = argv[2];
  57. rsa_privkey_filepath.clear();
  58. return true;
  59. } else {
  60. dry_run = false;
  61. navicat_install_path = argv[1];
  62. rsa_privkey_filepath = argv[2];
  63. return true;
  64. }
  65. } else if (argc == 4) {
  66. if (_wcsicmp(argv[1], L"-dry-run") == 0) {
  67. dry_run = true;
  68. navicat_install_path = argv[2];
  69. rsa_privkey_filepath = argv[3];
  70. return true;
  71. } else {
  72. return false;
  73. }
  74. } else {
  75. return false;
  76. }
  77. }
  78. void select_patch_solutions
  79. (nkg::resource_wrapper<nkg::resource_traits::cxx_object_traits<nkg::patch_solution>>& solution0)
  80. {
  81. return;
  82. }
  83. void load_rsa_privkey(nkg::rsa_cipher& cipher, std::filesystem::path& rsa_privkey_filepath, nkg::patch_solution* solution0) {
  84. if (!rsa_privkey_filepath.empty()) {
  85. wprintf_s(L"[*] Import RSA-2048 private key from\n");
  86. wprintf_s(L" %s\n", rsa_privkey_filepath.native().c_str());
  87. cipher.import_private_key_file(rsa_privkey_filepath);
  88. if (solution0 && !solution0->check_rsa_privkey(cipher)) {
  89. throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"The RSA private key you provide cannot be used.");
  90. }
  91. } else {
  92. wprintf_s(L"[*] 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. wprintf_s(L"[*] Your RSA private key:\n%s\n", nkg::cp_converter<CP_UTF8, -1>::convert(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() + L".bak";
  105. if (std::filesystem::is_regular_file(backup_path)) {
  106. while (true) {
  107. wprintf_s(L"[*] Previous backup %s is detected. Delete? (y/n)", backup_path.native().c_str());
  108. auto select = getwchar();
  109. while (select != L'\n' && getwchar() != L'\n') {}
  110. if (select == L'Y' || select == L'y') {
  111. std::filesystem::remove(backup_path);
  112. break;
  113. } else if (select == TEXT('N') || select == TEXT('n')) {
  114. throw nkg::exceptions::operation_canceled_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"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() + L".bak";
  123. if (std::filesystem::exists(backup_path)) {
  124. throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Previous backup is detected.")
  125. .push_hint(fmt::format(u8"Please delete {} and try again.", nkg::cp_converter<-1, CP_UTF8>::convert(backup_path.native())));
  126. } else {
  127. std::filesystem::copy_file(file_path, backup_path);
  128. }
  129. }
  130. int wmain(int argc, wchar_t* argv[]) {
  131. welcome();
  132. bool dry_run = false;
  133. std::filesystem::path navicat_install_path;
  134. std::filesystem::path rsa_privkey_filepath;
  135. if (parse_cmdline(argc, argv, dry_run, navicat_install_path, rsa_privkey_filepath)) {
  136. try {
  137. if (!std::filesystem::is_directory(navicat_install_path)) {
  138. throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"Navicat install path doesn't point to a directory.")
  139. .push_hint(u8"Are you sure the path you specified is correct?")
  140. .push_hint(fmt::format(u8"The path you specified: {}", nkg::cp_converter<-1, CP_UTF8>::convert(navicat_install_path.native())));
  141. }
  142. if (!rsa_privkey_filepath.empty() && !std::filesystem::is_regular_file(rsa_privkey_filepath)) {
  143. throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"RSA key file path doesn't point to a file.")
  144. .push_hint(u8"Are you sure the path you specified is correct?")
  145. .push_hint(fmt::format(u8"The path you specified: {}", nkg::cp_converter<-1, CP_UTF8>::convert(rsa_privkey_filepath.native())));
  146. }
  147. nkg::rsa_cipher cipher;
  148. std::filesystem::path libcc_filepath = navicat_install_path / "libcc.dll";
  149. nkg::resource_wrapper libcc_handle{ nkg::resource_traits::win32::file_handle{} };
  150. nkg::resource_wrapper libcc_map_handle{ nkg::resource_traits::win32::generic_handle{} };
  151. nkg::resource_wrapper libcc_map_view{ nkg::resource_traits::win32::map_view_ptr{} };
  152. std::optional<nkg::image_interpreter> libcc_interpreter;
  153. nkg::resource_wrapper solution0{ nkg::resource_traits::cxx_object_traits<nkg::patch_solution>{} };
  154. // open libcc.dll
  155. libcc_handle.set(CreateFileW(libcc_filepath.native().c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL));
  156. if (libcc_handle.is_valid()) {
  157. wprintf_s(L"[+] Try to open libcc.dll ... OK!\n");
  158. } else {
  159. if (GetLastError() == ERROR_FILE_NOT_FOUND) {
  160. wprintf_s(L"[-] Try to open libcc.dll ... NOT FOUND!\n");
  161. } else {
  162. throw nkg::exceptions::win32_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), GetLastError(), u8"Failed to open libcc.dll");
  163. }
  164. }
  165. if (libcc_handle.is_valid()) {
  166. libcc_map_handle.set(CreateFileMapping(libcc_handle.get(), NULL, PAGE_READWRITE, 0, 0, NULL));
  167. if (!libcc_map_handle.is_valid()) {
  168. throw nkg::exceptions::win32_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), GetLastError(), u8"CreateFileMapping failed.");
  169. }
  170. libcc_map_view.set(MapViewOfFile(libcc_map_handle.get(), FILE_MAP_ALL_ACCESS, 0, 0, 0));
  171. if (!libcc_map_view.is_valid()) {
  172. throw nkg::exceptions::win32_exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), GetLastError(), u8"MapViewOfFile failed.");
  173. }
  174. libcc_interpreter = nkg::image_interpreter::parse(libcc_map_view.get(), true);
  175. solution0.set(new nkg::patch_solution_since<16, 0, 7, 0>(libcc_interpreter.value()));
  176. }
  177. _putws(L"");
  178. // find patch and decide which solution will be applied
  179. if (solution0.is_valid()) {
  180. auto patch_found = solution0->find_patch();
  181. _putws(L"");
  182. if (!patch_found) {
  183. solution0.release();
  184. }
  185. }
  186. select_patch_solutions(solution0);
  187. if (all_patch_solutions_are_suppressed(solution0)) {
  188. throw nkg::exception(NKG_CURRENT_SOURCE_FILE(), NKG_CURRENT_SOURCE_LINE(), u8"All patch solutions are suppressed. Patch abort!")
  189. .push_hint(u8"Are you sure your navicat has not been patched/modified before?");
  190. }
  191. // load key
  192. load_rsa_privkey(cipher, rsa_privkey_filepath, solution0.get());
  193. // apply patch solutions
  194. if (dry_run) {
  195. _putws(L"*******************************************************");
  196. _putws(L"* DRY-RUN MODE ENABLE! *");
  197. _putws(L"* NO PATCH WILL BE APPLIED! *");
  198. _putws(L"*******************************************************");
  199. } else {
  200. // save private key if not given
  201. if (rsa_privkey_filepath.empty()) {
  202. cipher.export_private_key_file(u8"RegPrivateKey.pem");
  203. }
  204. // detecting backups
  205. if (solution0.is_valid()) {
  206. detect_backup(libcc_filepath);
  207. }
  208. // make backup
  209. if (solution0.is_valid()) {
  210. make_backup(libcc_filepath);
  211. }
  212. // make patch
  213. // no way to go back from here :-)
  214. if (solution0.is_valid()) {
  215. solution0->make_patch(cipher);
  216. }
  217. // print new key file path
  218. if (rsa_privkey_filepath.empty()) {
  219. wprintf_s(L"[*] New RSA-2048 private key has been saved to\n");
  220. wprintf_s(L" %s\n", (std::filesystem::current_path() / L"RegPrivateKey.pem").c_str());
  221. wprintf_s(L"\n");
  222. }
  223. _putws(L"");
  224. _putws(L"*******************************************************");
  225. _putws(L"* PATCH HAS BEEN DONE SUCCESSFULLY! *");
  226. _putws(L"* HAVE FUN AND ENJOY~ *");
  227. _putws(L"*******************************************************");
  228. }
  229. return 0;
  230. } catch (nkg::exception& e) {
  231. wprintf_s(L"[-] %s:%d ->\n", nkg::cp_converter<CP_UTF8, -1>::convert(e.source_file()).c_str(), e.source_line());
  232. wprintf_s(L" %s\n", nkg::cp_converter<CP_UTF8, -1>::convert(e.custom_message()).c_str());
  233. if (e.error_code_exists()) {
  234. wprintf_s(L" %s (0x%zx)\n", nkg::cp_converter<CP_UTF8, -1>::convert(e.error_string()).c_str(), e.error_code());
  235. }
  236. for (auto& hint : e.hints()) {
  237. wprintf_s(L" HINT: %s\n", nkg::cp_converter<CP_UTF8, -1>::convert(hint).c_str());
  238. }
  239. return -1;
  240. }
  241. } else {
  242. help();
  243. return -1;
  244. }
  245. }
  246. #undef NKG_CURRENT_SOURCE_LINE
  247. #undef NKG_CURRENT_SOURCE_FILE