Подписывайтесь на наш Telegram и не пропускайте важные новости! Перейти

Гайд Принцип чтения памяти игры используя механики ядра ОС Windows (Virtual Memory, Physical Memory)

Ты разве не говоришь про подмену eprocess фиктивным значением? Можешь сам зайти в раст какой-нибудь и посмотреть. На последних билдах виндовса cr3 не меняется, хотя ранее он шафлился каждые 5-10 секунд. И на более старых билдах такого тоже вероятно уже нет, не проверял
да, возможно ты прав, что на каком-нибудь 25H2 крипт ср3 действительно убрали, я не смотрел, что там, но не смотря на это, начиная с 10 винды, заканчивая 23H2 (?), крипт ср3 ( а именно подмена значения в EPROCESS'e ) - остался. "хотя ранее он шафлился каждые 5-10 секунд" - раньше еак релокал пейдж тейблы, что заставляло заново получать ср3. сейчас ( с ~ лета / сентября прошлого года ) это убрали
 
да, возможно ты прав, что на каком-нибудь 25H2 крипт ср3 действительно убрали, я не смотрел, что там, но не смотря на это, начиная с 10 винды, заканчивая 23H2 (?), крипт ср3 ( а именно подмена значения в EPROCESS'e ) - остался. "хотя ранее он шафлился каждые 5-10 секунд" - раньше еак релокал пейдж тейблы, что заставляло заново получать ср3. сейчас ( с ~ лета / сентября прошлого года ) это убрали
Да. Подмена eprocess осталась. Но сейчас достаточно 1 раз при запуске получить cr3 и сохранить его. Он больше не меняется
 
И как это связано с твоим упоминанием LA57?
У большинства 4-х уровневая система умник, не у всех LA57 стоит.

Если в статье не учитывается LA57 это не делает ее технический ложной
я не говорил что она технически ложная потому что в ней не упомянут pml5, я добавил что по спеке pml4 это не самый верхний уровень

технически ложной ее делают фрагменты про физ и вирт память
 
после PG=1 понятие физ памяти не имеет смысла. в статье очень часто за чтением виртуальной памяти стоит вызов хуйни шинды которая подсосет cr3, а за чтение физ памяти(что, еще раз, некорректно) тот же процесс но со спизженным cr3.
 
после PG=1 понятие физ памяти не имеет смысла. в статье очень часто за чтением виртуальной памяти стоит вызов хуйни шинды которая подсосет cr3, а за чтение физ памяти(что, еще раз, некорректно) тот же процесс но со спизженным cr3.
до сих пор не понял о чем идет речь

если ты имел в виду: "В этой статье разберем, как работать с физической памятью и почему использование чтения / записи виртуальной памяти в условиях популярных анти-читов - плохое решение.", то я привел пример именно в контексте работы с анти-читами ( EAC, BE, etc ). сама система не предоставляет средств для безопасного чтения / записи виртуальной памяти, которое соответствовало бы требованиям вышеперечисленных анти-читов. опять таки я не говорю, что читать виртуальную память - бред, я всего лишь говорю, что пока-что в винде просто нет таких средств, которые обеспечивают безопасность.
 
после PG=1 понятие физ памяти не имеет смысла. в статье очень часто за чтением виртуальной памяти стоит вызов хуйни шинды которая подсосет cr3, а за чтение физ памяти(что, еще раз, некорректно) тот же процесс но со спизженным cr3.
Что значит неккоректно чтение физ памяти? Ты переводишь физику в вирт также как это делает твой процессор. Имея cr3 ты это можешь делать не находясь в контексте игры.
Если ты втупую делаешь mmcopyvirtualmemory то в зависимости от размера чита он у тебя может даже 20 тысяч раз вызываться. То есть ты 20 тысяч раз в секунду из вероятно неподписанной аттачишься к процессу игры.

И что за "вызов хуйни шинды". kestackattach? В случае с физикой его необязательно делать, когда у функции mmcopyvirtualmemory он делается в каждом вызове
 
до сих пор не понял о чем идет речь

если ты имел в виду: "В этой статье разберем, как работать с физической памятью и почему использование чтения / записи виртуальной памяти в условиях популярных анти-читов - плохое решение.", то я привел пример именно в контексте работы с анти-читами ( EAC, BE, etc ). сама система не предоставляет средств для безопасного чтения / записи виртуальной памяти, которое соответствовало бы требованиям вышеперечисленных анти-читов. опять таки я не говорю, что читать виртуальную память - бред, я всего лишь говорю, что пока-что в винде просто нет таких средств, которые обеспечивают безопасность.
я скорее про то что твоя статья не бьется с терминами x86
Что значит неккоректно чтение физ памяти? Ты переводишь физику в вирт также как это делает твой процессор. Имея cr3 ты это можешь делать не находясь в контексте игры.
Если ты втупую делаешь mmcopyvirtualmemory то в зависимости от размера чита он у тебя может даже 20 тысяч раз вызываться. То есть ты 20 тысяч раз в секунду из вероятно неподписанной аттачишься к процессу игры.

И что за "вызов хуйни шинды". kestackattach? В случае с физикой его необязательно делать, когда у функции mmcopyvirtualmemory он делается в каждом вызове
в x86 при PG вся память виртуальна, ты всегда читаешь и пишешь только в нее.
то что ты подменил cr3 и вирт адреса начали маппится на другие физические, не говорит о том что ты начал писать или читать физ память. ты все также работаешь с вирт памятью. вопрос именно к терминологии.
 
я скорее про то что твоя статья не бьется с терминами x86

в x86 при PG вся память виртуальна, ты всегда читаешь и пишешь только в нее.
то что ты подменил cr3 и вирт адреса начали маппится на другие физические, не говорит о том что ты начал писать или читать физ память. ты все также работаешь с вирт памятью. вопрос именно к терминологии.
Да. Ты не обрабатываешь физ память. Ты ее вручную переводишь в виртуальную и работаешь. Доеб только до этого.
То есть грубо говоря это тот же mmcopyvirutalmemory только без атача к процессу.
И это будет андетектнее. Все остальное по детектам уже зависит от остальных частей твоего чита
 
Да. Ты не обрабатываешь физ память. Ты ее вручную переводишь в виртуальную и работаешь. Доеб только до этого.
То есть грубо говоря это тот же mmcopyvirutalmemory только без атача к процессу.
И это будет андетектнее. Все остальное по детектам уже зависит от остальных частей твоего чита
не то чтоб прям доеб, статья про пейджинг, термины не про пейджинг
 
я скорее про то что твоя статья не бьется с терминами x86

в x86 при PG вся память виртуальна, ты всегда читаешь и пишешь только в нее.
то что ты подменил cr3 и вирт адреса начали маппится на другие физические, не говорит о том что ты начал писать или читать физ память. ты все также работаешь с вирт памятью. вопрос именно к терминологии.
ты прав, что мы работаем с виртуальной памятью, но в чит-деве термин "чтение физической памяти" означает ручной перевод адресов с помощью дтб без использования mm_copy_virtual_memory
 
Hoock:
Expand Collapse Copy
#include <Windows.h>
#include <stdio.h>
#include <string.h>
#include <TlHelp32.h>
#include <psapi.h>
#include <conio.h>
#include <iomanip>




DWORD pid = 0;

struct HookData {
    DWORD_PTR targetAddr;
    BYTE originalBytes[20];
    bool installed;
    DWORD oldProtect;
    DWORD_PTR trampolineAddr;
    int overwrittenBytes;
};

HookData g_hookData = { 0 };
HANDLE g_hProcess = NULL;

void realHandler(DWORD eaxValue) {
    if (eaxValue == 0) {
        printf("Некорректное значение EAX\n");
        return;
    }
    DWORD accessedAddr = eaxValue + 0x60;
    printf("Обращаемый адрес: 0x%p\n", (void*)accessedAddr);
}

__declspec(naked) void CppHookHandler() {
    __asm {
        push ebp
        mov ebp, esp

        pushad
        pushfd

        mov eax, [ebp + 8]
        call realHandler

        popfd
        popad

        mov eax, [eax + 0x60]
        jmp[g_hookData.targetAddr + 5]

        pop ebp
        ret
    }
}

bool ValidateMemory(HANDLE hProcess, DWORD_PTR addr, DWORD size) {
    MEMORY_BASIC_INFORMATION mbi;
    if (!VirtualQueryEx(hProcess, (LPCVOID)addr, &mbi, sizeof(mbi))) {
        return false;
    }
    return mbi.State == MEM_COMMIT && (mbi.Protect & PAGE_EXECUTE);
}

bool CreateTrampoline(HookData* hook) {
    if (!ReadProcessMemory(g_hProcess, (LPCVOID)hook->targetAddr,
        hook->originalBytes, 20, NULL)) {
        printf("Ошибка чтения байтов: %lu\n", GetLastError());
        return false;
    }

    MEMORY_BASIC_INFORMATION mbi;
    if (!VirtualQueryEx(g_hProcess, (LPCVOID)hook->targetAddr, &mbi, sizeof(mbi))) {
        return false;
    }

    hook->trampolineAddr = (DWORD_PTR)VirtualAllocEx(
        g_hProcess, NULL, 128, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

    if (!hook->trampolineAddr) {
        printf("Ошибка выделения памяти: %lu\n", GetLastError());
        return false;
    }

    BYTE trampoline[128] = { 0 };
    int bytesCopied = 0;

    for (int i = 0; i < 20; i++) {
        trampoline[i] = hook->originalBytes[i];
        bytesCopied++;
        if (i >= 2 &&
            hook->originalBytes[i - 2] == 0x8B &&
            hook->originalBytes[i - 1] == 0x40 &&
            hook->originalBytes[i] == 0x60) {
            break;
        }
    }

    DWORD_PTR returnAddr = hook->targetAddr + bytesCopied;
    INT64 returnOffset = (INT64)returnAddr - (INT64)(hook->trampolineAddr + bytesCopied) - 5;

    if (abs(returnOffset) > 0x7FFFFFFF) {
        printf("Слишком большое смещение для JMP\n");
        return false;
    }

    trampoline[bytesCopied] = 0xE9;
    memcpy(&trampoline[bytesCopied + 1], &returnOffset, 4);

    if (!WriteProcessMemory(g_hProcess, (LPVOID)hook->trampolineAddr,
        trampoline, 128, NULL)) {
        return false;
    }

    hook->overwrittenBytes = bytesCopied;
    return true;
}


bool InstallInlineHook(DWORD_PTR targetFunc) {
    g_hookData.targetAddr = targetFunc;

    MEMORY_BASIC_INFORMATION mbi;
    if (!VirtualQueryEx(g_hProcess, (LPCVOID)targetFunc, &mbi, sizeof(mbi))) {
        printf("Ошибка VirtualQueryEx: %lu\n", GetLastError());
        return false;
    }
    g_hookData.oldProtect = mbi.Protect;

    if (!CreateTrampoline(&g_hookData)) {
        return false;
    }

    INT64 offset = (INT64)g_hookData.trampolineAddr - (INT64)targetFunc - 5;
    if (abs(offset) > 0x7FFFFFFF) {
        printf("Слишком большое смещение: 0x%llX\n", offset);
        return false;
    }

    BYTE patch[5] = { 0xE9 };
    memcpy(&patch[1], &offset, 4);

    DWORD tempProtect;
    if (!VirtualProtectEx(g_hProcess, mbi.BaseAddress, mbi.RegionSize,
        PAGE_EXECUTE_READWRITE, &tempProtect)) {
        printf("Ошибка VirtualProtectEx: %lu\n", GetLastError());
        return false;
    }

    if (!WriteProcessMemory(g_hProcess, (LPVOID)targetFunc, patch, 5, NULL)) {
        printf("Ошибка записи патча: %lu\n", GetLastError());
        return false;
    }

    Sleep(100);
    if (!VirtualProtectEx(g_hProcess, mbi.BaseAddress, mbi.RegionSize,
        g_hookData.oldProtect, NULL)) {
        DWORD error = GetLastError();
        if (error == ERROR_NOACCESS) {
            printf("Античит блокирует восстановление прав\n");
        }
        else {
            printf("Ошибка восстановления прав: %lu\n", error);
            return false;
        }
    }

    g_hookData.installed = true;
    printf("Хук установлен на адрес 0x%p\n", (void*)targetFunc);
    return true;
}

void RemoveInlineHook() {
    if (g_hookData.installed) {
        MEMORY_BASIC_INFORMATION mbi;
        if (!VirtualQueryEx(g_hProcess, (LPCVOID)g_hookData.targetAddr, &mbi, sizeof(mbi))) {
            printf("Ошибка получения информации о памяти\n");
            return;
        }

        if (!WriteProcessMemory(g_hProcess, (LPVOID)g_hookData.targetAddr,
            g_hookData.originalBytes, g_hookData.overwrittenBytes, NULL)) {
            printf("Ошибка восстановления оригинальных байтов\n");
            return;
        }

        if (g_hookData.trampolineAddr) {
            VirtualFreeEx(g_hProcess, (LPVOID)g_hookData.trampolineAddr, 0, MEM_RELEASE);
        }

        g_hookData.installed = false;
        printf("Хук удален\n");
    }
}

DWORD GetProcId(const char* ProcName) {
    PROCESSENTRY32 pe32;
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

    if (hSnapshot == INVALID_HANDLE_VALUE) {
        return 0;
    }

    pe32.dwSize = sizeof(PROCESSENTRY32);

    if (Process32First(hSnapshot, &pe32)) {
        do {
            if (_stricmp(pe32.szExeFile, ProcName) == 0) {
                CloseHandle(hSnapshot);
                return pe32.th32ProcessID;
            }
        } while (Process32Next(hSnapshot, &pe32));
    }

    CloseHandle(hSnapshot);
    return 0;
}

DWORD_PTR GetModuleBaseAddress(DWORD processId, const char* moduleName) {
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
    if (!hProcess) return 0;

    HMODULE hMods[1024];
    DWORD cbNeeded;
    if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) {
        for (int i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {
            char szModName[MAX_PATH];
            if (GetModuleBaseNameA(hProcess, hMods[i], szModName, sizeof(szModName))) {
                if (_stricmp(szModName, moduleName) == 0) {
                    DWORD_PTR baseAddr = (DWORD_PTR)hMods[i];
                    CloseHandle(hProcess);
                    return baseAddr;
                }
            }
        }
    }
    CloseHandle(hProcess);
    return 0;
}

int main() {
    setlocale(LC_ALL, "Russian");
    const char* processName = "gta_sa.exe";
    

    DWORD instructionOffset = 0x2008BF;
    

    printf("Ищем процесс: %s\n", processName);

    pid = GetProcId(processName);
    if (pid == 0) {
        printf("Процесс не найден!\n");
        return 1;
    }

    g_hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    if (!g_hProcess) {
        printf("Не удалось открыть процесс\n");
        return 1;
    }

    DWORD_PTR baseAddr = GetModuleBaseAddress(pid, processName);
    if (baseAddr == 0) {
        printf("Не удалось получить базовый адрес\n");
        CloseHandle(g_hProcess);
        return 1;
    }

    DWORD_PTR targetAddr = baseAddr + instructionOffset;
    printf("Базовый адрес: 0x%p\n", (void*)baseAddr);
    printf("Целевой адрес: 0x%p\n", (void*)targetAddr);

    MEMORY_BASIC_INFORMATION mbi;
    if (VirtualQueryEx(g_hProcess, (LPCVOID)targetAddr, &mbi, sizeof(mbi))) {
        printf("Информация о регионе:\n");
        printf("  BaseAddress: 0x%p\n", mbi.BaseAddress);
        printf("  RegionSize: %lu байт\n", mbi.RegionSize);
        printf("  Protect: 0x%lx\n", mbi.Protect);
        printf("  State: 0x%lx\n", mbi.State);
    }

    if (!InstallInlineHook(targetAddr)) {
        printf("Не удалось установить хук\n");
        CloseHandle(g_hProcess);
        return 1;
    }

    printf("Хук установлен. Ожидание...\n");

    while (true) {
        if (_kbhit()) {
            int ch = _getch();
            if (ch == '\r' || ch == '\n') {
                break;
            }
        }
        Sleep(100);
    }

    RemoveInlineHook();
    CloseHandle(g_hProcess);
    printf("Программа завершена\n");
    return 0;
}
Всем Привет, кто может подсказсть что как прочитать eax?
 
Назад
Сверху Снизу