Dump interfaces

This commit is contained in:
a2x
2023-09-09 23:51:17 +10:00
parent 28e23da953
commit 2e7cc79b06
7 changed files with 1079 additions and 13 deletions

View File

@@ -20,6 +20,12 @@ static const std::array<std::pair<std::string_view, std::unique_ptr<builder::IFi
}
};
std::string sanitize_module_name(const std::string& name) {
static std::regex double_colon_pattern("\\.");
return std::regex_replace(name, double_colon_pattern, "_");
}
template <class IFileBuilder>
void generate_file(const std::string_view file_name, const Entries& entries, IFileBuilder& builder) {
const std::string output_file_path = std::format("generated/{}.{}", file_name, builder.get_extension());
@@ -32,18 +38,18 @@ void generate_file(const std::string_view file_name, const Entries& entries, IFi
return;
}
builder.write_top_level(output);
const auto sanitize_namespace_name = [](const std::string& namespace_name) -> std::string {
static std::regex double_colon_pattern("\\::");
const auto sanitize_namespace = [](const std::string& namespace_name) -> std::string {
static std::regex namespace_regex("\\::");
return std::regex_replace(namespace_name, namespace_regex, "_");
return std::regex_replace(namespace_name, double_colon_pattern, "_");
};
builder.write_top_level(output);
for (auto it = entries.begin(); it != entries.end(); ++it) {
const auto& [namespace_name, variables] = *it;
const std::string sanitized_namespace = sanitize_namespace(namespace_name);
const std::string sanitized_namespace = sanitize_namespace_name(namespace_name);
builder.write_namespace(output, sanitized_namespace);
@@ -148,7 +154,59 @@ std::optional<std::uint64_t> get_view_matrix() noexcept {
return process::resolve_rip_relative_address(address.value()).value_or(0);
}
void fetch_offsets() noexcept {
void dump_interfaces() noexcept {
const std::optional<std::vector<std::string>> loaded_modules = process::get_loaded_modules();
if (!loaded_modules.has_value()) {
spdlog::error("failed to get loaded modules.");
return;
}
spdlog::info("generating interface files...");
Entries entries;
for (const auto& module_name : loaded_modules.value()) {
const std::optional<std::uint64_t> module_base = process::get_module_base(module_name);
if (!module_base.has_value())
continue;
const std::optional<std::uint64_t> create_interface_address = process::get_export(module_base.value(), "CreateInterface");
if (!create_interface_address.has_value())
continue;
std::optional<std::uint64_t> interface_registry = process::resolve_rip_relative_address(create_interface_address.value());
if (!interface_registry.has_value())
continue;
interface_registry = process::read_memory<std::uint64_t>(interface_registry.value());
if (!interface_registry.has_value())
continue;
while (interface_registry.value() != 0) {
const auto interface_ptr = process::read_memory<std::uint64_t>(interface_registry.value());
const std::string interface_version_name = process::read_string(process::read_memory<std::uint64_t>(interface_registry.value() + 0x8), 64);
entries[sanitize_module_name(module_name)].emplace_back(interface_version_name, interface_ptr - module_base.value());
interface_registry = process::read_memory<std::uint64_t>(interface_registry.value() + 0x10);
}
}
for (const auto& [extension, builder] : builders) {
generate_file("interfaces", entries, *builder);
spdlog::info(" > generated {}.{}!", "interfaces", extension);
}
}
void dump_offsets() noexcept {
const std::optional<std::uint64_t> client_base = process::get_module_base("client.dll");
if (!client_base.has_value()) {
@@ -167,11 +225,12 @@ void fetch_offsets() noexcept {
const std::uint64_t view_angles_rva = get_client_rva(get_view_angles().value_or(0));
const std::uint64_t view_matrix_rva = get_client_rva(get_view_matrix().value_or(0));
spdlog::info("entity list: {:#x}", entity_list_rva);
spdlog::info("global vars: {:#x}", global_vars_rva);
spdlog::info("local player controller: {:#x}", local_player_controller_rva);
spdlog::info("view angles: {:#x}", view_angles_rva);
spdlog::info("view matrix: {:#x}", view_matrix_rva);
spdlog::info("found offsets!");
spdlog::info(" > entity list: {:#x}", entity_list_rva);
spdlog::info(" > global vars: {:#x}", global_vars_rva);
spdlog::info(" > local player controller: {:#x}", local_player_controller_rva);
spdlog::info(" > view angles: {:#x}", view_angles_rva);
spdlog::info(" > view matrix: {:#x}", view_matrix_rva);
const Entries entries = {
{ "client_dll", {
@@ -217,7 +276,9 @@ int main() {
for (const sdk::CSchemaSystemTypeScope* type_scope : schema_system->get_type_scopes())
generate_files_for_type_scope(type_scope);
fetch_offsets();
dump_interfaces();
dump_offsets();
spdlog::info("finished!");

View File

@@ -118,6 +118,90 @@ namespace process {
return std::nullopt;
}
std::optional<std::uintptr_t> get_export(const std::uintptr_t module_base, const std::string_view function_name) noexcept {
const auto buffer = std::make_unique<std::uint8_t[]>(0x1000);
if (!read_memory(module_base, buffer.get(), 0x1000))
return std::nullopt;
const auto dos_header = reinterpret_cast<PIMAGE_DOS_HEADER>(buffer.get());
if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
return std::nullopt;
const auto nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(buffer.get() + dos_header->e_lfanew);
if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
return std::nullopt;
const IMAGE_DATA_DIRECTORY export_data_directory = nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
if (export_data_directory.VirtualAddress == 0 || export_data_directory.Size == 0)
return std::nullopt;
const auto export_directory_buffer = std::make_unique<std::uint8_t[]>(export_data_directory.Size);
if (!read_memory(module_base + export_data_directory.VirtualAddress, export_directory_buffer.get(), export_data_directory.Size))
return std::nullopt;
const auto export_directory = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(export_directory_buffer.get());
const std::uintptr_t delta = reinterpret_cast<std::uintptr_t>(export_directory) - export_data_directory.VirtualAddress;
const auto name_table = reinterpret_cast<std::uint32_t*>(export_directory->AddressOfNames + delta);
const auto name_ordinal_table = reinterpret_cast<std::uint16_t*>(export_directory->AddressOfNameOrdinals + delta);
const auto function_table = reinterpret_cast<std::uint32_t*>(export_directory->AddressOfFunctions + delta);
for (std::size_t i = 0; i < export_directory->NumberOfNames; ++i) {
const std::string_view current_function_name = reinterpret_cast<const char*>(name_table[i] + delta);
if (current_function_name != function_name)
continue;
const std::uint16_t function_ordinal = name_ordinal_table[i];
const std::uintptr_t function_address = module_base + function_table[function_ordinal];
if (function_address >= module_base + export_data_directory.VirtualAddress &&
function_address <= module_base + export_data_directory.VirtualAddress + export_data_directory.Size
) {
// TODO: Handle forwarded exports.
return std::nullopt;
}
return function_address;
}
return std::nullopt;
}
std::optional<std::uintptr_t> get_export(const std::string_view module_name, const std::string_view function_name) noexcept {
const std::optional<std::uintptr_t> module_base = get_module_base(module_name);
if (!module_base.has_value())
return std::nullopt;
return get_export(module_base.value(), function_name);
}
std::optional<std::vector<std::string>> get_loaded_modules() noexcept {
const base::SafeHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, process_id));
if (snapshot.get() == INVALID_HANDLE_VALUE)
return std::nullopt;
MODULEENTRY32 module_entry = {};
module_entry.dwSize = sizeof(MODULEENTRY32);
std::vector<std::string> loaded_modules;
for (Module32First(snapshot.get(), &module_entry); Module32Next(snapshot.get(), &module_entry);)
loaded_modules.emplace_back(module_entry.szModule);
return loaded_modules;
}
std::optional<std::uintptr_t> get_module_base(const std::string_view module_name) noexcept {
const base::SafeHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32, process_id));