mirror of
https://github.com/HolographicHat/Yae.git
synced 2026-03-23 12:59:44 +08:00
205 lines
4.9 KiB
C++
205 lines
4.9 KiB
C++
// ReSharper disable CppClangTidyCertErr33C
|
|
#include <Windows.h>
|
|
#include <print>
|
|
#include <string>
|
|
#include <future>
|
|
#include <TlHelp32.h>
|
|
|
|
#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<uint8_t>* val, const int startIndex)
|
|
{
|
|
using namespace Globals;
|
|
const auto ToUInt16 = reinterpret_cast<decltype(&BitConverter_ToUInt16)>(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<PacketMeta*>();
|
|
const auto packetType = GetPacketType(packet);
|
|
if (packetType == PacketType::None)
|
|
return ret;
|
|
|
|
#ifdef _DEBUG
|
|
std::println("PacketType: {}", static_cast<uint8_t>(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<void*>(Offset.BitConverter_ToUInt16)) {
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
contextRecord->Rip = reinterpret_cast<DWORD64>(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;
|
|
}
|