// ReSharper disable CppClangTidyCertErr33C #include #include #include #include #include #include "globals.h" #include "util.h" #include "il2cpp-init.h" #include "il2cpp-types.h" 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 { uint16_t __fastcall BitConverter_ToUInt16(Array* val, const int startIndex) { using namespace Globals; const auto ToUInt16 = reinterpret_cast(Offset.BitConverter_ToUInt16); EnterCriticalSection(&CriticalSection); SetBreakpoint((HANDLE)-2, 0, false); const auto ret = ToUInt16(val, startIndex); SetBreakpoint((HANDLE)-2, Offset.BitConverter_ToUInt16, true); LeaveCriticalSection(&CriticalSection); if (ret != 0xAB89) return ret; const auto packet = val->As(); const auto packetType = GetPacketType(packet); if (packetType == PacketType::None) return ret; #ifdef _DEBUG std::println("PacketType: {}", static_cast(packetType)); std::println("CmdId: {}", packet->CmdId); std::println("DataLength: {}", packet->DataLength); //std::println("Data: {}", Util::Base64Encode(packet->AsSpan())); #endif if (!MessagePipe.Write(packetType)) Util::Win32ErrorDialog(1002, GetLastError()); if (!MessagePipe.Write(packet->DataLength)) Util::Win32ErrorDialog(1003, GetLastError()); if (!MessagePipe.Write(packet->AsSpan())) Util::Win32ErrorDialog(1004, GetLastError()); if (!AchievementsWritten) AchievementsWritten = packetType == PacketType::Achivement; if (!PlayerStoreWritten) PlayerStoreWritten = packetType == PacketType::Inventory; if (AchievementsWritten && PlayerStoreWritten) { #ifdef _DEBUG system("pause"); #endif ExitProcess(0); } return ret; } } LONG __stdcall VectoredExceptionHandler(PEXCEPTION_POINTERS ep) { using namespace Globals; const auto exceptionRecord = ep->ExceptionRecord; const auto contextRecord = ep->ContextRecord; if (exceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP) { if (exceptionRecord->ExceptionAddress != reinterpret_cast(Offset.BitConverter_ToUInt16)) { return EXCEPTION_CONTINUE_SEARCH; } contextRecord->Rip = reinterpret_cast(Hook::BitConverter_ToUInt16); contextRecord->EFlags &= ~0x100; // clear the trap flag return EXCEPTION_CONTINUE_EXECUTION; } return EXCEPTION_CONTINUE_SEARCH; } void SetBreakpoint(HANDLE thread, uintptr_t address, bool enable, uint8_t index) { using namespace Globals; if (index > 3) { return; } CONTEXT ctx{}; ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; GetThreadContext(thread, &ctx); DWORD64* dr = &ctx.Dr0; dr[index] = enable ? address : 0; const auto mask = 1ull << (index * 2); ctx.Dr7 |= mask; SetThreadContext(thread, &ctx); } DWORD __stdcall ThreadProc(LPVOID hInstance) { #ifdef _DEBUG AllocConsole(); freopen_s((FILE**)stdout, "CONOUT$", "w", stdout); system("pause"); #endif InitializeCriticalSection(&CriticalSection); auto initFuture = std::async(std::launch::async, InitIL2CPP); using namespace Globals; const auto pid = GetCurrentProcessId(); while ((GameWindow = Util::FindMainWindowByPID(pid)) == nullptr) { SwitchToThread(); } if (!initFuture.get()) ExitProcess(0); MessagePipe = CreateFileA(R"(\\.\pipe\YaeAchievementPipe)", GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr); if (!MessagePipe) { #ifdef _DEBUG std::println("CreateFile failed: {}", GetLastError()); #else Util::Win32ErrorDialog(1001, GetLastError()); ExitProcess(0); #endif } AddVectoredExceptionHandler(1, VectoredExceptionHandler); while (true) { THREADENTRY32 te32{}; te32.dwSize = sizeof(THREADENTRY32); const auto hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); for (Thread32First(hSnapshot, &te32); Thread32Next(hSnapshot, &te32);) { if (te32.th32OwnerProcessID != pid) { continue; } if (const auto hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID)) { EnterCriticalSection(&CriticalSection); SetBreakpoint(hThread, Offset.BitConverter_ToUInt16, true); CloseHandle(hThread); LeaveCriticalSection(&CriticalSection); } } CloseHandle(hSnapshot); Sleep(1); } return 0; } // DLL entry point BOOL __stdcall DllMain(HMODULE hInstance, DWORD fdwReason, LPVOID lpReserved) { if (fdwReason == DLL_PROCESS_ATTACH) { if (const auto hThread = CreateThread(nullptr, 0, ThreadProc, hInstance, 0, nullptr)) { CloseHandle(hThread); } } return TRUE; }