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