Implement pattern scanning for mask type patches

This commit is contained in:
CrazyBloo 2024-08-24 07:11:12 -04:00
parent 7564f195c3
commit 5377ae9c37
4 changed files with 113 additions and 15 deletions

View file

@ -9,6 +9,7 @@
namespace MemoryPatcher {
uintptr_t g_eboot_address;
u64 g_eboot_image_size;
std::vector<patchInfo> pending_patches;
@ -18,25 +19,42 @@ void AddPatchToQueue(patchInfo patchToAdd) {
void ApplyPendingPatches() {
//TODO: need to verify that the patch is actually for the game we open,
//if we enable patches but open a different game they will still attempt to load
for (size_t i = 0; i < pending_patches.size(); ++i) {
patchInfo currentPatch = pending_patches[i];
PatchMemory(currentPatch.modNameStr, currentPatch.offsetStr, currentPatch.valueStr,
currentPatch.isOffset, currentPatch.littleEndian);
currentPatch.isOffset, currentPatch.littleEndian, currentPatch.patchMask,
currentPatch.maskOffset);
}
pending_patches.clear();
}
void PatchMemory(std::string modNameStr, std::string offsetStr, std::string valueStr, bool isOffset,
bool littleEndian) {
bool littleEndian, PatchMask patchMask, int maskOffset) {
// Send a request to modify the process memory.
void* cheatAddress = nullptr;
if (isOffset) {
cheatAddress = reinterpret_cast<void*>(g_eboot_address + std::stoi(offsetStr, 0, 16));
} else {
cheatAddress =
reinterpret_cast<void*>(g_eboot_address + (std::stoi(offsetStr, 0, 16) - 0x400000));
if (patchMask == PatchMask::None) {
if (isOffset) {
cheatAddress = reinterpret_cast<void*>(g_eboot_address + std::stoi(offsetStr, 0, 16));
} else {
cheatAddress =
reinterpret_cast<void*>(g_eboot_address + (std::stoi(offsetStr, 0, 16) - 0x400000));
}
}
if (patchMask == PatchMask::Mask) {
cheatAddress = reinterpret_cast<void*>(PatternScan(offsetStr) + maskOffset);
}
//TODO: implement mask_jump32
if (cheatAddress == nullptr) {
LOG_ERROR(Loader, "Failed to get address for patch {}", modNameStr);
return;
}
std::vector<unsigned char> bytePatch;
@ -58,4 +76,49 @@ void PatchMemory(std::string modNameStr, std::string offsetStr, std::string valu
valueStr);
}
static std::vector<int32_t> PatternToByte(const std::string& pattern) {
std::vector<int32_t> bytes;
const char* start = pattern.data();
const char* end = start + pattern.size();
for (const char* current = start; current < end; ++current) {
if (*current == '?') {
++current;
if (*current == '?')
++current;
bytes.push_back(-1);
} else {
bytes.push_back(strtoul(current, const_cast<char**>(&current), 16));
}
}
return bytes;
}
uintptr_t PatternScan(const std::string& signature) {
std::vector<int32_t> patternBytes = PatternToByte(signature);
const auto scanBytes = static_cast<uint8_t*>((void*)g_eboot_address);
const int32_t* sigPtr = patternBytes.data();
const size_t sigSize = patternBytes.size();
uint32_t foundResults = 0;
for (uint32_t i = 0; i < g_eboot_image_size - sigSize; ++i) {
bool found = true;
for (uint32_t j = 0; j < sigSize; ++j) {
if (scanBytes[i + j] != sigPtr[j] && sigPtr[j] != -1) {
found = false;
break;
}
}
if (found) {
foundResults++;
return reinterpret_cast<uintptr_t>(&scanBytes[i]);
}
}
return 0;
}
} // namespace MemoryPatcher

View file

@ -9,6 +9,13 @@
namespace MemoryPatcher {
extern uintptr_t g_eboot_address;
extern u64 g_eboot_image_size;
enum PatchMask : uint8_t {
None,
Mask,
Mask_Jump32,
};
struct patchInfo {
std::string modNameStr;
@ -16,6 +23,8 @@ struct patchInfo {
std::string valueStr;
bool isOffset;
bool littleEndian;
PatchMask patchMask;
int maskOffset;
};
extern std::vector<patchInfo> pending_patches;
@ -24,6 +33,9 @@ void AddPatchToQueue(patchInfo patchToAdd);
void ApplyPendingPatches();
void PatchMemory(std::string modNameStr, std::string offsetStr, std::string valueStr, bool isOffset,
bool littleEndian);
bool littleEndian, PatchMask patchMask = PatchMask::None, int maskOffset = 0);
static std::vector<int32_t> PatternToByte(const std::string& pattern);
uintptr_t PatternScan(const std::string& signature);
} // namespace MemoryPatcher

View file

@ -197,6 +197,7 @@ void Module::LoadModuleToMemory(u32& max_tls_index) {
if (MemoryPatcher::g_eboot_address == 0) {
if (name == "eboot") {
MemoryPatcher::g_eboot_address = base_virtual_addr;
MemoryPatcher::g_eboot_image_size = base_size;
MemoryPatcher::ApplyPendingPatches();
}
}

View file

@ -748,9 +748,14 @@ void CheatsPatches::loadPatches(const QString& serial) {
QXmlStreamAttributes attributes = xmlReader.attributes();
QString appVer = attributes.value("AppVer").toString();
if (appVer == m_gameVersion) {
patchName = attributes.value("Name").toString();
patchAuthor = attributes.value("Author").toString();
patchNote = attributes.value("Note").toString();
patchName = attributes.value("Name").toString();
patchAuthor = attributes.value("Author").toString();
patchNote = attributes.value("Note").toString();
}
if (appVer == "mask") {
patchName = attributes.value("Name").toString();
patchAuthor = attributes.value("Author").toString();
patchNote = attributes.value("Note").toString();
}
} else if (xmlReader.name() == QStringLiteral("PatchList")) {
QJsonArray linesArray;
@ -873,19 +878,34 @@ void CheatsPatches::applyPatch(const QString& patchName, bool enabled) {
QString type = lineObject["Type"].toString();
QString address = lineObject["Address"].toString();
QString patchValue = lineObject["Value"].toString();
QString maskOffsetStr = lineObject["Offset"].toString();
patchValue = convertValueToHex(type, patchValue);
bool littleEndian = false;
if (type.toStdString() == "bytes16") {
if (type == "bytes16") {
littleEndian = true;
} else if (type.toStdString() == "bytes32") {
} else if (type == "bytes32") {
littleEndian = true;
} else if (type.toStdString() == "bytes64") {
} else if (type == "bytes64") {
littleEndian = true;
}
MemoryPatcher::PatchMask patchMask = MemoryPatcher::PatchMask::None;
int maskOffsetValue = 0;
if (type == "mask") {
patchMask = MemoryPatcher::PatchMask::Mask;
//im not sure if this works, there is no games to test the mask offset on yet
if (!maskOffsetStr.toStdString().empty())
maskOffsetValue = std::stoi(maskOffsetStr.toStdString(), 0, 10);
}
if (type == "mask_jump32")
patchMask = MemoryPatcher::PatchMask::Mask_Jump32;
if (MemoryPatcher::g_eboot_address == 0) {
MemoryPatcher::patchInfo addingPatch;
addingPatch.modNameStr = patchName.toStdString();
@ -893,13 +913,15 @@ void CheatsPatches::applyPatch(const QString& patchName, bool enabled) {
addingPatch.valueStr = patchValue.toStdString();
addingPatch.isOffset = false;
addingPatch.littleEndian = littleEndian;
addingPatch.patchMask = patchMask;
addingPatch.maskOffset = maskOffsetValue;
MemoryPatcher::AddPatchToQueue(addingPatch);
continue;
}
MemoryPatcher::PatchMemory(patchName.toStdString(), address.toStdString(),
patchValue.toStdString(), false, littleEndian);
patchValue.toStdString(), false, littleEndian, patchMask);
}
}
}