Skip to content

Commit 543ed2a

Browse files
authored
Merge branch 'praydog:master' into master
2 parents 0109df9 + b7bde94 commit 543ed2a

7 files changed

Lines changed: 262 additions & 10 deletions

File tree

reversing/dd2.genny

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,23 @@ namespace via {
166166
}
167167
}
168168

169+
namespace via {
170+
struct Application 0x5000 {
171+
struct Function {
172+
void* entry; // 0
173+
void* func;
174+
void* unk; // 0x10
175+
char* description [[utf8*]]; // 0x18
176+
uint16_t priority; // 0x20 (via.ModuleEntry enum)
177+
uint16_t type; // 0x22
178+
179+
uint8_t pad[0xAC];
180+
}
181+
182+
Function functions[100] @ 0x808
183+
}
184+
}
185+
169186
namespace via {
170187
struct Transform{};
171188
struct Folder{};

shared/sdk/Application.cpp

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,24 @@ Application::Function* Application::get_functions() {
4343
const auto candidate = *(int32_t*)(*ref + 12) - 8;
4444

4545
// If the offset is aligned to 8 bytes, it's a valid offset.
46-
if (((uintptr_t)candidate & (sizeof(void*) - 1)) == 0 && candidate >= 0x400) {
46+
if (((uintptr_t)candidate & (sizeof(void*) - 1)) == 0 && candidate >= 0x400 && candidate < 0x5000) {
47+
try {
48+
const auto ptr = (Application::Function*)((uintptr_t)this + candidate);
49+
50+
if (ptr == nullptr || IsBadReadPtr(ptr, sizeof(Application::Function) * 50)) {
51+
spdlog::info("Skipping invalid Application::functions offset: {:x}", candidate);
52+
continue;
53+
}
54+
55+
if (ptr->description == nullptr || IsBadReadPtr(ptr->description, 32)) {
56+
spdlog::info("Skipping invalid Application::functions offset: {:x}", candidate);
57+
continue;
58+
}
59+
} catch (...) {
60+
spdlog::info("Skipping invalid Application::functions offset: {:x}", candidate);
61+
continue;
62+
}
63+
4764
spdlog::info("Application::functions offset: {:x}", candidate);
4865
return candidate;
4966
}
@@ -64,7 +81,24 @@ Application::Function* Application::get_functions() {
6481
const auto candidate = *(int32_t*)(*ref + 10);
6582

6683
// If the offset is aligned to 8 bytes, it's a valid offset.
67-
if (((uintptr_t)candidate & (sizeof(void*) - 1)) == 0 && candidate >= 0x400) {
84+
if (((uintptr_t)candidate & (sizeof(void*) - 1)) == 0 && candidate >= 0x400 && candidate < 0x5000) {
85+
try {
86+
const auto ptr = (Application::Function*)((uintptr_t)this + candidate);
87+
88+
if (ptr == nullptr || IsBadReadPtr(ptr, sizeof(Application::Function) * 50)) {
89+
spdlog::info("Skipping invalid Application::functions offset: {:x}", candidate);
90+
continue;
91+
}
92+
93+
if (ptr->description == nullptr || IsBadReadPtr(ptr->description, 32)) {
94+
spdlog::info("Skipping invalid Application::functions offset: {:x}", candidate);
95+
continue;
96+
}
97+
} catch (...) {
98+
spdlog::info("Skipping invalid Application::functions offset: {:x}", candidate);
99+
continue;
100+
}
101+
68102
spdlog::info("Application::functions offset: {:x}", candidate);
69103
return candidate;
70104
}

shared/sdk/REContext.cpp

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ namespace sdk {
165165
return;
166166
}
167167

168+
sdk::RETypeDB* tdb = nullptr;
169+
168170
for (auto i = 0; i < 0x20000; i += sizeof(void*)) {
169171
auto ptr = *(sdk::RETypeDB**)((uintptr_t)*potential_context + i);
170172

@@ -173,6 +175,7 @@ namespace sdk {
173175
}
174176

175177
if (*(uint32_t*)ptr == *(uint32_t*)"TDB") {
178+
tdb = ptr;
176179
const auto version = *(uint32_t*)((uintptr_t)ptr + 4);
177180

178181
s_tdb_version = version;
@@ -286,6 +289,93 @@ namespace sdk {
286289
48 8B CF mov rcx, rdi
287290
*/
288291

292+
auto alternative_invoke_scan = [&]() -> bool {
293+
auto tdb_references = utility::scan_displacement_references(mod, (uintptr_t)tdb);
294+
295+
if (tdb_references.empty()) {
296+
spdlog::info("[VM::update_pointers] Unable to find TDB references.");
297+
return false;
298+
}
299+
300+
for (const auto& ref : tdb_references) {
301+
const auto fn_start = utility::find_function_start(ref);
302+
303+
if (!fn_start) {
304+
continue;
305+
}
306+
307+
spdlog::info("[VM::update_pointers] Disassembling function at {:x}", *fn_start);
308+
309+
bool found = false;
310+
311+
utility::exhaustive_decode((uint8_t*)*fn_start, 1000, [&](utility::ExhaustionContext& ctx) -> utility::ExhaustionResult {
312+
if (found) {
313+
return utility::ExhaustionResult::BREAK;
314+
}
315+
316+
if (std::string_view{ctx.instrux.Mnemonic} == "CALL") {
317+
return utility::ExhaustionResult::STEP_OVER;
318+
}
319+
320+
if (std::string_view{ctx.instrux.Mnemonic} != "LEA") {
321+
return utility::ExhaustionResult::CONTINUE;
322+
}
323+
324+
const auto disp = utility::resolve_displacement(ctx.addr);
325+
326+
if (!disp) {
327+
return utility::ExhaustionResult::CONTINUE;
328+
}
329+
330+
try {
331+
uintptr_t* functions = (uintptr_t*)*disp;
332+
333+
// First pointer must always be null
334+
if (functions[0] != 0) {
335+
return utility::ExhaustionResult::CONTINUE;
336+
}
337+
338+
// Rest of pointers are not null and point somewhere within the game module
339+
for (auto i = 1; i < 100; ++i) {
340+
if (functions[i] == 0 || IsBadReadPtr(&functions[i], sizeof(void*))) {
341+
return utility::ExhaustionResult::CONTINUE;
342+
}
343+
344+
if (utility::get_module_within(functions[i]).value_or(nullptr) != mod) {
345+
return utility::ExhaustionResult::CONTINUE;
346+
}
347+
348+
/*const auto ptr_fn_start = utility::find_function_start(functions[i]);
349+
350+
if (!ptr_fn_start) {
351+
return utility::ExhaustionResult::CONTINUE;
352+
}
353+
354+
if (*ptr_fn_start != functions[i]) {
355+
break;
356+
}*/
357+
}
358+
359+
s_invoke_tbl = (sdk::InvokeMethod*)functions;
360+
found = true;
361+
362+
spdlog::info("[VM::update_pointers] s_invoke_tbl: {:x}", (uintptr_t)s_invoke_tbl);
363+
364+
return utility::ExhaustionResult::BREAK;
365+
} catch (...) {
366+
return utility::ExhaustionResult::CONTINUE;
367+
}
368+
369+
return utility::ExhaustionResult::CONTINUE;
370+
});
371+
372+
if (found) {
373+
return true;
374+
}
375+
}
376+
377+
return false;
378+
};
289379

290380
std::optional<uintptr_t> method_inside_invoke_tbl{std::nullopt};
291381

@@ -297,19 +387,32 @@ namespace sdk {
297387
break;
298388
}
299389
}
390+
300391
if (!method_inside_invoke_tbl) {
301392
spdlog::info("[VM::update_pointers] Unable to find method inside invoke table. Trying fallback scan...");
302393
const auto anchor = utility::scan(mod, "8D 56 FF 48 8B CF E8 ? ? ? ?");
303394

304395
if (!anchor) {
305-
spdlog::info("[VM::update_pointers] Unable to find anchor for invoke table.");
396+
spdlog::info("[VM::update_pointers] Unable to find anchor for invoke table, trying alternative scan...");
397+
398+
if (!alternative_invoke_scan()) {
399+
spdlog::info("[VM::update_pointers] Unable to find invoke table.");
400+
return;
401+
}
402+
306403
return;
307404
}
308405

309406
const auto lea_rdx = utility::scan_reverse(*anchor, 0x100, "48 8D 15 ? ? ? ?");
310407

311408
if (!lea_rdx) {
312409
spdlog::info("[VM::update_pointers] Unable to find lea rdx for invoke table.");
410+
411+
if (!alternative_invoke_scan()) {
412+
spdlog::info("[VM::update_pointers] Unable to find invoke table.");
413+
return;
414+
}
415+
313416
return;
314417
}
315418

@@ -326,6 +429,12 @@ namespace sdk {
326429

327430
if (!ptr_inside_invoke_tbl) {
328431
spdlog::info("[VM::update_pointers] Unable to find ptr inside invoke table.");
432+
433+
if (!alternative_invoke_scan()) {
434+
spdlog::info("[VM::update_pointers] Unable to find invoke table.");
435+
return;
436+
}
437+
329438
return;
330439
}
331440

shared/sdk/RETypeDB.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,13 +457,62 @@ void* REMethodDefinition::get_function() const {
457457
// The function this function uses to offset from has a predictable pattern as well.
458458
// So the pattern for that can be searched for if this one breaks.
459459
// It remains to be seen if this "encoding" is used in games other than MHRise.
460+
static uintptr_t encoded_function_base = [&]() -> uintptr_t {
461+
const auto game = utility::get_executable();
462+
const auto invoke_table = sdk::VM::get_invoke_table();
463+
464+
if (invoke_table == nullptr) {
465+
spdlog::error("[REMethodDefinition] Failed to find invoke table, cannot find encoded_function_base");
466+
return 0;
467+
}
468+
469+
const auto first_fn = invoke_table[1];
470+
471+
if (first_fn == nullptr) {
472+
spdlog::error("[REMethodDefinition] Failed to find first function in invoke table, cannot find encoded_function_base");
473+
return 0;
474+
}
475+
476+
uintptr_t result = 0;
477+
478+
utility::exhaustive_decode((uint8_t*)first_fn, 100, [&](utility::ExhaustionContext& ctx) -> utility::ExhaustionResult {
479+
if (result != 0) {
480+
return utility::ExhaustionResult::BREAK;
481+
}
482+
483+
if (std::string_view{ctx.instrux.Mnemonic} != "LEA") {
484+
return utility::ExhaustionResult::CONTINUE;
485+
}
486+
487+
const auto disp = utility::resolve_displacement(ctx.addr);
488+
489+
if (!disp) {
490+
return utility::ExhaustionResult::CONTINUE;
491+
}
492+
493+
if (utility::get_module_within(*disp).value_or(nullptr) != game) {
494+
return utility::ExhaustionResult::CONTINUE;
495+
}
496+
497+
result = *disp;
498+
return utility::ExhaustionResult::BREAK;
499+
});
500+
501+
spdlog::info("[REMethodDefinition] Found encoded_function_base at {:x}", result);
502+
503+
return result;
504+
}();
505+
460506
static void* (*get_encoded_pointer)(int32_t offset) = []() {
461507
spdlog::info("[REMethodDefinition] Finding get_encoded_pointer");
462508

463509
auto fn = utility::scan(utility::get_executable(), "85 C9 75 03 33 C0 C3 48 63 C1 48 8d 0D ? ? ? ? 48 03 C1 C3");
464510

511+
// Alternative scan where we find the first LEA instruction that loads a pointer to a function basically
512+
// inside the invoke table.
465513
if (!fn) {
466514
spdlog::error("[REMethodDefinition] Failed to find get_encoded_pointer");
515+
467516
return (void* (*)(int32_t))nullptr;
468517
}
469518

@@ -473,6 +522,10 @@ void* REMethodDefinition::get_function() const {
473522
}();
474523

475524
if (get_encoded_pointer == nullptr) {
525+
if (encoded_function_base != 0) {
526+
return (void*)(encoded_function_base + this->encoded_offset);
527+
}
528+
476529
return nullptr;
477530
}
478531

shared/sdk/ResourceManager.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ void ResourceManager::update_pointers() {
100100
return;
101101
}
102102

103+
spdlog::info("[ResourceManager::create_resource] Found string reference at {:x}", *string_reference);
104+
103105
// use HDE to disasm *string_reference - 3 and disasm forward a bit
104106
// to find a call instruction which is the function we want
105107
auto ip = *string_reference - 3;
@@ -126,8 +128,10 @@ void ResourceManager::update_pointers() {
126128
// now find create_userdata, using the previous function as a reference to ignore
127129
// since they both have the same pattern at the start of the function
128130
const auto valid_patterns = {
131+
#if TDB_VER < 73
129132
"66 83 F8 40 75 ? C6",
130133
"66 83 F8 40 75 ? 48",
134+
#endif
131135
"66 41 83 39 40" // DD2+
132136
};
133137

src/mods/Hooks.cpp

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -553,13 +553,18 @@ std::optional<std::string> Hooks::hook_view_get_size() {
553553
spdlog::info("via.SceneView.get_Size: {:x}", (uintptr_t)get_size_func);
554554

555555
// Pattern scan for the native function call
556-
auto ref = utility::scan((uintptr_t)get_size_func, 0x100, "49 8B C8 E8");
556+
//auto ref = utility::scan((uintptr_t)get_size_func, 0x100, "49 8B C8 E8");
557+
auto ref = utility::find_pattern_in_path((uint8_t*)get_size_func, 1000, false, "49 8B C8 E8");
558+
559+
if (!ref) {
560+
ref = utility::find_pattern_in_path((uint8_t*)get_size_func, 1000, false, "48 8B CB E8");
561+
}
557562

558563
if (!ref) {
559564
return "Hook init failed: via.SceneView.get_Size native function not found. Pattern scan failed.";
560565
}
561566

562-
auto native_func = utility::calculate_absolute(*ref + 4);
567+
auto native_func = utility::calculate_absolute(ref->addr + 4);
563568

564569
// Hook the native function
565570
m_view_get_size_hook = std::make_unique<FunctionHook>(native_func, view_get_size_hook);
@@ -583,13 +588,17 @@ std::optional<std::string> Hooks::hook_camera_get_projection_matrix() {
583588
spdlog::info("via.Camera.get_ProjectionMatrix: {:x}", (uintptr_t)func);
584589

585590
// Pattern scan for the native function call
586-
auto ref = utility::scan((uintptr_t)func, 0x100, "49 8B C8 E8");
591+
auto ref = utility::find_pattern_in_path((uint8_t*)func, 1000, false, "49 8B C8 E8");
592+
593+
if (!ref) {
594+
ref = utility::find_pattern_in_path((uint8_t*)func, 1000, false, "48 8B CB E8");
595+
}
587596

588597
if (!ref) {
589598
return "Hook init failed: via.Camera.get_ProjectionMatrix native function not found. Pattern scan failed.";
590599
}
591600

592-
auto native_func = utility::calculate_absolute(*ref + 4);
601+
auto native_func = utility::calculate_absolute(ref->addr + 4);
593602

594603
// Hook the native function
595604
m_camera_get_projection_matrix_hook = std::make_unique<FunctionHook>(native_func, camera_get_projection_matrix_hook);
@@ -613,13 +622,17 @@ std::optional<std::string> Hooks::hook_camera_get_view_matrix() {
613622
spdlog::info("via.Camera.get_ViewMatrix: {:x}", (uintptr_t)func);
614623

615624
// Pattern scan for the native function call
616-
auto ref = utility::scan((uintptr_t)func, 0x100, "49 8B C8 E8");
625+
auto ref = utility::find_pattern_in_path((uint8_t*)func, 1000, false, "49 8B C8 E8");
626+
627+
if (!ref) {
628+
ref = utility::find_pattern_in_path((uint8_t*)func, 1000, false, "48 8B CB E8");
629+
}
617630

618631
if (!ref) {
619632
return "Hook init failed: via.Camera.get_ViewMatrix native function not found. Pattern scan failed.";
620633
}
621634

622-
auto native_func = utility::calculate_absolute(*ref + 4);
635+
auto native_func = utility::calculate_absolute(ref->addr + 4);
623636

624637
// Hook the native function
625638
m_camera_get_view_matrix_hook = std::make_unique<FunctionHook>(native_func, camera_get_view_matrix_hook);

0 commit comments

Comments
 (0)