#include #include #include #include #include #include #include #include #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 ParsePattern(std::string_view combo) { std::vector 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(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(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(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(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(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(metadataPtr), &mbi, sizeof(mbi))) { std::cout << "[+] Region Size: " << std::dec << mbi.RegionSize << " bytes" << std::endl; DWORD oldProtect; if (VirtualProtect(reinterpret_cast(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(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(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; }