#include <windows.h>
#include <iostream>
#include <fstream>
#include <shlobj.h>
#include <string>

volatile bool GuardRemoved = false;
static DWORD64 AddressToGuard = NULL;
static HANDLE TestVEHHandle = NULL;
static DWORD TestVEHOldProtect = NULL;
static bool ExitQueued = false; // Change this flag to exit the thread loop
static DWORD64 Offset = 0x1000; // Example Offset, adjust as needed for your target

// Function to log messages to a file in the user's documents folder
void LogToFile(const std::string& message) {
    char docPath[MAX_PATH];
    if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, 0, docPath))) {
        std::string logFilePath = std::string(docPath) + "\\log.txt";
        std::ofstream logFile(logFilePath, std::ios_base::app);
        if (logFile.is_open()) {
            logFile << message << std::endl;
            logFile.close();
        }
    }
}

// Function to get the current module's base address
DWORD64 GetModuleBaseAddress() {
    return reinterpret_cast<DWORD64>(GetModuleHandle(NULL));
}

LONG WINAPI TestVEH(EXCEPTION_POINTERS* _ExceptionInfo) {
    if (_ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) {
        DWORD64 relativeRip = _ExceptionInfo->ContextRecord->Rip - AddressToGuard;
        LogToFile("[+] RIP relative offset: " + std::to_string(relativeRip));

        GuardRemoved = true;
        while (GuardRemoved) {
            // Busy-wait until GuardRemoved is set to false
        }

        return EXCEPTION_CONTINUE_EXECUTION;
    }

    return EXCEPTION_CONTINUE_SEARCH;
}

void RemoveTestVEH() {
    if (!TestVEHHandle || !TestVEHOldProtect) {
        LogToFile("[x] (!TestVEHHandle || !TestVEHOldProtect)");
        return;
    }

    DWORD ignored;
    if (!VirtualProtect((LPVOID)(AddressToGuard), 1, TestVEHOldProtect, &ignored)) {
        LogToFile("[x] Failed to restore protection on address");
        return;
    }

    if (!RemoveVectoredExceptionHandler(TestVEHHandle)) {
        LogToFile("[x] Failed to remove Vectored Exception Handler");
        return;
    }

    TestVEHHandle = NULL;
    TestVEHOldProtect = NULL;
}

bool SetTestVEH() {
    if (TestVEHHandle || TestVEHOldProtect) {
        return false;
    }

    TestVEHHandle = AddVectoredExceptionHandler(true, TestVEH);
    if (!TestVEHHandle) {
        return false;
    }

    if (!VirtualProtect((void*)(AddressToGuard), 1, PAGE_EXECUTE_READ | PAGE_GUARD, &TestVEHOldProtect)) {
        return false;
    }

    return true;
}

DWORD WINAPI VEHThread(LPVOID) {
    // Set AddressToGuard to the base address of the game plus offset
    AddressToGuard = GetModuleBaseAddress() + Offset;

    if (!SetTestVEH()) {
        LogToFile("[x] Failed to set TestVEH");
        return 1;
    }

    while (!ExitQueued) {
        if (GuardRemoved) {
            DWORD oldProtect;
            VirtualProtect((LPVOID)(AddressToGuard), 1, PAGE_EXECUTE_READ | PAGE_GUARD, &oldProtect);
            GuardRemoved = false;
        }
        Sleep(50); // Avoid busy-wait burning CPU
    }

    RemoveTestVEH();
    return 0;
}

// DLL main entry point
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) {
    if (dwReason == DLL_PROCESS_ATTACH) {
        DisableThreadLibraryCalls(hInstance);
        CreateThread(nullptr, 0, VEHThread, nullptr, 0, nullptr);
    }
    return TRUE;
}