From e56a6228aa261e450b8fec699df1438bd7d52c03 Mon Sep 17 00:00:00 2001
From: REL <25654009+34736384@users.noreply.github.com>
Date: Tue, 7 Jan 2025 20:16:14 -0500
Subject: [PATCH] use cpp23 and 'modernize'
---
lib/YaeAchievementLib.vcxproj | 4 +-
lib/src/dllmain.cpp | 9 +--
lib/src/il2cpp-init.cpp | 92 ++++++++++++------------
lib/src/il2cpp-types.h | 9 +++
lib/src/util.cpp | 132 ++++++++++++++++++++--------------
lib/src/util.h | 7 +-
6 files changed, 144 insertions(+), 109 deletions(-)
diff --git a/lib/YaeAchievementLib.vcxproj b/lib/YaeAchievementLib.vcxproj
index c81ff62..6fbf0d6 100644
--- a/lib/YaeAchievementLib.vcxproj
+++ b/lib/YaeAchievementLib.vcxproj
@@ -60,7 +60,7 @@
true
_DEBUG;YAEACHIEVEMENTLIB_EXPORTS;_WINDOWS;_USRDLL;WIN32_LEAN_AND_MEAN;ZYDIS_STATIC_BUILD;ZYCORE_STATIC_BUILD;%(PreprocessorDefinitions)
false
- stdcpp20
+ stdcpplatest
true
@@ -78,7 +78,7 @@
true
_AMD64_;NDEBUG;YAEACHIEVEMENTLIB_EXPORTS;_WINDOWS;_USRDLL;WIN32_LEAN_AND_MEAN;ZYDIS_STATIC_BUILD;ZYCORE_STATIC_BUILD;%(PreprocessorDefinitions)
false
- stdcpp20
+ stdcpplatest
true
Speed
true
diff --git a/lib/src/dllmain.cpp b/lib/src/dllmain.cpp
index 3f1928c..a916390 100644
--- a/lib/src/dllmain.cpp
+++ b/lib/src/dllmain.cpp
@@ -1,5 +1,6 @@
// ReSharper disable CppClangTidyCertErr33C
#include
+#include
#include
#include
#include
@@ -49,11 +50,11 @@ namespace Hook {
const auto headLength = _byteswap_ushort(packet->HeaderLength);
const auto dataLength = _byteswap_ulong(packet->DataLength);
- printf("CmdId: %d\n", _byteswap_ushort(packet->CmdId));
- printf("DataLength: %d\n", dataLength);
+ std::println("CmdId: {}", _byteswap_ushort(packet->CmdId));
+ std::println("DataLength: {}", dataLength);
const auto base64 = Util::Base64Encode(packet->Data + headLength, dataLength) + "\n";
- printf("Base64: %s\n", base64.c_str());
+ std::println("Base64: {}", base64);
#ifdef _DEBUG
system("pause");
@@ -137,7 +138,7 @@ DWORD __stdcall ThreadProc(LPVOID hInstance)
if (MessagePipe == INVALID_HANDLE_VALUE)
{
#ifdef _DEBUG
- printf("CreateFile failed: %d\n", GetLastError());
+ std::println("CreateFile failed: {}", GetLastError());
#else
Util::Win32ErrorDialog(1001, GetLastError());
ExitProcess(0);
diff --git a/lib/src/il2cpp-init.cpp b/lib/src/il2cpp-init.cpp
index a14b0c6..65596eb 100644
--- a/lib/src/il2cpp-init.cpp
+++ b/lib/src/il2cpp-init.cpp
@@ -1,4 +1,5 @@
#include
+#include
#include
#include
#include
@@ -40,11 +41,11 @@ namespace
std::vector Operands;
};
- uintptr_t GetSection(LPCSTR name, size_t* sectionSize = nullptr)
+ std::span GetSection(LPCSTR name)
{
using namespace Globals;
if (BaseAddress == 0)
- return 0;
+ return {};
const auto dosHeader = (PIMAGE_DOS_HEADER)BaseAddress;
const auto ntHeader = (PIMAGE_NT_HEADERS)((uintptr_t)dosHeader + dosHeader->e_lfanew);
@@ -54,14 +55,13 @@ namespace
{
if (strcmp((char*)sectionHeader[i].Name, name) == 0)
{
- if (sectionSize != nullptr) {
- *sectionSize = sectionHeader[i].Misc.VirtualSize;
- }
- return BaseAddress + sectionHeader[i].VirtualAddress;
+ const auto sectionSize = sectionHeader[i].Misc.VirtualSize;
+ const auto virtualAddress = BaseAddress + sectionHeader[i].VirtualAddress;
+ return std::span(reinterpret_cast(virtualAddress), sectionSize);
}
}
- return 0;
+ return {};
}
///
@@ -148,7 +148,7 @@ namespace
for (const auto& instr : instructions)
{
if (instr.Instruction.mnemonic == ZYDIS_MNEMONIC_CALL) {
- uint32_t destination = instr.RVA + instr.Instruction.length + instr.Operands[0].imm.value.s;
+ uint32_t destination = instr.Operands[0].imm.value.s + instr.RVA + instr.Instruction.length;
calls.insert(destination);
}
}
@@ -165,52 +165,50 @@ namespace
void ResolveCmdId()
{
- size_t sectionSize;
- const auto sectionAddress = GetSection("il2cpp", §ionSize);
- const auto sectionEnd = sectionAddress + sectionSize;
+ const auto il2cppSection = GetSection("il2cpp");
- printf("Section Address: 0x%llX\n", sectionAddress);
- printf("Section End: 0x%llX\n", sectionEnd);
+ std::println("Section Address: 0x{:X}", reinterpret_cast(il2cppSection.data()));
+ std::println("Section End: 0x{:X}", reinterpret_cast(il2cppSection.data() + il2cppSection.size()));
- if (sectionAddress == 0)
+ if (il2cppSection.empty())
return; // message box?
- const auto candidates = Util::PatternScanAll(sectionAddress, sectionEnd, "56 48 83 EC 20 48 89 D0 48 89 CE 80 3D ? ? ? ? 00");
- printf("Candidates: %llu\n", candidates.size());
-
- std::vector> candidateInstructions;
- std::ranges::transform(candidates, std::back_inserter(candidateInstructions), DecodeFunction);
+ const auto candidates = Util::PatternScanAll(il2cppSection, "56 48 83 EC 20 48 89 D0 48 89 CE 80 3D ? ? ? ? 00");
+ std::println("Candidates: {}", candidates.size());
std::vector> filteredInstructions;
- std::ranges::copy_if(candidateInstructions, std::back_inserter(filteredInstructions), [](const std::vector& instr) {
- return GetDataReferenceCount(instr) == 5 && GetCallCount(instr) == 10 && GetUniqueCallCount(instr) == 6 && GetCmpImmCount(instr) == 5;
+ std::ranges::copy_if(
+ candidates | std::views::transform(DecodeFunction),
+ std::back_inserter(filteredInstructions),
+ [](const std::vector& instr) {
+ return GetDataReferenceCount(instr) == 5 && GetCallCount(instr) == 10 &&
+ GetUniqueCallCount(instr) == 6 && GetCmpImmCount(instr) == 5;
});
// should have only one result
if (filteredInstructions.size() != 1)
{
- printf("Filtered Instructions: %llu\n", filteredInstructions.size());
+ std::println("Filtered Instructions: {}", filteredInstructions.size());
return;
}
const auto& instructions = filteredInstructions[0];
- printf("RVA: 0x%08X\n", instructions.front().RVA);
+ std::println("RVA: 0x{:08X}", instructions.front().RVA);
// extract all the non-zero immediate values from the cmp instructions
- std::decay_t cmpInstructions;
- std::ranges::copy_if(instructions, std::back_inserter(cmpInstructions), [](const DecodedInstruction& instr) {
- return instr.Instruction.mnemonic == ZYDIS_MNEMONIC_CMP && instr.Operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE && instr.Operands[1].imm.value.u;
- });
-
std::vector cmdIds;
- std::ranges::transform(cmpInstructions, std::back_inserter(cmdIds), [](const DecodedInstruction& instr) {
- return instr.Operands[1].imm.value.u;
+ std::ranges::for_each(instructions, [&cmdIds](const DecodedInstruction& instr) {
+ if (instr.Instruction.mnemonic == ZYDIS_MNEMONIC_CMP &&
+ instr.Operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE &&
+ instr.Operands[1].imm.value.u != 0) {
+ cmdIds.push_back(static_cast(instr.Operands[1].imm.value.u));
+ }
});
for (const auto& cmdId : cmdIds)
{
- printf("CmdId: %u\n", cmdId);
- Globals::DynamicCmdIds.insert(cmdId);
+ std::println("CmdId: {}", cmdId);
+ Globals::DynamicCmdIds.insert(static_cast(cmdId));
}
@@ -218,9 +216,9 @@ namespace
int32_t GetCallCount(uint8_t* target)
{
- size_t sectionSize;
- const auto sectionAddress = GetSection("il2cpp", §ionSize);
- const auto sectionEnd = sectionAddress + sectionSize;
+ const auto il2cppSection = GetSection("il2cpp");
+ const auto sectionAddress = reinterpret_cast(il2cppSection.data());
+ const auto sectionSize = il2cppSection.size();
int32_t count = 0;
const __m128i callOpcode = _mm_set1_epi8(0xE8);
@@ -283,12 +281,10 @@ namespace
uintptr_t Resolve_BitConverter_ToUInt16()
{
- size_t sectionSize;
- const auto sectionAddress = GetSection("il2cpp", §ionSize);
- const auto sectionEnd = sectionAddress + sectionSize;
+ const auto il2cppSection = GetSection("il2cpp");
- printf("Section Address: 0x%llX\n", sectionAddress);
- printf("Section End: 0x%llX\n", sectionEnd);
+ std::print("Section Address: 0x{:X}", reinterpret_cast(il2cppSection.data()));
+ std::println("Section End: 0x{:X}", reinterpret_cast(il2cppSection.data() + il2cppSection.size()));
/*
mov ecx, 0Fh
@@ -299,8 +295,8 @@ namespace
mov ecx, 5
call ThrowHelper.ThrowArgumentException
*/
- auto candidates = Util::PatternScanAll(sectionAddress, sectionEnd, "B9 0F 00 00 00 E8 ? ? ? ? B9 0E 00 00 00 BA 16 00 00 00 E8 ? ? ? ? B9 05 00 00 00 E8 ? ? ? ?");
- printf("Candidates: %llu\n", candidates.size());
+ auto candidates = Util::PatternScanAll(il2cppSection, "B9 0F 00 00 00 E8 ? ? ? ? B9 0E 00 00 00 BA 16 00 00 00 E8 ? ? ? ? B9 05 00 00 00 E8 ? ? ? ?");
+ std::println("Candidates: {}", candidates.size());
std::vector filteredEntries;
std::ranges::copy_if(candidates, std::back_inserter(filteredEntries), [](uintptr_t& entry) {
@@ -310,10 +306,10 @@ namespace
for (const auto& entry : filteredEntries)
{
- printf("Entry: 0x%llX\n", entry);
+ std::println("Entry: 0x{:X}", entry);
}
- printf("Looking for call counts...\n");
+ std::println("Looking for call counts...");
std::mutex mutex;
std::unordered_map callCounts;
// find the call counts to candidate functions
@@ -333,7 +329,7 @@ namespace
uintptr_t targetEntry = 0;
for (const auto& [entry, count] : callCounts)
{
- printf("Entry: 0x%llX, RVA: 0x%08llX, Count: %d\n", entry, entry - Globals::BaseAddress, count);
+ std::println("Entry: 0x{:X}, RVA: 0x{:08X}, Count: {}", entry, entry - Globals::BaseAddress, count);
if (count == 5) {
targetEntry = entry;
}
@@ -380,7 +376,7 @@ void InitIL2CPP()
resolveFuncFuture.get();
resolveCmdIdFuture.get();
- printf("BaseAddress: 0x%llX\n", BaseAddress);
- printf("IsCNREL: %d\n", IsCNREL);
- printf("BitConverter_ToUInt16: 0x%llX\n", Offset.BitConverter_ToUInt16);
+ std::println("BaseAddress: 0x{:X}", BaseAddress);
+ std::println("IsCNREL: {:d}", IsCNREL);
+ std::println("BitConverter_ToUInt16: 0x{:X}", Offset.BitConverter_ToUInt16);
}
diff --git a/lib/src/il2cpp-types.h b/lib/src/il2cpp-types.h
index c043e00..c667da7 100644
--- a/lib/src/il2cpp-types.h
+++ b/lib/src/il2cpp-types.h
@@ -1,5 +1,6 @@
#pragma once
#include
+#include
template
class Array
@@ -16,6 +17,10 @@ public:
T* data() {
return vector;
}
+
+ std::span AsSpan() {
+ return { vector, max_length };
+ }
};
static_assert(alignof(Array) == 8, "Array alignment is incorrect");
@@ -29,6 +34,10 @@ struct PacketMeta
uint16_t HeaderLength;
uint32_t DataLength;
uint8_t Data[1];
+
+ std::span AsSpan() {
+ return {Data, DataLength};
+ }
};
#pragma pack(pop)
diff --git a/lib/src/util.cpp b/lib/src/util.cpp
index f90a1ec..23b89d4 100644
--- a/lib/src/util.cpp
+++ b/lib/src/util.cpp
@@ -1,5 +1,9 @@
#include
+#include
+#include
+#include
#include "util.h"
+
#include "globals.h"
#ifdef _DEBUG
@@ -11,12 +15,12 @@
namespace
{
struct HandleData {
- DWORD pid;
- HWND hwnd;
+ DWORD Pid;
+ HWND Hwnd;
};
bool IsMainWindow(HWND handle) {
- return GetWindow(handle, GW_OWNER) == (HWND)0 && IsWindowVisible(handle) == TRUE;
+ return GetWindow(handle, GW_OWNER) == nullptr && IsWindowVisible(handle) == TRUE;
}
bool IsUnityWindow(HWND handle) {
@@ -29,16 +33,16 @@ namespace
HandleData& data = *(HandleData*)lParam;
DWORD pid = 0;
GetWindowThreadProcessId(handle, &pid);
- if (data.pid != pid || !IsMainWindow(handle) || !IsUnityWindow(handle))
+ if (data.Pid != pid || !IsMainWindow(handle) || !IsUnityWindow(handle))
return TRUE;
- data.hwnd = handle;
+ data.Hwnd = handle;
return FALSE;
}
- std::tuple, std::vector> PatternToBytes(const char* pattern)
+ std::tuple, std::string> PatternToBytes(const char* pattern)
{
std::vector bytes;
- std::vector maskBytes;
+ std::string mask;
const auto start = const_cast(pattern);
const auto end = const_cast(pattern) + strlen(pattern);
@@ -49,14 +53,14 @@ namespace
if (*current == '?')
++current;
bytes.push_back(-1);
- maskBytes.push_back(false);
+ mask.push_back('?');
}
else {
bytes.push_back(strtoul(current, ¤t, 16));
- maskBytes.push_back(true);
+ mask.push_back('x');
}
}
- return { bytes, maskBytes };
+ return { bytes, mask };
}
}
@@ -69,17 +73,25 @@ namespace Util
{
HWND FindMainWindowByPID(DWORD pid)
{
- HandleData data = { pid, 0 };
+ HandleData data = {
+ .Pid = pid,
+ .Hwnd = nullptr
+ };
EnumWindows(EnumWindowsCallback, (LPARAM)&data);
- return data.hwnd;
+ return data.Hwnd;
}
- std::string Base64Encode(BYTE const* buf, unsigned int bufLen)
+ std::string Base64Encode(std::span data)
+ {
+ return Base64Encode(data.data(), data.size());
+ }
+
+ std::string Base64Encode(uint8_t const* buf, size_t bufLen)
{
std::string ret;
int i = 0;
- BYTE char_array_3[3];
- BYTE char_array_4[4];
+ uint8_t char_array_3[3];
+ uint8_t char_array_4[4];
while (bufLen--) {
char_array_3[i++] = *buf++;
if (i == 3) {
@@ -126,51 +138,67 @@ namespace Util
ErrorDialog("YaeAchievement", msg.c_str());
}
- uintptr_t PatternScan(uintptr_t start, uintptr_t end, const char* pattern)
- {
- const auto [patternBytes, patternMask] = PatternToBytes(pattern);
- const auto scanBytes = reinterpret_cast(start);
-
- const auto patternSize = patternBytes.size();
- const auto pBytes = patternBytes.data();
-
- for (auto i = 0ul; i < end - start - patternSize; ++i) {
- bool found = true;
- for (auto j = 0ul; j < patternSize; ++j) {
- if (scanBytes[i + j] != pBytes[j] && patternMask[j]) {
- found = false;
- break;
- }
- }
- if (found) {
- return reinterpret_cast(&scanBytes[i]);
- }
- }
-
- return 0;
- }
-
- std::vector PatternScanAll(uintptr_t start, uintptr_t end, const char* pattern)
+ std::vector PatternScanAll(std::span bytes, const char* pattern)
{
std::vector results;
const auto [patternBytes, patternMask] = PatternToBytes(pattern);
- const auto scanBytes = reinterpret_cast(start);
+ constexpr std::size_t chunkSize = 16;
- const auto patternSize = patternBytes.size();
- const auto pBytes = patternBytes.data();
+ const auto maskCount = static_cast(std::ceil(patternMask.size() / chunkSize));
+ std::array masks{};
- for (auto i = 0ul; i < end - start - patternSize; ++i) {
- bool found = true;
- for (auto j = 0ul; j < patternSize; ++j) {
- if (scanBytes[i + j] != pBytes[j] && patternMask[j]) {
- found = false;
- break;
+ auto chunks = patternMask | std::views::chunk(chunkSize);
+ for (std::size_t i = 0; auto chunk : chunks) {
+ int32_t mask = 0;
+ for (std::size_t j = 0; j < chunk.size(); ++j) {
+ if (chunk[j] == 'x') {
+ mask |= 1 << j;
}
}
- if (found) {
- results.push_back(reinterpret_cast(&scanBytes[i]));
- i += patternSize - 1;
+ masks[i++] = mask;
+ }
+
+ __m128i xmm1 = _mm_loadu_si128(reinterpret_cast(patternBytes.data()));
+ __m128i xmm2, xmm3, mask;
+
+ auto pData = bytes.data();
+ const auto end = pData + bytes.size() - patternMask.size();
+
+ while (pData < end)
+ {
+ _mm_prefetch(reinterpret_cast(pData + 64), _MM_HINT_NTA);
+
+ if (patternBytes[0] == pData[0])
+ {
+ xmm2 = _mm_loadu_si128(reinterpret_cast(pData));
+ mask = _mm_cmpeq_epi8(xmm1, xmm2);
+
+ if ((_mm_movemask_epi8(mask) & masks[0]) == masks[0])
+ {
+ bool found = true;
+ for (int i = 1; i < maskCount; ++i)
+ {
+ xmm2 = _mm_loadu_si128(reinterpret_cast(pData + i * chunkSize));
+ xmm3 = _mm_loadu_si128(reinterpret_cast(patternBytes.data() + i * chunkSize));
+ mask = _mm_cmpeq_epi8(xmm2, xmm3);
+ if ((_mm_movemask_epi8(mask) & masks[i]) != masks[i])
+ {
+ found = false;
+ break;
+ }
+ }
+
+
+ if (found) {
+ results.push_back(reinterpret_cast(pData));
+ }
+
+ }
+
+
}
+
+ ++pData;
}
return results;
diff --git a/lib/src/util.h b/lib/src/util.h
index 8346e1b..e60cf32 100644
--- a/lib/src/util.h
+++ b/lib/src/util.h
@@ -3,16 +3,17 @@
#include
#include
#include
+#include
namespace Util
{
HWND FindMainWindowByPID(DWORD pid);
- std::string Base64Encode(BYTE const* buf, unsigned int bufLen);
+ std::string Base64Encode(std::span data);
+ std::string Base64Encode(uint8_t const* buf, size_t bufLen);
void ErrorDialog(LPCSTR title, LPCSTR msg);
void ErrorDialog(LPCSTR msg);
void Win32ErrorDialog(DWORD code, DWORD winerrcode);
- uintptr_t PatternScan(uintptr_t start, uintptr_t end, const char* pattern);
- std::vector PatternScanAll(uintptr_t start, uintptr_t end, const char* pattern);
+ std::vector PatternScanAll(std::span bytes, const char* pattern);
}
\ No newline at end of file