Skip to content

Commit 14ba65f

Browse files
committed
LooseFileLoader: Fix for Monster Hunter Wilds
1 parent 12e6dbe commit 14ba65f

File tree

1 file changed

+88
-37
lines changed

1 file changed

+88
-37
lines changed

src/mods/LooseFileLoader.cpp

+88-37
Original file line numberDiff line numberDiff line change
@@ -127,59 +127,110 @@ void LooseFileLoader::hook() {
127127
return;
128128
}
129129

130-
const auto via_io_file = tdb->find_type("via.io.file");
130+
auto initial_candidate = [&]() -> std::optional<uintptr_t> {
131+
const auto via_io_file = tdb->find_type("via.io.file");
131132

132-
if (via_io_file == nullptr) {
133-
spdlog::error("[LooseFileLoader] Failed to find via.io.file");
134-
return;
135-
}
133+
if (via_io_file == nullptr) {
134+
spdlog::error("[LooseFileLoader] Failed to find via.io.file");
135+
return std::nullopt;
136+
}
136137

137-
// "exist" and "exists" are 2 different functions. one searches in pak and other doesnt.
138-
const auto exists_fn = via_io_file->get_method("exists(System.String)");
138+
// "exist" and "exists" are 2 different functions. one searches in pak and other doesnt.
139+
const auto exists_fn = via_io_file->get_method("exists(System.String)");
139140

140-
if (exists_fn == nullptr) {
141-
spdlog::error("[LooseFileLoader] Failed to find via.io.file.exists(System.String)");
142-
return;
143-
}
141+
if (exists_fn == nullptr) {
142+
spdlog::error("[LooseFileLoader] Failed to find via.io.file.exists(System.String)");
143+
return std::nullopt;
144+
}
144145

145-
const auto exists_ptr = exists_fn->get_function();
146+
const auto exists_ptr = exists_fn->get_function();
146147

147-
if (exists_ptr == nullptr) {
148-
spdlog::error("[LooseFileLoader] Failed to get function pointer for via.io.file.exists(System.String)");
149-
return;
150-
}
148+
if (exists_ptr == nullptr) {
149+
spdlog::error("[LooseFileLoader] Failed to get function pointer for via.io.file.exists(System.String)");
150+
return std::nullopt;
151+
}
152+
153+
return (uintptr_t)exists_ptr;
154+
}();
151155

152-
std::optional<uintptr_t> candidate{};
153156
const auto game_module = utility::get_executable();
154157
const auto game_module_size = utility::get_module_size(game_module).value_or(0);
155158
const auto game_module_end = (uintptr_t)game_module + game_module_size;
156159

157-
utility::exhaustive_decode((uint8_t*)exists_ptr, 500, [&](utility::ExhaustionContext& ctx) -> utility::ExhaustionResult {
158-
if (candidate) {
159-
return utility::ExhaustionResult::BREAK;
160-
}
160+
std::optional<uintptr_t> candidate{};
161161

162-
// We do not care about the address if it is not in the game module, e.g. inside of kernel32.dll
163-
if (ctx.addr < (uintptr_t)game_module || ctx.addr > game_module_end) {
164-
return utility::ExhaustionResult::BREAK;
165-
}
162+
auto check_fn = [&](uintptr_t exists_ptr) {
163+
spdlog::info("[LooseFileLoader] Scanning for path_to_hash candidate at {:x}", exists_ptr);
166164

167-
// This means we have just entered call or something
168-
if (ctx.branch_start == ctx.addr) {
169-
if (auto bs = utility::find_string_reference_in_path(ctx.branch_start, L"\\", false); bs.has_value()) {
170-
spdlog::info("[LooseFileLoader] Found a backslash reference at {:x}", bs->addr);
165+
utility::exhaustive_decode((uint8_t*)exists_ptr, 500, [&](utility::ExhaustionContext& ctx) -> utility::ExhaustionResult {
166+
if (candidate) {
167+
return utility::ExhaustionResult::BREAK;
168+
}
171169

172-
// Now check if murmur hash constant is in the path
173-
if (utility::find_pattern_in_path((uint8_t*)ctx.branch_start, 500, true, "? ? 6B CA EB 85")) {
174-
candidate = ctx.branch_start;
175-
spdlog::info("[LooseFileLoader] Found path_to_hash candidate at {:x}", *candidate);
176-
return utility::ExhaustionResult::BREAK;
170+
// We do not care about the address if it is not in the game module, e.g. inside of kernel32.dll
171+
if (ctx.addr < (uintptr_t)game_module || ctx.addr > game_module_end) {
172+
return utility::ExhaustionResult::BREAK;
173+
}
174+
175+
// This means we have just entered call or something
176+
if (ctx.branch_start == ctx.addr) {
177+
if (auto bs = utility::find_string_reference_in_path(ctx.branch_start, L"\\", false); bs.has_value()) {
178+
spdlog::info("[LooseFileLoader] Found a backslash reference at {:x}", bs->addr);
179+
180+
// Now check if murmur hash constant is in the path
181+
if (utility::find_pattern_in_path((uint8_t*)ctx.branch_start, 500, true, "? ? 6B CA EB 85")) {
182+
candidate = ctx.branch_start;
183+
spdlog::info("[LooseFileLoader] Found path_to_hash candidate at {:x}", *candidate);
184+
return utility::ExhaustionResult::BREAK;
185+
}
177186
}
178187
}
179-
}
180188

181-
return utility::ExhaustionResult::CONTINUE;
182-
});
189+
return utility::ExhaustionResult::CONTINUE;
190+
});
191+
};
192+
193+
if (!initial_candidate) {
194+
// Basically what we're doing here is finding an initial "mov r8d, 800h"
195+
// and then finding a "mov r8d, 400h" in the function, as well as another "mov r8d, 800h"
196+
// I call this a landmark scan, where we find a sequence of instructions that are unique to the function
197+
// but they don't need to be near each other, they just need to be in the same function.
198+
// Some other giveaways of the function are the uses of the UTF-16 backslash characters
199+
// and the two calls to murmur hash functions at the end (these can be found in System.String's GetHashCode)
200+
const std::string start_seq {"41 B8 00 08 00 00"}; // mov r8d, 800h
201+
std::vector<std::string> patterns_in_function {
202+
"BA 00 04 00 00",
203+
"41 B8 00 08 00 00",
204+
};
205+
206+
std::unordered_set<uintptr_t> analyzed_fns{};
207+
208+
for (auto new_candidate = utility::find_landmark_sequence(game_module, start_seq, patterns_in_function, false);
209+
new_candidate.has_value();
210+
new_candidate = utility::find_landmark_sequence(new_candidate->addr + new_candidate->instrux.Length, game_module_end - (new_candidate->addr + 1), start_seq, patterns_in_function, false))
211+
{
212+
const auto fn_start = utility::find_function_start_with_call(new_candidate->addr);
213+
214+
if (!fn_start.has_value()) {
215+
spdlog::error("[LooseFileLoader] Failed to find path_to_hash candidate's start, cannot continue");
216+
continue;
217+
}
218+
219+
if (analyzed_fns.contains(fn_start.value())) {
220+
continue;
221+
}
222+
223+
analyzed_fns.insert(fn_start.value());
224+
225+
check_fn(*fn_start);
226+
227+
if (candidate) {
228+
break;
229+
}
230+
}
231+
} else {
232+
check_fn(initial_candidate.value());
233+
}
183234

184235
if (!candidate) {
185236
spdlog::error("[LooseFileLoader] Failed to find path_to_hash candidate, cannot continue");

0 commit comments

Comments
 (0)