Skip to content

Commit dc06e95

Browse files
committed
Merge branch 'master' into csharp-api
2 parents 81403f4 + 6a38ef8 commit dc06e95

File tree

3 files changed

+239
-6
lines changed

3 files changed

+239
-6
lines changed

shared/sdk/RETypeDefinition.cpp

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -617,11 +617,21 @@ uint32_t RETypeDefinition::get_index() const {
617617

618618
int32_t RETypeDefinition::get_fieldptr_offset() const {
619619
#if TDB_VER > 49
620-
if (this->managed_vt == nullptr) {
621-
return 0;
622-
}
620+
#ifdef MHWILDS
621+
REObjectInfo *target_managed_vt = get_managed_vt();
622+
623+
if (target_managed_vt == nullptr) {
624+
return 0;
625+
}
626+
627+
return *(int32_t*)((uintptr_t)target_managed_vt - sizeof(void*));
628+
#else
629+
if (this->managed_vt == nullptr) {
630+
return 0;
631+
}
623632

624-
return *(int32_t*)((uintptr_t)this->managed_vt - sizeof(void*));
633+
return *(int32_t*)((uintptr_t)this->managed_vt - sizeof(void*));
634+
#endif
625635
#else
626636
auto vm = sdk::VM::get();
627637
const auto& vm_type = vm->types[this->get_index()];
@@ -632,7 +642,11 @@ int32_t RETypeDefinition::get_fieldptr_offset() const {
632642

633643
bool RETypeDefinition::has_fieldptr_offset() const {
634644
#if TDB_VER > 49
635-
return this->managed_vt != nullptr;
645+
#ifdef MHWILDS
646+
return get_managed_vt() != nullptr;
647+
#else
648+
return this->managed_vt != nullptr;
649+
#endif
636650
#else
637651
return true;
638652
#endif
@@ -1119,7 +1133,31 @@ ::REManagedObject* RETypeDefinition::create_instance_full(bool simplify) {
11191133

11201134
::REObjectInfo* RETypeDefinition::get_managed_vt() const {
11211135
#if TDB_VER > 49
1122-
return (::REObjectInfo*)this->managed_vt;
1136+
#if MHWILDS
1137+
REObjectInfo *target_managed_vt = this->managed_vt;
1138+
const int ABSTRACT_TYPE_FLAG = 128;
1139+
1140+
if (this->managed_vt == nullptr) {
1141+
// Abstract
1142+
if (this->type_flags & ABSTRACT_TYPE_FLAG) {
1143+
// Keep getting parent type until getting a hit
1144+
auto parent_type_def = this->get_parent_type();
1145+
while (parent_type_def != nullptr) {
1146+
target_managed_vt = parent_type_def->managed_vt;
1147+
1148+
if (target_managed_vt != nullptr) {
1149+
break;
1150+
}
1151+
1152+
parent_type_def = parent_type_def->get_parent_type();
1153+
}
1154+
}
1155+
}
1156+
1157+
return target_managed_vt;
1158+
#else
1159+
return (::REObjectInfo*)this->managed_vt;
1160+
#endif
11231161
#else
11241162
return (::REObjectInfo*)&sdk::VM::get()->types[this->get_index()];
11251163
#endif

src/mods/IntegrityCheckBypass.cpp

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include <unordered_set>
22
#include <iomanip>
3+
#include <regex>
34

45
#include "utility/Module.hpp"
56
#include "utility/Scan.hpp"
@@ -230,6 +231,8 @@ std::optional<std::string> IntegrityCheckBypass::on_initialize() {
230231
}
231232
#endif
232233

234+
s_patch_count_checked = false;
235+
233236
spdlog::info("Done.");
234237

235238
return Mod::on_initialize();
@@ -634,6 +637,109 @@ void IntegrityCheckBypass::init_anti_debug_watcher() {
634637
});
635638
}
636639

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+
637743
// This allows unencrypted paks to load.
638744
void IntegrityCheckBypass::sha3_rsa_code_midhook(safetyhook::Context& context) {
639745
spdlog::info("[IntegrityCheckBypass]: sha3_code_midhook called!");
@@ -771,6 +877,33 @@ void IntegrityCheckBypass::restore_unencrypted_paks() {
771877

772878
spdlog::info("[IntegrityCheckBypass]: Created sha3_rsa_code_midhook!");
773879

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+
774907
auto previous_instructions = utility::get_disassembly_behind(*s_sha3_code_end);
775908
auto previous_instructions_start = utility::get_disassembly_behind(*sha3_code_start);
776909

@@ -826,6 +959,60 @@ void IntegrityCheckBypass::restore_unencrypted_paks() {
826959
}
827960
}
828961

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+
8291016
void IntegrityCheckBypass::immediate_patch_dd2() {
8301017
// Just like RE4, this deals with the scans that are done every frame on the game's memory.
8311018
// The scans are still performed, but the crash will be avoided.

src/mods/IntegrityCheckBypass.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,18 @@ class IntegrityCheckBypass : public Mod {
4545
static inline uint32_t s_last_non_zero_corruption{ 8 }; // What I've seen it default to
4646

4747
static void sha3_rsa_code_midhook(safetyhook::Context& context);
48+
static void pak_load_check_function(safetyhook::Context& context);
49+
static void patch_version_hook(safetyhook::Context& context);
50+
static int scan_patch_files_count();
4851
static void restore_unencrypted_paks();
4952
static inline safetyhook::MidHook s_sha3_rsa_code_midhook;
53+
static inline safetyhook::MidHook s_pak_load_check_function_hook;
54+
static inline safetyhook::MidHook s_patch_version_hook;
5055
static inline std::optional<uintptr_t> s_sha3_code_end{};
5156
static inline int32_t s_sha3_reg_index{-1};
57+
static inline int32_t s_patch_version_reg_index{-1};
58+
static inline int s_patch_count;
59+
static inline bool s_patch_count_checked;
5260

5361
static void anti_debug_watcher();
5462
static void init_anti_debug_watcher();

0 commit comments

Comments
 (0)