Scan executable for GPS route strings
This commit is contained in:
@@ -8,7 +8,10 @@
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
@@ -87,6 +90,7 @@ std::mutex gLogMutex;
|
||||
RED4ext::v1::PluginHandle gHandle = nullptr;
|
||||
const RED4ext::v1::Sdk* gSdk = nullptr;
|
||||
uint32_t gUpdateLogCount = 0;
|
||||
bool gScannedExecutable = false;
|
||||
|
||||
std::filesystem::path GetLogPath()
|
||||
{
|
||||
@@ -142,11 +146,156 @@ const char* ReasonToString(RED4ext::v1::EMainReason aReason)
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
bool IsReadablePage(DWORD aProtect)
|
||||
{
|
||||
if ((aProtect & PAGE_GUARD) != 0 || (aProtect & PAGE_NOACCESS) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto baseProtect = aProtect & 0xFF;
|
||||
return baseProtect == PAGE_READONLY || baseProtect == PAGE_READWRITE || baseProtect == PAGE_WRITECOPY ||
|
||||
baseProtect == PAGE_EXECUTE_READ || baseProtect == PAGE_EXECUTE_READWRITE ||
|
||||
baseProtect == PAGE_EXECUTE_WRITECOPY;
|
||||
}
|
||||
|
||||
void ScanRegionForString(const uint8_t* aRegionBegin, size_t aRegionSize, uintptr_t aImageBase, const char* aNeedle,
|
||||
uint32_t& aMatches)
|
||||
{
|
||||
constexpr uint32_t kMaxMatchesPerNeedle = 8;
|
||||
|
||||
const auto needleSize = std::strlen(aNeedle);
|
||||
if (needleSize == 0 || aRegionSize < needleSize || aMatches >= kMaxMatchesPerNeedle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* cursor = aRegionBegin;
|
||||
const auto* regionEnd = aRegionBegin + aRegionSize;
|
||||
|
||||
while (cursor + needleSize <= regionEnd && aMatches < kMaxMatchesPerNeedle)
|
||||
{
|
||||
if (std::memcmp(cursor, aNeedle, needleSize) == 0)
|
||||
{
|
||||
const auto rva = reinterpret_cast<uintptr_t>(cursor) - aImageBase;
|
||||
std::ostringstream line;
|
||||
line << "exe-string match needle=\"" << aNeedle << "\" rva=0x" << std::hex << rva << std::dec;
|
||||
LogRed4ext(line.str());
|
||||
++aMatches;
|
||||
cursor += needleSize;
|
||||
continue;
|
||||
}
|
||||
|
||||
++cursor;
|
||||
}
|
||||
}
|
||||
|
||||
void ScanExecutableStrings()
|
||||
{
|
||||
if (gScannedExecutable)
|
||||
{
|
||||
return;
|
||||
}
|
||||
gScannedExecutable = true;
|
||||
|
||||
const auto imageBase = reinterpret_cast<uintptr_t>(GetModuleHandleW(nullptr));
|
||||
if (!imageBase)
|
||||
{
|
||||
LogRed4ext("exe-string scan skipped: main module unavailable");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* dos = reinterpret_cast<const IMAGE_DOS_HEADER*>(imageBase);
|
||||
if (dos->e_magic != IMAGE_DOS_SIGNATURE)
|
||||
{
|
||||
LogRed4ext("exe-string scan skipped: bad DOS header");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto* nt = reinterpret_cast<const IMAGE_NT_HEADERS*>(imageBase + dos->e_lfanew);
|
||||
if (nt->Signature != IMAGE_NT_SIGNATURE)
|
||||
{
|
||||
LogRed4ext("exe-string scan skipped: bad NT header");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto imageSize = static_cast<size_t>(nt->OptionalHeader.SizeOfImage);
|
||||
std::ostringstream header;
|
||||
header << "exe-string scan begin base=0x" << std::hex << imageBase << " size=0x" << imageSize << std::dec;
|
||||
LogRed4ext(header.str());
|
||||
|
||||
constexpr std::array<const char*, 18> kNeedles{
|
||||
"GPSSystem/Tick",
|
||||
"GPSSystem",
|
||||
"GPSSettings",
|
||||
"OnSetHasGPSPortal",
|
||||
"IsGPSPortal",
|
||||
"m_cookedGpsData",
|
||||
"gameCookedGpsMappinData",
|
||||
"GetNavigationFunctionalTests",
|
||||
"TrafficSystem_Pathfinding",
|
||||
"TrafficSystem_PathMap",
|
||||
"Pathfinding Algorithm Failed",
|
||||
"No Path Found in Traffic",
|
||||
"PathFindingFailed",
|
||||
"StartPathfinding",
|
||||
"StopPathfinding",
|
||||
"worldTrafficLanePlayerGPSInfo",
|
||||
"worldTrafficPersistentResource",
|
||||
"worldTrafficConnectivityOutLane",
|
||||
};
|
||||
|
||||
for (const auto* needle : kNeedles)
|
||||
{
|
||||
uint32_t matches = 0;
|
||||
uintptr_t cursor = imageBase;
|
||||
const uintptr_t end = imageBase + imageSize;
|
||||
|
||||
while (cursor < end)
|
||||
{
|
||||
MEMORY_BASIC_INFORMATION mbi{};
|
||||
if (!VirtualQuery(reinterpret_cast<const void*>(cursor), &mbi, sizeof(mbi)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const auto regionBegin = reinterpret_cast<uintptr_t>(mbi.BaseAddress);
|
||||
const auto regionEnd = regionBegin + mbi.RegionSize;
|
||||
if (mbi.State == MEM_COMMIT && mbi.Type == MEM_IMAGE && IsReadablePage(mbi.Protect))
|
||||
{
|
||||
const auto scanBegin = std::max(regionBegin, imageBase);
|
||||
const auto scanEnd = std::min(regionEnd, end);
|
||||
if (scanBegin < scanEnd)
|
||||
{
|
||||
ScanRegionForString(reinterpret_cast<const uint8_t*>(scanBegin), scanEnd - scanBegin, imageBase,
|
||||
needle, matches);
|
||||
}
|
||||
}
|
||||
|
||||
if (regionEnd <= cursor)
|
||||
{
|
||||
break;
|
||||
}
|
||||
cursor = regionEnd;
|
||||
}
|
||||
|
||||
if (matches == 0)
|
||||
{
|
||||
std::ostringstream line;
|
||||
line << "exe-string no match needle=\"" << needle << "\"";
|
||||
LogRed4ext(line.str());
|
||||
}
|
||||
}
|
||||
|
||||
LogRed4ext("exe-string scan end");
|
||||
}
|
||||
|
||||
bool OnRunningEnter(void* aApp)
|
||||
{
|
||||
std::ostringstream line;
|
||||
line << "GameState Running OnEnter app=" << aApp;
|
||||
LogRed4ext(line.str());
|
||||
ScanExecutableStrings();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user