Skip to content

Commit

Permalink
Merge branch 'master' into csharp-api
Browse files Browse the repository at this point in the history
  • Loading branch information
praydog committed Jun 27, 2024
2 parents 8e6f3aa + de9a1ba commit d28c51f
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 9 deletions.
17 changes: 17 additions & 0 deletions reversing/dd2.genny
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,23 @@ namespace via {
}
}

namespace via {
struct Application 0x5000 {
struct Function {
void* entry; // 0
void* func;
void* unk; // 0x10
char* description [[utf8*]]; // 0x18
uint16_t priority; // 0x20 (via.ModuleEntry enum)
uint16_t type; // 0x22

uint8_t pad[0xAC];
}

Function functions[100] @ 0x808
}
}

namespace via {
struct Transform{};
struct Folder{};
Expand Down
38 changes: 36 additions & 2 deletions shared/sdk/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,24 @@ Application::Function* Application::get_functions() {
const auto candidate = *(int32_t*)(*ref + 12) - 8;

// If the offset is aligned to 8 bytes, it's a valid offset.
if (((uintptr_t)candidate & (sizeof(void*) - 1)) == 0 && candidate >= 0x400) {
if (((uintptr_t)candidate & (sizeof(void*) - 1)) == 0 && candidate >= 0x400 && candidate < 0x5000) {
try {
const auto ptr = (Application::Function*)((uintptr_t)this + candidate);

if (ptr == nullptr || IsBadReadPtr(ptr, sizeof(Application::Function) * 50)) {
spdlog::info("Skipping invalid Application::functions offset: {:x}", candidate);
continue;
}

if (ptr->description == nullptr || IsBadReadPtr(ptr->description, 32)) {
spdlog::info("Skipping invalid Application::functions offset: {:x}", candidate);
continue;
}
} catch (...) {
spdlog::info("Skipping invalid Application::functions offset: {:x}", candidate);
continue;
}

spdlog::info("Application::functions offset: {:x}", candidate);
return candidate;
}
Expand All @@ -64,7 +81,24 @@ Application::Function* Application::get_functions() {
const auto candidate = *(int32_t*)(*ref + 10);

// If the offset is aligned to 8 bytes, it's a valid offset.
if (((uintptr_t)candidate & (sizeof(void*) - 1)) == 0 && candidate >= 0x400) {
if (((uintptr_t)candidate & (sizeof(void*) - 1)) == 0 && candidate >= 0x400 && candidate < 0x5000) {
try {
const auto ptr = (Application::Function*)((uintptr_t)this + candidate);

if (ptr == nullptr || IsBadReadPtr(ptr, sizeof(Application::Function) * 50)) {
spdlog::info("Skipping invalid Application::functions offset: {:x}", candidate);
continue;
}

if (ptr->description == nullptr || IsBadReadPtr(ptr->description, 32)) {
spdlog::info("Skipping invalid Application::functions offset: {:x}", candidate);
continue;
}
} catch (...) {
spdlog::info("Skipping invalid Application::functions offset: {:x}", candidate);
continue;
}

spdlog::info("Application::functions offset: {:x}", candidate);
return candidate;
}
Expand Down
111 changes: 110 additions & 1 deletion shared/sdk/REContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ namespace sdk {
return;
}

sdk::RETypeDB* tdb = nullptr;

for (auto i = 0; i < 0x20000; i += sizeof(void*)) {
auto ptr = *(sdk::RETypeDB**)((uintptr_t)*potential_context + i);

Expand All @@ -173,6 +175,7 @@ namespace sdk {
}

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

s_tdb_version = version;
Expand Down Expand Up @@ -286,6 +289,93 @@ namespace sdk {
48 8B CF mov rcx, rdi
*/

auto alternative_invoke_scan = [&]() -> bool {
auto tdb_references = utility::scan_displacement_references(mod, (uintptr_t)tdb);

if (tdb_references.empty()) {
spdlog::info("[VM::update_pointers] Unable to find TDB references.");
return false;
}

for (const auto& ref : tdb_references) {
const auto fn_start = utility::find_function_start(ref);

if (!fn_start) {
continue;
}

spdlog::info("[VM::update_pointers] Disassembling function at {:x}", *fn_start);

bool found = false;

utility::exhaustive_decode((uint8_t*)*fn_start, 1000, [&](utility::ExhaustionContext& ctx) -> utility::ExhaustionResult {
if (found) {
return utility::ExhaustionResult::BREAK;
}

if (std::string_view{ctx.instrux.Mnemonic} == "CALL") {
return utility::ExhaustionResult::STEP_OVER;
}

if (std::string_view{ctx.instrux.Mnemonic} != "LEA") {
return utility::ExhaustionResult::CONTINUE;
}

const auto disp = utility::resolve_displacement(ctx.addr);

if (!disp) {
return utility::ExhaustionResult::CONTINUE;
}

try {
uintptr_t* functions = (uintptr_t*)*disp;

// First pointer must always be null
if (functions[0] != 0) {
return utility::ExhaustionResult::CONTINUE;
}

// Rest of pointers are not null and point somewhere within the game module
for (auto i = 1; i < 100; ++i) {
if (functions[i] == 0 || IsBadReadPtr(&functions[i], sizeof(void*))) {
return utility::ExhaustionResult::CONTINUE;
}

if (utility::get_module_within(functions[i]).value_or(nullptr) != mod) {
return utility::ExhaustionResult::CONTINUE;
}

/*const auto ptr_fn_start = utility::find_function_start(functions[i]);
if (!ptr_fn_start) {
return utility::ExhaustionResult::CONTINUE;
}
if (*ptr_fn_start != functions[i]) {
break;
}*/
}

s_invoke_tbl = (sdk::InvokeMethod*)functions;
found = true;

spdlog::info("[VM::update_pointers] s_invoke_tbl: {:x}", (uintptr_t)s_invoke_tbl);

return utility::ExhaustionResult::BREAK;
} catch (...) {
return utility::ExhaustionResult::CONTINUE;
}

return utility::ExhaustionResult::CONTINUE;
});

if (found) {
return true;
}
}

return false;
};

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

Expand All @@ -297,19 +387,32 @@ namespace sdk {
break;
}
}

if (!method_inside_invoke_tbl) {
spdlog::info("[VM::update_pointers] Unable to find method inside invoke table. Trying fallback scan...");
const auto anchor = utility::scan(mod, "8D 56 FF 48 8B CF E8 ? ? ? ?");

if (!anchor) {
spdlog::info("[VM::update_pointers] Unable to find anchor for invoke table.");
spdlog::info("[VM::update_pointers] Unable to find anchor for invoke table, trying alternative scan...");

if (!alternative_invoke_scan()) {
spdlog::info("[VM::update_pointers] Unable to find invoke table.");
return;
}

return;
}

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

if (!lea_rdx) {
spdlog::info("[VM::update_pointers] Unable to find lea rdx for invoke table.");

if (!alternative_invoke_scan()) {
spdlog::info("[VM::update_pointers] Unable to find invoke table.");
return;
}

return;
}

Expand All @@ -326,6 +429,12 @@ namespace sdk {

if (!ptr_inside_invoke_tbl) {
spdlog::info("[VM::update_pointers] Unable to find ptr inside invoke table.");

if (!alternative_invoke_scan()) {
spdlog::info("[VM::update_pointers] Unable to find invoke table.");
return;
}

return;
}

Expand Down
53 changes: 53 additions & 0 deletions shared/sdk/RETypeDB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -457,13 +457,62 @@ void* REMethodDefinition::get_function() const {
// The function this function uses to offset from has a predictable pattern as well.
// So the pattern for that can be searched for if this one breaks.
// It remains to be seen if this "encoding" is used in games other than MHRise.
static uintptr_t encoded_function_base = [&]() -> uintptr_t {
const auto game = utility::get_executable();
const auto invoke_table = sdk::VM::get_invoke_table();

if (invoke_table == nullptr) {
spdlog::error("[REMethodDefinition] Failed to find invoke table, cannot find encoded_function_base");
return 0;
}

const auto first_fn = invoke_table[1];

if (first_fn == nullptr) {
spdlog::error("[REMethodDefinition] Failed to find first function in invoke table, cannot find encoded_function_base");
return 0;
}

uintptr_t result = 0;

utility::exhaustive_decode((uint8_t*)first_fn, 100, [&](utility::ExhaustionContext& ctx) -> utility::ExhaustionResult {
if (result != 0) {
return utility::ExhaustionResult::BREAK;
}

if (std::string_view{ctx.instrux.Mnemonic} != "LEA") {
return utility::ExhaustionResult::CONTINUE;
}

const auto disp = utility::resolve_displacement(ctx.addr);

if (!disp) {
return utility::ExhaustionResult::CONTINUE;
}

if (utility::get_module_within(*disp).value_or(nullptr) != game) {
return utility::ExhaustionResult::CONTINUE;
}

result = *disp;
return utility::ExhaustionResult::BREAK;
});

spdlog::info("[REMethodDefinition] Found encoded_function_base at {:x}", result);

return result;
}();

static void* (*get_encoded_pointer)(int32_t offset) = []() {
spdlog::info("[REMethodDefinition] Finding get_encoded_pointer");

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

// Alternative scan where we find the first LEA instruction that loads a pointer to a function basically
// inside the invoke table.
if (!fn) {
spdlog::error("[REMethodDefinition] Failed to find get_encoded_pointer");

return (void* (*)(int32_t))nullptr;
}

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

if (get_encoded_pointer == nullptr) {
if (encoded_function_base != 0) {
return (void*)(encoded_function_base + this->encoded_offset);
}

return nullptr;
}

Expand Down
4 changes: 4 additions & 0 deletions shared/sdk/ResourceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ void ResourceManager::update_pointers() {
return;
}

spdlog::info("[ResourceManager::create_resource] Found string reference at {:x}", *string_reference);

// use HDE to disasm *string_reference - 3 and disasm forward a bit
// to find a call instruction which is the function we want
auto ip = *string_reference - 3;
Expand All @@ -126,8 +128,10 @@ void ResourceManager::update_pointers() {
// now find create_userdata, using the previous function as a reference to ignore
// since they both have the same pattern at the start of the function
const auto valid_patterns = {
#if TDB_VER < 73
"66 83 F8 40 75 ? C6",
"66 83 F8 40 75 ? 48",
#endif
"66 41 83 39 40" // DD2+
};

Expand Down
25 changes: 19 additions & 6 deletions src/mods/Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -553,13 +553,18 @@ std::optional<std::string> Hooks::hook_view_get_size() {
spdlog::info("via.SceneView.get_Size: {:x}", (uintptr_t)get_size_func);

// Pattern scan for the native function call
auto ref = utility::scan((uintptr_t)get_size_func, 0x100, "49 8B C8 E8");
//auto ref = utility::scan((uintptr_t)get_size_func, 0x100, "49 8B C8 E8");
auto ref = utility::find_pattern_in_path((uint8_t*)get_size_func, 1000, false, "49 8B C8 E8");

if (!ref) {
ref = utility::find_pattern_in_path((uint8_t*)get_size_func, 1000, false, "48 8B CB E8");
}

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

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

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

// Pattern scan for the native function call
auto ref = utility::scan((uintptr_t)func, 0x100, "49 8B C8 E8");
auto ref = utility::find_pattern_in_path((uint8_t*)func, 1000, false, "49 8B C8 E8");

if (!ref) {
ref = utility::find_pattern_in_path((uint8_t*)func, 1000, false, "48 8B CB E8");
}

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

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

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

// Pattern scan for the native function call
auto ref = utility::scan((uintptr_t)func, 0x100, "49 8B C8 E8");
auto ref = utility::find_pattern_in_path((uint8_t*)func, 1000, false, "49 8B C8 E8");

if (!ref) {
ref = utility::find_pattern_in_path((uint8_t*)func, 1000, false, "48 8B CB E8");
}

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

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

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

0 comments on commit d28c51f

Please sign in to comment.