/* dbdata_probe.c -- call FIFA 17's dbdata.dll!getTableData and dump what it returns. * * FINDING (2026-08-04): dbdata.dll is NOT a player database. Its single export * `getTableData` is an ANTI-TAMPER ATTESTATION function whose name is a decoy. * Signature resolved empirically (crash-matrix over the 4 Win64 register args): * * const char * __cdecl getTableData(int *pOutBase64Len); * * It returns a heap-allocated, NUL-terminated base64url string: * dbdata.dll -> 1012 chars (759 bytes decoded) md5 59b46dce231e419f4c1effbd8024e5ae * dbdataEA.dll -> 1004 chars (753 bytes decoded) md5 8e10d1c4ce5ca54974aad42a10f05de0 * Deterministic across calls and across processes. There is no table selector * argument; args 2..4 are ignored. * * What it actually does (Wine +relay trace): * CommandLineToArgvW(GetCommandLineW()) * for each argv: StrStrW(argv[i], L"/antitamperdiagnosis") * GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, &getTableData) * GetModuleFileNameA -> CreateFileW(own .dll, GENERIC_READ) * ReadFile(0x28f000) <- whole file into a heap buffer * VirtualAlloc(1MB, PAGE_EXECUTE_READWRITE) <- unpack scratch, freed before return * SetFilePointerEx(0x28e200); ReadFile(0x200) <- Authenticode Security Directory * -> 759-byte attestation blob, base64url encoded * * Flip one byte inside the .xdata payload and it raises 0xC0000096 * (PRIVILEGED_INSTRUCTION) at image+0x3C0DF instead of returning. * * Build + run: * x86_64-w64-mingw32-gcc -O0 -o dbdata_probe.exe dbdata_probe.c * cp "/mnt/games/FIFA 17/dbdata.dll" . # must sit next to the exe * WINEDEBUG=-all wine dbdata_probe.exe dbdata.dll 1 out * Add WINEDEBUG=+relay to re-derive the call sequence above. */ #include #include #include #include typedef void *(*F4)(uint64_t, uint64_t, uint64_t, uint64_t); static LONG CALLBACK veh(EXCEPTION_POINTERS *ep) { fprintf(stderr, "!! EXCEPTION 0x%08lx rip=%p addr=%p\n", (unsigned long)ep->ExceptionRecord->ExceptionCode, (void *)ep->ContextRecord->Rip, ep->ExceptionRecord->ExceptionAddress); fflush(stderr); ExitProcess(9); return EXCEPTION_CONTINUE_SEARCH; } int main(int argc, char **argv) { AddVectoredExceptionHandler(1, veh); const char *dllname = (argc > 1) ? argv[1] : "dbdata.dll"; int iters = (argc > 2) ? atoi(argv[2]) : 1; const char *pfx = (argc > 3) ? argv[3] : "gtd"; HMODULE h = LoadLibraryA(dllname); if (!h) { printf("LoadLibraryA(%s) failed err=%lu\n", dllname, GetLastError()); return 1; } F4 f = (F4)GetProcAddress(h, "getTableData"); printf("base=%p getTableData=%p (rva 0x%llx)\n", (void *)h, (void *)f, (unsigned long long)((uintptr_t)f - (uintptr_t)h)); if (!f) return 1; for (int i = 0; i < iters; i++) { volatile int len = -1; void *r = f((uint64_t)(uintptr_t)&len, 0, 0, 0); printf("call %d: ret=%p len=%d\n", i, r, len); if (!r || len <= 0) continue; char fn[256]; sprintf(fn, "%s_%d.bin", pfx, i); FILE *fp = fopen(fn, "wb"); fwrite(r, 1, (size_t)len, fp); fclose(fp); printf(" wrote %s (%d bytes of base64url)\n", fn, len); } return 0; }