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