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 1/4] 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 From e65f0465202419fc0c7e93f9ef243be26b3dad0b Mon Sep 17 00:00:00 2001 From: REL <25654009+34736384@users.noreply.github.com> Date: Tue, 7 Jan 2025 23:36:27 -0500 Subject: [PATCH 2/4] added PlayerStoreNotify --- lib/YaeAchievementLib.vcxproj | 1 + lib/src/NamedPipe.h | 38 ++++++ lib/src/dllmain.cpp | 83 +++++++----- lib/src/globals.h | 13 +- lib/src/il2cpp-init.cpp | 247 ++++++++++++++++++++++++++++++---- lib/src/il2cpp-init.h | 2 +- lib/src/il2cpp-types.h | 47 +++++-- 7 files changed, 355 insertions(+), 76 deletions(-) create mode 100644 lib/src/NamedPipe.h diff --git a/lib/YaeAchievementLib.vcxproj b/lib/YaeAchievementLib.vcxproj index 6fbf0d6..19d11eb 100644 --- a/lib/YaeAchievementLib.vcxproj +++ b/lib/YaeAchievementLib.vcxproj @@ -99,6 +99,7 @@ + diff --git a/lib/src/NamedPipe.h b/lib/src/NamedPipe.h new file mode 100644 index 0000000..8be4097 --- /dev/null +++ b/lib/src/NamedPipe.h @@ -0,0 +1,38 @@ +#pragma once +#include +#include + +class NamedPipe +{ + HANDLE m_hPipe = INVALID_HANDLE_VALUE; +public: + NamedPipe(HANDLE hPipe) : m_hPipe(hPipe) {} + ~NamedPipe() { if (m_hPipe != INVALID_HANDLE_VALUE) CloseHandle(m_hPipe); } + + operator HANDLE() const { return m_hPipe; } + operator bool() const { return m_hPipe != INVALID_HANDLE_VALUE && m_hPipe != nullptr; } + NamedPipe& operator= (HANDLE hPipe) { + m_hPipe = hPipe; + return *this; + } + + bool Write(const void* data, size_t size) const + { + DWORD bytesWritten; + if (!WriteFile(m_hPipe, data, static_cast(size), &bytesWritten, nullptr) || bytesWritten != size) + return false; + return true; + } + + bool Write(std::span data) const + { + return Write(data.data(), data.size()); + } + + template + bool Write(const T& data) const + { + return Write(&data, sizeof(T)); + } + +}; \ No newline at end of file diff --git a/lib/src/dllmain.cpp b/lib/src/dllmain.cpp index a916390..1f76ad0 100644 --- a/lib/src/dllmain.cpp +++ b/lib/src/dllmain.cpp @@ -13,6 +13,26 @@ CRITICAL_SECTION CriticalSection; void SetBreakpoint(HANDLE thread, uintptr_t address, bool enable, uint8_t index = 0); +namespace +{ + PacketType GetPacketType(const PacketMeta* packet) + { + using namespace Globals; + const auto cmdid = packet->CmdId; + + if (AchievementId && cmdid == AchievementId) + return PacketType::Achivement; + + if (AchievementIdSet.contains(cmdid) && packet->DataLength > 500) + return PacketType::Achivement; + + if (PlayerStoreId && cmdid == PlayerStoreId) + return PacketType::Inventory; + + return PacketType::None; + } +} + namespace Hook { @@ -27,41 +47,41 @@ namespace Hook { SetBreakpoint((HANDLE)-2, Offset.BitConverter_ToUInt16, true); LeaveCriticalSection(&CriticalSection); - const auto packet = reinterpret_cast(val->data()); + if (ret != 0xAB89) + return ret; - auto CheckPacket = [](const PacketMeta* packet) -> bool { - const auto cmdid = _byteswap_ushort(packet->CmdId); - const auto dataLength = _byteswap_ulong(packet->DataLength); + const auto packet = val->As(); + const auto packetType = GetPacketType(packet); + if (packetType == PacketType::None) + return ret; - if (dataLength < 500) { - return false; - } +#ifdef _DEBUG + std::println("PacketType: {}", static_cast(packetType)); + std::println("CmdId: {}", packet->CmdId); + std::println("DataLength: {}", packet->m_DataLength); + //std::println("Data: {}", Util::Base64Encode(packet->AsSpan())); +#endif - if (CmdId != 0) { - return cmdid == CmdId; - } + if (!MessagePipe.Write(packetType)) + Util::Win32ErrorDialog(1002, GetLastError()); - return DynamicCmdIds.contains(cmdid); - }; + if (!MessagePipe.Write(packet->DataLength)) + Util::Win32ErrorDialog(1003, GetLastError()); - using namespace Globals; - if (ret == 0xAB89 && CheckPacket(packet)) + if (!MessagePipe.Write(packet->AsSpan())) + Util::Win32ErrorDialog(1004, GetLastError()); + + if (!AchievementsWritten) + AchievementsWritten = packetType == PacketType::Achivement; + + if (packetType == PacketType::Inventory) + PlayerStoreWrittenCount++; + + if (AchievementsWritten && PlayerStoreWrittenCount == 2) { - const auto headLength = _byteswap_ushort(packet->HeaderLength); - const auto dataLength = _byteswap_ulong(packet->DataLength); - - std::println("CmdId: {}", _byteswap_ushort(packet->CmdId)); - std::println("DataLength: {}", dataLength); - - const auto base64 = Util::Base64Encode(packet->Data + headLength, dataLength) + "\n"; - std::println("Base64: {}", base64); - #ifdef _DEBUG system("pause"); #endif - - WriteFile(MessagePipe, base64.c_str(), (DWORD)base64.length(), nullptr, nullptr); - CloseHandle(MessagePipe); ExitProcess(0); } @@ -97,11 +117,6 @@ void SetBreakpoint(HANDLE thread, uintptr_t address, bool enable, uint8_t index) return; } - if (!BaseAddress || Offset.BitConverter_ToUInt16 <= BaseAddress) { - // not initialized yet - return; - } - CONTEXT ctx{}; ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; GetThreadContext(thread, &ctx); @@ -120,6 +135,7 @@ DWORD __stdcall ThreadProc(LPVOID hInstance) #ifdef _DEBUG AllocConsole(); freopen_s((FILE**)stdout, "CONOUT$", "w", stdout); + system("pause"); #endif InitializeCriticalSection(&CriticalSection); @@ -132,10 +148,11 @@ DWORD __stdcall ThreadProc(LPVOID hInstance) SwitchToThread(); } - initFuture.get(); + if (!initFuture.get()) + ExitProcess(0); MessagePipe = CreateFileA(R"(\\.\pipe\YaeAchievementPipe)", GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); - if (MessagePipe == INVALID_HANDLE_VALUE) + if (!MessagePipe) { #ifdef _DEBUG std::println("CreateFile failed: {}", GetLastError()); diff --git a/lib/src/globals.h b/lib/src/globals.h index 5501158..eecf2f5 100644 --- a/lib/src/globals.h +++ b/lib/src/globals.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include "NamedPipe.h" #define PROPERTY2(type, name, cn, os) \ type name##_cn = cn; \ @@ -12,13 +13,19 @@ namespace Globals { inline HWND GameWindow = nullptr; - inline HANDLE MessagePipe = nullptr; + inline NamedPipe MessagePipe = nullptr; inline bool IsCNREL = true; inline uintptr_t BaseAddress = 0; // 5.1.0 - 24082 - inline uint16_t CmdId = 0; // use non-zero to override dynamic search - inline std::unordered_set DynamicCmdIds; + inline uint16_t AchievementId = 0; // use non-zero to override dynamic search + inline std::unordered_set AchievementIdSet; + + // 5.3.0 - 23233 + inline uint16_t PlayerStoreId = 0; // use non-zero to override dynamic search + + inline bool AchievementsWritten = false; + inline int32_t PlayerStoreWrittenCount = 0; class Offsets { diff --git a/lib/src/il2cpp-init.cpp b/lib/src/il2cpp-init.cpp index 65596eb..a627aeb 100644 --- a/lib/src/il2cpp-init.cpp +++ b/lib/src/il2cpp-init.cpp @@ -68,8 +68,9 @@ namespace /// decodes all instruction until next push, ignores branching /// /// + /// /// std::vector DecodedInstruction - std::vector DecodeFunction(uintptr_t address) + std::vector DecodeFunction(uintptr_t address, int32_t maxInstructions = -1) { using namespace Globals; @@ -109,6 +110,10 @@ namespace instructions.emplace_back(rva, instruction, operands, instruction.operand_count_visible); address += instruction.length; + + if (maxInstructions != -1 && instructions.size() >= maxInstructions) + break; + } return instructions; @@ -163,8 +168,11 @@ namespace })); } - void ResolveCmdId() + void ResolveAchivementCmdId() { + if (Globals::AchievementId != 0) + return; + const auto il2cppSection = GetSection("il2cpp"); std::println("Section Address: 0x{:X}", reinterpret_cast(il2cppSection.data())); @@ -178,7 +186,7 @@ namespace std::vector> filteredInstructions; std::ranges::copy_if( - candidates | std::views::transform(DecodeFunction), + candidates | std::views::transform([](auto va) { return DecodeFunction(va); }), std::back_inserter(filteredInstructions), [](const std::vector& instr) { return GetDataReferenceCount(instr) == 5 && GetCallCount(instr) == 10 && @@ -207,20 +215,20 @@ namespace for (const auto& cmdId : cmdIds) { - std::println("CmdId: {}", cmdId); - Globals::DynamicCmdIds.insert(static_cast(cmdId)); + std::println("AchievementId: {}", cmdId); + Globals::AchievementIdSet.insert(static_cast(cmdId)); } } - int32_t GetCallCount(uint8_t* target) + std::vector GetCalls(uint8_t* target) { const auto il2cppSection = GetSection("il2cpp"); const auto sectionAddress = reinterpret_cast(il2cppSection.data()); const auto sectionSize = il2cppSection.size(); - int32_t count = 0; + std::vector callSites; const __m128i callOpcode = _mm_set1_epi8(0xE8); const size_t simdEnd = sectionSize / 16 * 16; @@ -245,7 +253,7 @@ namespace const uintptr_t dest = sectionAddress + instruction_index + 5 + delta; if (dest == (uintptr_t)target) { - count++; + callSites.push_back(sectionAddress + instruction_index); } // clear the bit we just processed and continue with the next match @@ -253,7 +261,7 @@ namespace } } - return count; + return callSites; } uintptr_t FindFunctionEntry(uintptr_t address) // not a correct way to find function entry @@ -279,8 +287,13 @@ namespace return address; } - uintptr_t Resolve_BitConverter_ToUInt16() + void Resolve_BitConverter_ToUInt16() { + if (Globals::Offset.BitConverter_ToUInt16 != 0) { + Globals::Offset.BitConverter_ToUInt16 += Globals::BaseAddress; + return; + } + const auto il2cppSection = GetSection("il2cpp"); std::print("Section Address: 0x{:X}", reinterpret_cast(il2cppSection.data())); @@ -316,9 +329,9 @@ namespace std::vector> futures; std::ranges::transform(filteredEntries, std::back_inserter(futures), [&](uintptr_t entry) { return std::async(std::launch::async, [&](uintptr_t e) { - const auto count = GetCallCount((uint8_t*)e); + const auto callSites = GetCalls((uint8_t*)e); std::lock_guard lock(mutex); - callCounts[e] = count; + callCounts[e] = callSites.size(); }, entry); }); @@ -335,12 +348,186 @@ namespace } } - return targetEntry; + Globals::Offset.BitConverter_ToUInt16 = targetEntry; + } + + void ResolveInventoryCmdId() + { + if (Globals::PlayerStoreId != 0) + return; + + const auto il2cppSection = GetSection("il2cpp"); + std::println("Section Address: 0x{:X}", reinterpret_cast(il2cppSection.data())); + std::println("Section End: 0x{:X}", reinterpret_cast(il2cppSection.data() + il2cppSection.size())); + + /* + cmp r8d, 2 + jz 0x3B + cmd r8d, 1 + mov rax + */ + + // look for ItemModule.GetBagManagerByStoreType + const auto candidates = Util::PatternScanAll(il2cppSection, "41 83 F8 02 74 ? 41 83 F8 01 48 8B 05"); + std::println("Candidates: {}", candidates.size()); + if (candidates.empty()) + return; + + auto pGetBagManagerByStoreType = candidates.front(); + std::println("GetBagManagerByStoreType: 0x{:X}", pGetBagManagerByStoreType); + for (auto i = 0; i < 213; ++i) + { + + const auto va = pGetBagManagerByStoreType - i; + uint8_t* code = reinterpret_cast(va); + if (va % 16 == 0 && + code[0] == 0x56 && // push rsi + code[1] == 0x57) // push rdi + { + pGetBagManagerByStoreType = va; + break; + } + + } + + std::println("GetBagManagerByStoreType: 0x{:X}", pGetBagManagerByStoreType); + if (pGetBagManagerByStoreType == candidates.front()) + { + std::println("Failed to find function entry"); + return; + } + + + uintptr_t pOnPlayerStoreNotify = 0; + { + // get all calls to GetBagManagerByStoreType + auto calls = GetCalls((uint8_t*)pGetBagManagerByStoreType); + auto decodedInstructions = calls | std::views::transform([](auto va) { return DecodeFunction(va); }); + + // from the call sites, find the one that has an arbitary branch after the call + const auto targetInstructions = std::ranges::find_if(decodedInstructions, [](const std::vector& instr) { + return std::ranges::any_of(instr, [](const DecodedInstruction& i) { + return (i.Instruction.mnemonic == ZYDIS_MNEMONIC_JMP || i.Instruction.mnemonic == ZYDIS_MNEMONIC_CALL) && + i.Operands.size() == 1 && i.Operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER; + }); + }); + + if (targetInstructions == decodedInstructions.end()) + { + std::println("Failed to find target instruction"); + return; + } + + // ItemModule.OnPlayerStoreNotify + const auto& instructions = *targetInstructions; + pOnPlayerStoreNotify = Globals::BaseAddress + instructions.front().RVA; + for (auto i = 0; i < 126; ++i) + { + + const auto va = pOnPlayerStoreNotify - i; + uint8_t* code = reinterpret_cast(va); + + if (va % 16 == 0 && + code[0] == 0x56 && // push rsi + (*(uint32_t*)&code[1] & ~0xFF000000) == _byteswap_ulong(0x4883EC00)) // sub rsp, ?? + { + pOnPlayerStoreNotify = va; + break; + } + + } + + std::println("OnPlayerStoreNotify: 0x{:X}", pOnPlayerStoreNotify); + if (pOnPlayerStoreNotify == Globals::BaseAddress + instructions.front().RVA) + { + std::println("Failed to find function entry"); + return; + } + } + + uintptr_t pOnPacket = 0; + { + // get all calls to OnPlayerStoreNotify + const auto calls = GetCalls((uint8_t*)pOnPlayerStoreNotify); + if (calls.size() != 1) + { + std::println("Failed to find call site"); + return; + } + + // ItemModule.OnPacket + pOnPacket = calls.front(); + for (auto i = 0; i < 3044; ++i) + { + + const auto va = pOnPacket - i; + uint8_t* code = reinterpret_cast(va); + + if (va % 16 == 0 && + code[0] == 0x56 && // push rsi + (*(uint32_t*)&code[1] & ~0xFF000000) == _byteswap_ulong(0x4883EC00)) // sub rsp, ?? + { + pOnPacket = va; + break; + } + + } + + if (pOnPacket == calls.front()) + { + std::println("Failed to find function entry"); + return; + } + + std::println("OnPacket: 0x{:X}", pOnPacket); + } + + const auto decodedInstructions = DecodeFunction(pOnPacket); + std::unordered_map immBranch; // + uint32_t immValue = 0; + for (const auto& i : decodedInstructions) + { + if (i.Instruction.mnemonic == ZYDIS_MNEMONIC_CMP && + i.Operands.size() == 2 && + i.Operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + i.Operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE) + { + immValue = static_cast(i.Operands[1].imm.value.u); + } + + if (i.Instruction.meta.branch_type == ZYDIS_BRANCH_TYPE_NEAR && i.Operands.size() == 1) { + immBranch[immValue] = Globals::BaseAddress + i.RVA + i.Instruction.length + i.Operands[0].imm.value.s; + } + + } + + uint32_t cmdid = 0; + for (const auto& [imm, branch] : immBranch) + { + const auto instructions = DecodeFunction(branch, 10); + const auto isMatch = std::ranges::any_of(instructions, [pOnPlayerStoreNotify](const DecodedInstruction& i) { + if (i.Instruction.mnemonic != ZYDIS_MNEMONIC_CALL) + return false; + + uintptr_t destination = 0; + ZydisCalcAbsoluteAddress(&i.Instruction, i.Operands.data(), Globals::BaseAddress + i.RVA, &destination); + return destination == pOnPlayerStoreNotify; + }); + + if (!isMatch) + continue; + + cmdid = imm; + break; + } + + Globals::PlayerStoreId = static_cast(cmdid); + std::println("PlayerStoreId: {}", Globals::PlayerStoreId); } } -void InitIL2CPP() +bool InitIL2CPP() { std::string buffer; buffer.resize(MAX_PATH); @@ -358,25 +545,29 @@ void InitIL2CPP() IsCNREL = buffer.find("YuanShen.exe") != std::string::npos; BaseAddress = (uintptr_t)GetModuleHandleA(nullptr); - std::future resolveFuncFuture = std::async(std::launch::async, [] { - if (Offset.BitConverter_ToUInt16 != 0) { - Offset.BitConverter_ToUInt16 += BaseAddress; - } - else { - Offset.BitConverter_ToUInt16 = Resolve_BitConverter_ToUInt16(); - } - }); - - std::future resolveCmdIdFuture = std::async(std::launch::async, [] { - if (CmdId == 0) { - ResolveCmdId(); - } - }); + std::future resolveFuncFuture = std::async(std::launch::async, Resolve_BitConverter_ToUInt16); + std::future resolveCmdIdFuture = std::async(std::launch::async, ResolveAchivementCmdId); + std::future resolveInventoryFuture = std::async(std::launch::async, ResolveInventoryCmdId); resolveFuncFuture.get(); resolveCmdIdFuture.get(); + resolveInventoryFuture.get(); std::println("BaseAddress: 0x{:X}", BaseAddress); std::println("IsCNREL: {:d}", IsCNREL); std::println("BitConverter_ToUInt16: 0x{:X}", Offset.BitConverter_ToUInt16); + + if (!AchievementId && AchievementIdSet.empty()) + { + Util::ErrorDialog("Failed to resolve achievement data"); + return false; + } + + if (!PlayerStoreId) + { + Util::ErrorDialog("Failed to resolve inventory data"); + return false; + } + + return true; } diff --git a/lib/src/il2cpp-init.h b/lib/src/il2cpp-init.h index d0b0e0d..ee54cf8 100644 --- a/lib/src/il2cpp-init.h +++ b/lib/src/il2cpp-init.h @@ -1,4 +1,4 @@ #pragma once // IL2CPP application initializer -void InitIL2CPP(); +bool InitIL2CPP(); diff --git a/lib/src/il2cpp-types.h b/lib/src/il2cpp-types.h index c667da7..6903648 100644 --- a/lib/src/il2cpp-types.h +++ b/lib/src/il2cpp-types.h @@ -2,6 +2,17 @@ #include #include +#define PROPERTY_GET_CONST(type, name, funcBody) \ + type get_##name() const funcBody \ + __declspec(property(get = get_##name)) type name; + +enum class PacketType : uint8_t +{ + None = 0, + Achivement = 1, + Inventory = 2, +}; + template class Array { @@ -21,27 +32,41 @@ public: std::span AsSpan() { return { vector, max_length }; } + + template + U As() { + return reinterpret_cast(vector); + } }; static_assert(alignof(Array) == 8, "Array alignment is incorrect"); static_assert(offsetof(Array, vector) == 32, "vector offset is incorrect"); #pragma pack(push, 1) -struct PacketMeta +class PacketMeta { - uint16_t HeadMagic; - uint16_t CmdId; - uint16_t HeaderLength; - uint32_t DataLength; - uint8_t Data[1]; +public: + uint16_t m_HeadMagic; + uint16_t m_CmdId; + uint16_t m_HeaderLength; + uint32_t m_DataLength; + uint8_t m_Data[1]; + + PacketMeta() = delete; + + PROPERTY_GET_CONST(uint16_t, HeadMagic, { return _byteswap_ushort(m_HeadMagic); }); + PROPERTY_GET_CONST(uint16_t, CmdId, { return _byteswap_ushort(m_CmdId); }); + PROPERTY_GET_CONST(uint16_t, HeaderLength, { return _byteswap_ushort(m_HeaderLength); }); + PROPERTY_GET_CONST(uint32_t, DataLength, { return _byteswap_ulong(m_DataLength); }); std::span AsSpan() { - return {Data, DataLength}; + return { m_Data, DataLength }; } + }; #pragma pack(pop) -static_assert(offsetof(PacketMeta, CmdId) == 2, "CmdId offset is incorrect"); -static_assert(offsetof(PacketMeta, HeaderLength) == 4, "HeadLength offset is incorrect"); -static_assert(offsetof(PacketMeta, DataLength) == 6, "DataLength offset is incorrect"); -static_assert(offsetof(PacketMeta, Data) == 10, "Data offset is incorrect"); \ No newline at end of file +static_assert(offsetof(PacketMeta, m_CmdId) == 2, "CmdId offset is incorrect"); +static_assert(offsetof(PacketMeta, m_HeaderLength) == 4, "HeadLength offset is incorrect"); +static_assert(offsetof(PacketMeta, m_DataLength) == 6, "DataLength offset is incorrect"); +static_assert(offsetof(PacketMeta, m_Data) == 10, "Data offset is incorrect"); \ No newline at end of file From 6e1c8f275f7f5c736be8458afdc73dff4d3a2233 Mon Sep 17 00:00:00 2001 From: REL <25654009+34736384@users.noreply.github.com> Date: Tue, 7 Jan 2025 23:56:16 -0500 Subject: [PATCH 3/4] refactor --- lib/src/il2cpp-init.cpp | 157 +++++++++++++++++++--------------------- 1 file changed, 73 insertions(+), 84 deletions(-) diff --git a/lib/src/il2cpp-init.cpp b/lib/src/il2cpp-init.cpp index a627aeb..3d058dd 100644 --- a/lib/src/il2cpp-init.cpp +++ b/lib/src/il2cpp-init.cpp @@ -373,47 +373,47 @@ namespace if (candidates.empty()) return; - auto pGetBagManagerByStoreType = candidates.front(); - std::println("GetBagManagerByStoreType: 0x{:X}", pGetBagManagerByStoreType); - for (auto i = 0; i < 213; ++i) + uintptr_t pGetBagManagerByStoreType = candidates.front(); { + std::println("GetBagManagerByStoreType: 0x{:X}", pGetBagManagerByStoreType); - const auto va = pGetBagManagerByStoreType - i; - uint8_t* code = reinterpret_cast(va); - if (va % 16 == 0 && - code[0] == 0x56 && // push rsi - code[1] == 0x57) // push rdi + const auto isFunctionEntry = [](uintptr_t va) -> bool { + auto* code = reinterpret_cast(va); + return (va % 16 == 0 && + code[0] == 0x56 && // push rsi + code[1] == 0x57); // push rdi + }; + + auto range = std::views::iota(0, 213); + if (const auto it = std::ranges::find_if(range, [&](int i) { return isFunctionEntry(pGetBagManagerByStoreType - i); }); + it != range.end()) { - pGetBagManagerByStoreType = va; - break; + pGetBagManagerByStoreType -= *it; + } + else { + std::println("Failed to find function entry"); + return; } - } - - std::println("GetBagManagerByStoreType: 0x{:X}", pGetBagManagerByStoreType); - if (pGetBagManagerByStoreType == candidates.front()) - { - std::println("Failed to find function entry"); - return; + std::println("GetBagManagerByStoreType: 0x{:X}", pGetBagManagerByStoreType); } uintptr_t pOnPlayerStoreNotify = 0; { // get all calls to GetBagManagerByStoreType - auto calls = GetCalls((uint8_t*)pGetBagManagerByStoreType); + auto calls = GetCalls(reinterpret_cast(pGetBagManagerByStoreType)); auto decodedInstructions = calls | std::views::transform([](auto va) { return DecodeFunction(va); }); - // from the call sites, find the one that has an arbitary branch after the call - const auto targetInstructions = std::ranges::find_if(decodedInstructions, [](const std::vector& instr) { + // find the call site with an arbitrary branch (JMP or CALL) after the call + auto targetInstructions = std::ranges::find_if(decodedInstructions, [](const auto& instr) { return std::ranges::any_of(instr, [](const DecodedInstruction& i) { return (i.Instruction.mnemonic == ZYDIS_MNEMONIC_JMP || i.Instruction.mnemonic == ZYDIS_MNEMONIC_CALL) && i.Operands.size() == 1 && i.Operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER; }); }); - if (targetInstructions == decodedInstructions.end()) - { + if (targetInstructions == decodedInstructions.end()) { std::println("Failed to find target instruction"); return; } @@ -421,60 +421,53 @@ namespace // ItemModule.OnPlayerStoreNotify const auto& instructions = *targetInstructions; pOnPlayerStoreNotify = Globals::BaseAddress + instructions.front().RVA; - for (auto i = 0; i < 126; ++i) - { - const auto va = pOnPlayerStoreNotify - i; - uint8_t* code = reinterpret_cast(va); - - if (va % 16 == 0 && + const auto isFunctionEntry = [](uintptr_t va) -> bool { + auto* code = reinterpret_cast(va); + return (va % 16 == 0 && code[0] == 0x56 && // push rsi - (*(uint32_t*)&code[1] & ~0xFF000000) == _byteswap_ulong(0x4883EC00)) // sub rsp, ?? - { - pOnPlayerStoreNotify = va; - break; - } + (*reinterpret_cast(&code[1]) & ~0xFF000000) == _byteswap_ulong(0x4883EC00)); // sub rsp, ?? + }; - } - - std::println("OnPlayerStoreNotify: 0x{:X}", pOnPlayerStoreNotify); - if (pOnPlayerStoreNotify == Globals::BaseAddress + instructions.front().RVA) + auto range = std::views::iota(0, 126); + if (const auto it = std::ranges::find_if(range, [&](int i) { return isFunctionEntry(pOnPlayerStoreNotify - i); }); + it != range.end()) { + pOnPlayerStoreNotify -= *it; + } + else { std::println("Failed to find function entry"); return; } + + std::println("OnPlayerStoreNotify: 0x{:X}", pOnPlayerStoreNotify); } uintptr_t pOnPacket = 0; { // get all calls to OnPlayerStoreNotify - const auto calls = GetCalls((uint8_t*)pOnPlayerStoreNotify); - if (calls.size() != 1) - { + const auto calls = GetCalls(reinterpret_cast(pOnPlayerStoreNotify)); + if (calls.size() != 1) { std::println("Failed to find call site"); return; } - // ItemModule.OnPacket + // ItemModule.OnPacket - search backwards for function entry pOnPacket = calls.front(); - for (auto i = 0; i < 3044; ++i) - { - - const auto va = pOnPacket - i; - uint8_t* code = reinterpret_cast(va); - - if (va % 16 == 0 && + const auto isFunctionEntry = [](uintptr_t va) -> bool { + auto* code = reinterpret_cast(va); + return (va % 16 == 0 && code[0] == 0x56 && // push rsi - (*(uint32_t*)&code[1] & ~0xFF000000) == _byteswap_ulong(0x4883EC00)) // sub rsp, ?? - { - pOnPacket = va; - break; - } + (*reinterpret_cast(&code[1]) & ~0xFF000000) == _byteswap_ulong(0x4883EC00)); // sub rsp, ?? + }; - } - - if (pOnPacket == calls.front()) + auto range = std::views::iota(0, 3044); + if (const auto it = std::ranges::find_if(range, [&](int i) { return isFunctionEntry(pOnPacket - i); }); + it != range.end()) { + pOnPacket -= *it; + } + else { std::println("Failed to find function entry"); return; } @@ -483,11 +476,11 @@ namespace } const auto decodedInstructions = DecodeFunction(pOnPacket); - std::unordered_map immBranch; // - uint32_t immValue = 0; - for (const auto& i : decodedInstructions) - { - if (i.Instruction.mnemonic == ZYDIS_MNEMONIC_CMP && + uint32_t cmdid = 0; + std::ranges::for_each(decodedInstructions, [&cmdid, pOnPlayerStoreNotify](const DecodedInstruction& i) { + static uint32_t immValue = 0; // keep track of the last immediate value + + if (i.Instruction.mnemonic == ZYDIS_MNEMONIC_CMP && i.Operands.size() == 2 && i.Operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && i.Operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE) @@ -496,30 +489,26 @@ namespace } if (i.Instruction.meta.branch_type == ZYDIS_BRANCH_TYPE_NEAR && i.Operands.size() == 1) { - immBranch[immValue] = Globals::BaseAddress + i.RVA + i.Instruction.length + i.Operands[0].imm.value.s; + uintptr_t branchAddr = Globals::BaseAddress + i.RVA + i.Instruction.length + i.Operands[0].imm.value.s; + + // decode the branch address immediately + const auto instructions = DecodeFunction(branchAddr, 10); + const auto isMatch = std::ranges::any_of(instructions, [pOnPlayerStoreNotify](const DecodedInstruction& instr) { + if (instr.Instruction.mnemonic != ZYDIS_MNEMONIC_CALL) + return false; + + uintptr_t destination = 0; + ZydisCalcAbsoluteAddress(&instr.Instruction, instr.Operands.data(), Globals::BaseAddress + instr.RVA, &destination); + return destination == pOnPlayerStoreNotify; + }); + + if (isMatch) { + cmdid = immValue; + } + } - - } - - uint32_t cmdid = 0; - for (const auto& [imm, branch] : immBranch) - { - const auto instructions = DecodeFunction(branch, 10); - const auto isMatch = std::ranges::any_of(instructions, [pOnPlayerStoreNotify](const DecodedInstruction& i) { - if (i.Instruction.mnemonic != ZYDIS_MNEMONIC_CALL) - return false; - - uintptr_t destination = 0; - ZydisCalcAbsoluteAddress(&i.Instruction, i.Operands.data(), Globals::BaseAddress + i.RVA, &destination); - return destination == pOnPlayerStoreNotify; - }); - - if (!isMatch) - continue; - - cmdid = imm; - break; - } + return cmdid == 0; // stop processing if cmdid is found + }); Globals::PlayerStoreId = static_cast(cmdid); std::println("PlayerStoreId: {}", Globals::PlayerStoreId); From 43b38df986e9bdef51270ea324e3c307dece3435 Mon Sep 17 00:00:00 2001 From: REL <25654009+34736384@users.noreply.github.com> Date: Thu, 9 Jan 2025 01:05:46 -0500 Subject: [PATCH 4/4] fix bugs --- lib/src/NamedPipe.h | 12 ++++++++++-- lib/src/dllmain.cpp | 8 ++++---- lib/src/globals.h | 2 +- lib/src/il2cpp-types.h | 16 ++++++++++------ 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/lib/src/NamedPipe.h b/lib/src/NamedPipe.h index 8be4097..c1cb0f5 100644 --- a/lib/src/NamedPipe.h +++ b/lib/src/NamedPipe.h @@ -2,6 +2,13 @@ #include #include +template +concept IsSpan = requires(T t) { + { t.data() } -> std::convertible_to; + { t.size() } -> std::convertible_to; + { t.size_bytes() } -> std::convertible_to; +}; + class NamedPipe { HANDLE m_hPipe = INVALID_HANDLE_VALUE; @@ -24,9 +31,10 @@ public: return true; } - bool Write(std::span data) const + template + bool Write(const T data) const { - return Write(data.data(), data.size()); + return Write(data.data(), data.size_bytes()); } template diff --git a/lib/src/dllmain.cpp b/lib/src/dllmain.cpp index 1f76ad0..2e54aa8 100644 --- a/lib/src/dllmain.cpp +++ b/lib/src/dllmain.cpp @@ -58,7 +58,7 @@ namespace Hook { #ifdef _DEBUG std::println("PacketType: {}", static_cast(packetType)); std::println("CmdId: {}", packet->CmdId); - std::println("DataLength: {}", packet->m_DataLength); + std::println("DataLength: {}", packet->DataLength); //std::println("Data: {}", Util::Base64Encode(packet->AsSpan())); #endif @@ -74,10 +74,10 @@ namespace Hook { if (!AchievementsWritten) AchievementsWritten = packetType == PacketType::Achivement; - if (packetType == PacketType::Inventory) - PlayerStoreWrittenCount++; + if (!PlayerStoreWritten) + PlayerStoreWritten = packetType == PacketType::Inventory; - if (AchievementsWritten && PlayerStoreWrittenCount == 2) + if (AchievementsWritten && PlayerStoreWritten) { #ifdef _DEBUG system("pause"); diff --git a/lib/src/globals.h b/lib/src/globals.h index eecf2f5..b84d721 100644 --- a/lib/src/globals.h +++ b/lib/src/globals.h @@ -25,7 +25,7 @@ namespace Globals inline uint16_t PlayerStoreId = 0; // use non-zero to override dynamic search inline bool AchievementsWritten = false; - inline int32_t PlayerStoreWrittenCount = 0; + inline bool PlayerStoreWritten = false; class Offsets { diff --git a/lib/src/il2cpp-types.h b/lib/src/il2cpp-types.h index 6903648..25dad07 100644 --- a/lib/src/il2cpp-types.h +++ b/lib/src/il2cpp-types.h @@ -45,12 +45,12 @@ static_assert(offsetof(Array, vector) == 32, "vector offset is incorrec #pragma pack(push, 1) class PacketMeta { -public: uint16_t m_HeadMagic; uint16_t m_CmdId; uint16_t m_HeaderLength; uint32_t m_DataLength; uint8_t m_Data[1]; +public: PacketMeta() = delete; @@ -60,13 +60,17 @@ public: PROPERTY_GET_CONST(uint32_t, DataLength, { return _byteswap_ulong(m_DataLength); }); std::span AsSpan() { - return { m_Data, DataLength }; + return { m_Data + HeaderLength, DataLength }; } + friend struct PacketMetaStaticAssertHelper; }; #pragma pack(pop) -static_assert(offsetof(PacketMeta, m_CmdId) == 2, "CmdId offset is incorrect"); -static_assert(offsetof(PacketMeta, m_HeaderLength) == 4, "HeadLength offset is incorrect"); -static_assert(offsetof(PacketMeta, m_DataLength) == 6, "DataLength offset is incorrect"); -static_assert(offsetof(PacketMeta, m_Data) == 10, "Data offset is incorrect"); \ No newline at end of file +struct PacketMetaStaticAssertHelper +{ + static_assert(offsetof(PacketMeta, m_CmdId) == 2, "CmdId offset is incorrect"); + static_assert(offsetof(PacketMeta, m_HeaderLength) == 4, "HeadLength offset is incorrect"); + static_assert(offsetof(PacketMeta, m_DataLength) == 6, "DataLength offset is incorrect"); + static_assert(offsetof(PacketMeta, m_Data) == 10, "Data offset is incorrect"); +}; \ No newline at end of file