|
1 | 1 | #include <unordered_set> |
2 | 2 | #include <iomanip> |
| 3 | +#include <regex> |
3 | 4 |
|
4 | 5 | #include "utility/Module.hpp" |
5 | 6 | #include "utility/Scan.hpp" |
@@ -230,6 +231,8 @@ std::optional<std::string> IntegrityCheckBypass::on_initialize() { |
230 | 231 | } |
231 | 232 | #endif |
232 | 233 |
|
| 234 | + s_patch_count_checked = false; |
| 235 | + |
233 | 236 | spdlog::info("Done."); |
234 | 237 |
|
235 | 238 | return Mod::on_initialize(); |
@@ -634,6 +637,109 @@ void IntegrityCheckBypass::init_anti_debug_watcher() { |
634 | 637 | }); |
635 | 638 | } |
636 | 639 |
|
| 640 | +void IntegrityCheckBypass::pak_load_check_function(safetyhook::Context& context) { |
| 641 | + const auto return_address = *reinterpret_cast<uintptr_t*>(context.rsp); |
| 642 | + auto pak_name_wstr = reinterpret_cast<const wchar_t*>(context.rdx); |
| 643 | + |
| 644 | + spdlog::info("[IntegrityCheckBypass]: pak_load_check_function called from: 0x{:X}", return_address); |
| 645 | + spdlog::info("[IntegrityCheckBypass]: Pak name: {}", utility::narrow(pak_name_wstr)); |
| 646 | +} |
| 647 | + |
| 648 | +void IntegrityCheckBypass::patch_version_hook(safetyhook::Context& context) { |
| 649 | + // THEY STORE PATCH VERSION INSIDE SOMEWHERE NOW! And only load until that patch version then dont load no more paks |
| 650 | + spdlog::info("[IntegrityCheckBypass]: patch_version_hook called!"); |
| 651 | + |
| 652 | + // Scan for amount of paks. Get exe directory. To be honest set this to 9999 is okay, but i feel like it might take a long time |
| 653 | + int file_count_result = std::max<int>(scan_patch_files_count(), context.rax); |
| 654 | + |
| 655 | + switch (s_patch_version_reg_index) { |
| 656 | + case NDR_RAX: |
| 657 | + spdlog::info("[IntegrityCheckBypass]: Patch version: {}. Game wont load past this patch version. Setting new patch version at RAX to {}", context.rax, file_count_result); |
| 658 | + context.rax = file_count_result; |
| 659 | + break; |
| 660 | + |
| 661 | + case NDR_RCX: |
| 662 | + spdlog::info("[IntegrityCheckBypass]: Patch version: {}. Game wont load past this patch version. Setting new patch version at RCX to {}", context.rcx, file_count_result); |
| 663 | + context.rcx = file_count_result; |
| 664 | + break; |
| 665 | + |
| 666 | + case NDR_RDX: |
| 667 | + spdlog::info("[IntegrityCheckBypass]: Patch version: {}. Game wont load past this patch version. Setting new patch version at RDX to {}", context.rdx, file_count_result); |
| 668 | + context.rdx = file_count_result; |
| 669 | + break; |
| 670 | + |
| 671 | + case NDR_RBX: |
| 672 | + spdlog::info("[IntegrityCheckBypass]: Patch version: {}. Game wont load past this patch version. Setting new patch version at RBX to {}", context.rbx, file_count_result); |
| 673 | + context.rbx = file_count_result; |
| 674 | + break; |
| 675 | + |
| 676 | + case NDR_RSP: |
| 677 | + spdlog::info("[IntegrityCheckBypass]: Patch version: {}. Game wont load past this patch version. Setting new patch version at RSP to {}", context.rsp, file_count_result); |
| 678 | + context.rsp = file_count_result; |
| 679 | + break; |
| 680 | + |
| 681 | + case NDR_RBP: |
| 682 | + spdlog::info("[IntegrityCheckBypass]: Patch version: {}. Game wont load past this patch version. Setting new patch version at RBP to {}", context.rbp, file_count_result); |
| 683 | + context.rbp = file_count_result; |
| 684 | + break; |
| 685 | + |
| 686 | + case NDR_RSI: |
| 687 | + spdlog::info("[IntegrityCheckBypass]: Patch version: {}. Game wont load past this patch version. Setting new patch version at RSI to {}", context.rsi, file_count_result); |
| 688 | + context.rsi = file_count_result; |
| 689 | + break; |
| 690 | + |
| 691 | + case NDR_RDI: |
| 692 | + spdlog::info("[IntegrityCheckBypass]: Patch version: {}. Game wont load past this patch version. Setting new patch version at RDI to {}", context.rdi, file_count_result); |
| 693 | + context.rdi = file_count_result; |
| 694 | + break; |
| 695 | + |
| 696 | + case NDR_R8: |
| 697 | + spdlog::info("[IntegrityCheckBypass]: Patch version: {}. Game wont load past this patch version. Setting new patch version at R8 to {}", context.r8, file_count_result); |
| 698 | + context.r8 = file_count_result; |
| 699 | + break; |
| 700 | + |
| 701 | + case NDR_R9: |
| 702 | + spdlog::info("[IntegrityCheckBypass]: Patch version: {}. Game wont load past this patch version. Setting new patch version at R9 to {}", context.r9, file_count_result); |
| 703 | + context.r9 = file_count_result; |
| 704 | + break; |
| 705 | + |
| 706 | + case NDR_R10: |
| 707 | + spdlog::info("[IntegrityCheckBypass]: Patch version: {}. Game wont load past this patch version. Setting new patch version at R10 to {}", context.r10, file_count_result); |
| 708 | + context.r10 = file_count_result; |
| 709 | + break; |
| 710 | + |
| 711 | + case NDR_R11: |
| 712 | + spdlog::info("[IntegrityCheckBypass]: Patch version: {}. Game wont load past this patch version. Setting new patch version at R11 to {}", context.r11, file_count_result); |
| 713 | + context.r11 = file_count_result; |
| 714 | + break; |
| 715 | + |
| 716 | + case NDR_R12: |
| 717 | + spdlog::info("[IntegrityCheckBypass]: Patch version: {}. Game wont load past this patch version. Setting new patch version at R12 to {}", context.r12, file_count_result); |
| 718 | + context.r12 = file_count_result; |
| 719 | + break; |
| 720 | + |
| 721 | + case NDR_R13: |
| 722 | + spdlog::info("[IntegrityCheckBypass]: Patch version: {}. Game wont load past this patch version. Setting new patch version at R13 to {}", context.r13, file_count_result); |
| 723 | + context.r13 = file_count_result; |
| 724 | + break; |
| 725 | + |
| 726 | + case NDR_R14: |
| 727 | + spdlog::info("[IntegrityCheckBypass]: Patch version: {}. Game wont load past this patch version. Setting new patch version at R14 to {}", context.r14, file_count_result); |
| 728 | + context.r14 = file_count_result; |
| 729 | + break; |
| 730 | + |
| 731 | + case NDR_R15: |
| 732 | + spdlog::info("[IntegrityCheckBypass]: Patch version: {}. Game wont load past this patch version. Setting new patch version at R15 to {}", context.r15, file_count_result); |
| 733 | + context.r15 = file_count_result; |
| 734 | + break; |
| 735 | + |
| 736 | + default: |
| 737 | + spdlog::info("[IntegrityCheckBypass]: Unknown register, falling back to RAX for patch version: {} (update it to {})", context.rax, file_count_result); |
| 738 | + context.rax = file_count_result; // fallback to RAX |
| 739 | + break; |
| 740 | + } |
| 741 | +} |
| 742 | + |
637 | 743 | // This allows unencrypted paks to load. |
638 | 744 | void IntegrityCheckBypass::sha3_rsa_code_midhook(safetyhook::Context& context) { |
639 | 745 | spdlog::info("[IntegrityCheckBypass]: sha3_code_midhook called!"); |
@@ -771,6 +877,33 @@ void IntegrityCheckBypass::restore_unencrypted_paks() { |
771 | 877 |
|
772 | 878 | spdlog::info("[IntegrityCheckBypass]: Created sha3_rsa_code_midhook!"); |
773 | 879 |
|
| 880 | +#ifdef MHWILDS |
| 881 | + const auto pak_load_check_start = utility::scan(game, "41 57 41 56 41 55 41 54 56 57 55 53 48 81 EC ? ? ? ? 48 89 CE 48 8B 05 ? ? ? ? 48 31 E0 48 89 84 24 ? ? ? ? 48 8B 81 ? ? ? ? 48 C1 E8 10"); |
| 882 | + |
| 883 | + if (pak_load_check_start) { |
| 884 | + spdlog::info("[IntegrityCheckBypass]: Found pak_load_check_function @ 0x{:X}, hook!", (uintptr_t)*pak_load_check_start); |
| 885 | + s_pak_load_check_function_hook = safetyhook::create_mid((void*)*pak_load_check_start, &IntegrityCheckBypass::pak_load_check_function); |
| 886 | + } |
| 887 | + |
| 888 | + const auto patch_version_start = utility::scan(game, "48 89 ? 24 ? 48 85 FF 0F 84 ? ? ? ? 66 83 3F 72 0F 85 ? ? ? ? 66 BA 72 00"); |
| 889 | + |
| 890 | + if (patch_version_start) { |
| 891 | + // Before patching, decode the instruction at patch_version_start to find the source register of the MOV instruction |
| 892 | + auto move_instruction = utility::decode_one((std::uint8_t*)*patch_version_start); |
| 893 | + |
| 894 | + spdlog::info("[IntegrityCheckBypass]: Created patch_version_hook to 0x{:X}, hook!", (uintptr_t)*patch_version_start); |
| 895 | + s_patch_version_hook = safetyhook::create_mid((void*)*patch_version_start, &IntegrityCheckBypass::patch_version_hook); |
| 896 | + |
| 897 | + // Get the source register of the MOV instruction |
| 898 | + if (move_instruction && move_instruction->Instruction == ND_INS_MOV && move_instruction->Operands[1].Type == ND_OP_REG) { |
| 899 | + s_patch_version_reg_index = move_instruction->Operands[1].Info.Register.Reg; |
| 900 | + spdlog::info("[IntegrityCheckBypass]: patch_version_reg_index set to {}", s_patch_version_reg_index); |
| 901 | + } else { |
| 902 | + spdlog::error("[IntegrityCheckBypass]: Could not determine patch_version_reg_index! Default to RAX"); |
| 903 | + } |
| 904 | + } |
| 905 | +#endif |
| 906 | + |
774 | 907 | auto previous_instructions = utility::get_disassembly_behind(*s_sha3_code_end); |
775 | 908 | auto previous_instructions_start = utility::get_disassembly_behind(*sha3_code_start); |
776 | 909 |
|
@@ -826,6 +959,60 @@ void IntegrityCheckBypass::restore_unencrypted_paks() { |
826 | 959 | } |
827 | 960 | } |
828 | 961 |
|
| 962 | +int IntegrityCheckBypass::scan_patch_files_count() { |
| 963 | + if (s_patch_count_checked) { |
| 964 | + return s_patch_count; |
| 965 | + } |
| 966 | + |
| 967 | + spdlog::info("[IntegrityCheckBypass]: Scanning for patch files..."); |
| 968 | + |
| 969 | + // Get executable directory |
| 970 | + const auto exe_module = utility::get_executable(); |
| 971 | + const auto exe_path = utility::get_module_pathw(exe_module); |
| 972 | + |
| 973 | + if (!exe_path) { |
| 974 | + spdlog::error("[IntegrityCheckBypass]: Could not get executable path!"); |
| 975 | + return -1; |
| 976 | + } |
| 977 | + |
| 978 | + const auto exe_dir = std::filesystem::path(*exe_path).parent_path(); |
| 979 | + |
| 980 | + spdlog::info("[IntegrityCheckBypass]: Scanning directory: {}", utility::narrow(exe_dir.wstring())); |
| 981 | + |
| 982 | + // Scan for matching files: re_chunk_000.pak.sub_000.pak.patch_0.pak |
| 983 | + // Capture the patch number to find the highest one |
| 984 | + std::regex pattern(R"(re_chunk_\d+\.pak\.sub_\d+\.pak\.patch_(\d+)\.pak)", std::regex::ECMAScript); |
| 985 | + std::smatch match; |
| 986 | + int highest_patch_num = -1; |
| 987 | + |
| 988 | + for (const auto& entry : std::filesystem::directory_iterator(exe_dir)) { |
| 989 | + if (entry.is_regular_file()) { |
| 990 | + const auto filename = entry.path().filename().string(); |
| 991 | + if (std::regex_match(filename, match, pattern)) { |
| 992 | + try { |
| 993 | + const int patch_num = std::stoi(match[1].str()); |
| 994 | + highest_patch_num = std::max(highest_patch_num, patch_num); |
| 995 | + spdlog::info("[IntegrityCheckBypass]: Found patch file: {} (patch_{})", filename, patch_num); |
| 996 | + } catch (const std::exception& e) { |
| 997 | + spdlog::warn("[IntegrityCheckBypass]: Failed to parse patch number from {}: {}", filename, e.what()); |
| 998 | + } |
| 999 | + } |
| 1000 | + } |
| 1001 | + } |
| 1002 | + |
| 1003 | + if (highest_patch_num >= 0) { |
| 1004 | + spdlog::info("[IntegrityCheckBypass]: Highest patch number found: {}", highest_patch_num); |
| 1005 | + } else { |
| 1006 | + spdlog::warn("[IntegrityCheckBypass]: No valid patch files found!"); |
| 1007 | + highest_patch_num = 0; |
| 1008 | + } |
| 1009 | + |
| 1010 | + s_patch_count_checked = true; |
| 1011 | + s_patch_count = highest_patch_num; |
| 1012 | + |
| 1013 | + return highest_patch_num; |
| 1014 | +} |
| 1015 | + |
829 | 1016 | void IntegrityCheckBypass::immediate_patch_dd2() { |
830 | 1017 | // Just like RE4, this deals with the scans that are done every frame on the game's memory. |
831 | 1018 | // The scans are still performed, but the crash will be avoided. |
|
0 commit comments