Files
MetadataDumper/main.cpp
Failzuma 6647ce2800 init
2026-01-22 10:48:34 +07:00

166 lines
6.0 KiB
C++

#include <windows.h>
#include <iostream>
#include <vector>
#include <fstream>
#include <thread>
#include <string>
#include <span>
#include <shlobj.h>
#pragma comment(lib, "shell32.lib")
void CreateConsole() {
AllocConsole();
FILE* f;
freopen_s(&f, "CONOUT$", "w", stdout);
freopen_s(&f, "CONOUT$", "w", stderr);
SetConsoleTitleW(L"Endfield Runtime Metadata Dumper");
}
std::vector<int> ParsePattern(std::string_view combo) {
std::vector<int> pattern;
for (size_t i = 0; i < combo.size(); ++i) {
if (combo[i] == ' ') continue;
if (combo[i] == '?') {
pattern.push_back(-1);
if (i + 1 < combo.size() && combo[i + 1] == '?') i++;
}
else {
char buffer[3] = { combo[i], combo[i + 1], 0 };
pattern.push_back(std::strtoul(buffer, nullptr, 16));
i++;
}
}
return pattern;
}
uintptr_t ScanPattern(uintptr_t base, size_t size, std::string_view signature) {
auto pattern = ParsePattern(signature);
uint8_t* pData = reinterpret_cast<uint8_t*>(base);
for (size_t i = 0; i < size - pattern.size(); ++i) {
bool found = true;
for (size_t j = 0; j < pattern.size(); ++j) {
if (pattern[j] != -1 && pData[i + j] != pattern[j]) {
found = false;
break;
}
}
if (found) return base + i;
}
return 0;
}
uintptr_t ResolveRip(uintptr_t instructionAddr, int instructionLen, int offsetToRead) {
int32_t relativeOffset = *reinterpret_cast<int32_t*>(instructionAddr + offsetToRead);
return instructionAddr + instructionLen + relativeOffset;
}
void DumpThread(HMODULE hModule) {
CreateConsole();
std::cout << "\n[+] Initializing Dumper..." << std::endl;
uintptr_t modBase = 0;
while (!modBase) {
modBase = reinterpret_cast<uintptr_t>(GetModuleHandleW(L"GameAssembly.dll"));
if (!modBase) std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
std::cout << "[+] GameAssembly: 0x" << std::hex << modBase << std::endl;
constexpr std::string_view sig = "48 89 05 ? ? ? ? 48 85 C0 0F 84 ? ? ? ? 4C 89 05 ? ? ? ? 48 63 88";
uintptr_t sigAddr = ScanPattern(modBase, 0x8000000, sig);
if (!sigAddr) {
std::cout << "[-] Pattern not found." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
FreeConsole(); FreeLibraryAndExitThread(hModule, 0);
return;
}
std::cout << "[+] Signature: 0x" << std::hex << sigAddr << std::endl;
uintptr_t globalMetadataVar = ResolveRip(sigAddr, 7, 3);
std::cout << "[+] Global Var: 0x" << std::hex << globalMetadataVar << std::endl;
uintptr_t metadataPtr = 0;
std::cout << "[*] Waiting for pointer..." << std::endl;
for (int i = 0; i < 200; i++) {
metadataPtr = *reinterpret_cast<uintptr_t*>(globalMetadataVar);
if (metadataPtr != 0) break;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
if (!metadataPtr) {
std::cout << "[-] Timed out waiting for pointer." << std::endl;
}
else {
std::cout << "[+] Pointer: 0x" << std::hex << metadataPtr << std::endl;
uint32_t magic = *reinterpret_cast<uint32_t*>(metadataPtr);
std::cout << "[*] Header Magic: 0x" << std::hex << magic << std::endl;
if (magic != 0xFAB11BAF) {
std::cout << "[-] Magic Bytes mismatch! Expected 0xFAB11BAF." << std::endl;
std::cout << "[-] Aborting dump." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(3));
FreeConsole();
FreeLibraryAndExitThread(hModule, 0);
return;
}
std::cout << "[+] Magic Bytes verified!" << std::endl;
MEMORY_BASIC_INFORMATION mbi{};
if (VirtualQuery(reinterpret_cast<LPCVOID>(metadataPtr), &mbi, sizeof(mbi))) {
std::cout << "[+] Region Size: " << std::dec << mbi.RegionSize << " bytes" << std::endl;
DWORD oldProtect;
if (VirtualProtect(reinterpret_cast<LPVOID>(metadataPtr), mbi.RegionSize, PAGE_EXECUTE_READWRITE, &oldProtect)) {
std::cout << "[+] Permissions set to RWX." << std::endl;
}
else {
std::cout << "[!] VirtualProtect failed. Dumping might fail." << std::endl;
}
std::wstring fullPath = L"global-metadata-dump.dat";
FILE* f = nullptr;
_wfopen_s(&f, fullPath.c_str(), L"wb");
if (f) {
size_t totalWritten = 0;
size_t chunkSize = 4096;
uint8_t* pData = reinterpret_cast<uint8_t*>(metadataPtr);
for (size_t i = 0; i < mbi.RegionSize; i += chunkSize) {
size_t toWrite = (mbi.RegionSize - i < chunkSize) ? (mbi.RegionSize - i) : chunkSize;
size_t w = fwrite(pData + i, 1, toWrite, f);
totalWritten += w;
if (w != toWrite) break;
}
fclose(f);
if (totalWritten > 0) {
std::wcout << L"[SUCCESS] Dumped " << totalWritten << L" bytes to: " << fullPath << std::endl;
}
else {
std::cout << "[-] Write failed completely. Zero bytes written." << std::endl;
}
}
else {
std::cout << "[-] Failed to open file. Error: " << errno << std::endl;
}
VirtualProtect(reinterpret_cast<LPVOID>(metadataPtr), mbi.RegionSize, oldProtect, &oldProtect);
}
}
std::cout << "Unloading in 5s..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
FreeConsole();
FreeLibraryAndExitThread(hModule, 0);
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
DisableThreadLibraryCalls(hModule);
CloseHandle(CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)DumpThread, hModule, 0, nullptr));
}
return TRUE;
}