AHK скрипт AHK | SIGNATURE SCANNER | АВТООБНОВЛЕНИЕ ОФФСЕТОВ (15.01.2023)

Пользователь
Статус
Оффлайн
Регистрация
30 Апр 2019
Сообщения
143
Реакции[?]
34
Поинты[?]
5K
ПОСЛЕДНИЕ ИЗМЕНЕНИЯ - 15.01.2023:
- переработан оптимизирован Fcode.dll

- добавлен новый (необязательный) параметр для FAST_SEARCH_SIGNATURE_IN_PROCESS

Всем привет, друзья! Наконец-то, у меня получилось реализовать собственный сканнер сигнатур (или, в простонародье, автоматические обновления оффсетов) на языке AutoHotKey.

Как вы можете помнить из прошлых моих тем, AutoHotKey это скриптовой язык программирования, который даст просраться другим языкам в геймхакинге при правильном его использовании. Плюсом AutoHotKey является его простота использования и неплохая скорость выполнения кода. По моим наблюдениям, он быстрее Python и даже в некоторых случаях по скорости обходит C#. Но, так как это всё-таки скриптовой язык, его скорость выполнения кода не сопоставима с легендарным C++. Казалось бы, хвалил - хвалил, но по итогам сам же его слил. Но что, если я скажу, что AutoHotKey может работать в связке с C++? Ооо, это уже совсем меняет суть вопроса, не так ли? Но вы скажете: "Чел, в чём понт в твоём AutoHotKey, если его фишка в простоте написании кода?" А я в таком случае отвечу: "Простота написания кода остаётся прежней, с помощью всего одной функции в AutoHotKey, мы можем загрузить в наш AHK код на C++, который не потребует особо много мозгов."

Заинтересовал? Погнали!

Содержимое темы:
1. Кратное объяснение, определения: массив байт, сигнатура, паттерн
2. Поиск сигнатуры с помощью стороннего софта (CheatEngine)
3. Пример нахождение сигнатуры с помощью готовой функции в скрипте


ВНИМАНИЕ! Тема предназначена для аудитории, которая разбирается в Cheat Engine или OllyDbg. Прежде чем вы начнете изучать материал, я настоятельно рекомендую вам изучить хотя бы основы Cheat Engine, а также хорошо ориентироваться в коде AutoHotkey. Если вам не интересен принцип работы скрипта, готовый код будет прилеплен в конце этой темы.

Кратное объяснение, определения: массив байт, сигнатура.

Если по-простому, массив байт - это последовательность какой-то информации в игре или программе. В таком массиве может храниться: наш баланс в игре, количество ХП, патронов и т.д. Своего рода такой массив байт - это и есть наша сигнатура. Массив байт | сигнатура, выглядит так: 59 61 20 6C 6F 68 20 65 62 61 6E 69 79 00. В данном примере, этот массив байт хранит в себе какой-то текст в игре. Паттерн - это массив байт | сигнатура, которую мы ищем с помощью разных программ, в нашем случае это скрипт. Наша задача - получить адрес на этот текст, но проблема в том, что после каждого перезахода в игру нам приходится заново искать адрес, так как игра загружает его в другое место под другим адресом. В такой ситуации логично будет создать указатель на этот адрес с помощью программы CheatEngine и это будет правильно. Но, когда разработчики выкатят какой-то патч | обнову указатель перестанет работать. По этому нам на помощь приходит сканнер сигнатур. Запомните, адрес может манятся сколько угодно, но текст как неотъемлемый элемент игры, будет находится практически всегда неизменным.

Теория конечно хорошо, но практика лучше. Посмотрим на примере игры Need for Speed - Most Wanted (2005 года). Задача, найти мой ник в игре и получить постоянную сигнатуру на мой ник.
1) Открываем CheatEngine и подключаем нашу игру.
2) В строке Value Type вместо 4 Bytes выбираем String (так как мы ищем текст, а не значение).
3) В строке поиска пишу свой ник DayMond, и нажимаю поиск.
4) Нашло только один адрес, кликаю два раза по нему, после, правой кнопкой по адресу который перенесли в нижнюю таблицу:
1672851991753.png
5) Выбираем пункт Browse this memory region или комбинация клавиш CTRL + B.
6) Смотрим ниже, видим большое количество массивов байтов. Нас интересует самый верхний адрес: 083E7DB0 и его содержимое в виде сигнатуры:
1672853394208.png
Готово! Мы нашли сигнатуру для поиска. Осталось эти данные записать в AHK скрипт и запустить его. После чего мы получим наш адрес - 0x083E7DB0.


Давайте для начала скачаем AutoHotKey второй версии необходимый для работы моего скрипта, который недавно релизнули разработчики у себя на официальном сайте:
Пожалуйста, авторизуйтесь для просмотра ссылки.
(no ad).

После того, как мы скачали и установили AHK v2. Создаём простой скрипт с таким содержимым - MsgBox("Привет!").
Теперь нам нужно заставить его запускаться на 64\32 bit.
Для этого делаем следующее:
1) по только что созданному скрипту, нажимаем правой кнопкой мыши ---> Свойства ---> Изменить... ---> "Листаем в самый низ" ---> Дополнительно ---> Найти другое приложение на этом ПК ---> "Ищем папку куда установили AHK" ---> "Открываем ее" ---> v2 ---> Открыть ---> AutoHotkey32 ---> Открыть.
2) теперь повторяем те же действия, только в папке v2 выбираем AutoHotkey64, после чего открываем.
После всех манипуляций, когда мы нажимаем Изменить, у нас на выбор появляется запуск AHK в 64 и 32 бита (осторожно, нам нужны ahk с новой иконкой). Для наших дел втыкаем AutoHotKey 64-bit и запускаем созданный файл, если вылезло слово: Привет! Значит мы все сделали правильно.

И так, надеюсь у вас все получилось. Самое время испытать наш скрипт. Переходим на мой
Пожалуйста, авторизуйтесь для просмотра ссылки.
(no ad) и качаем два .dll файла (
Пожалуйста, авторизуйтесь для просмотра ссылки.
и
Пожалуйста, авторизуйтесь для просмотра ссылки.
). Это и есть код написанный на языке С++, который выполняет математику.

Исходники:
- переработан оптимизирован Fcode.dll
- добавлен новый (необязательный) параметр для FAST_SEARCH_SIGNATURE_IN_PROCESS
Fcode.dll (64-bit | 32-bit):
#include "pch.h"
#include <vector>

using namespace std;

extern "C" __declspec(dllexport) size_t module(HANDLE PROCESS_HANDLE, LPCVOID START_ADDRESS, uintptr_t END_ADDRESS, char* PATTERN, int PATTERN_LENGTH, int EXTRA)
{
    MEMORY_BASIC_INFORMATION mbi;
    mbi.RegionSize = 0x1000;
    size_t start;
    int i = 0;
    int j = 0;

    VirtualQueryEx(PROCESS_HANDLE, START_ADDRESS, &mbi, sizeof(mbi));
    for (start = (size_t)START_ADDRESS; start < start + END_ADDRESS; start += mbi.RegionSize)
    {
        if (!VirtualQueryEx(PROCESS_HANDLE, (LPCVOID)start, &mbi, sizeof(mbi))) continue;
        if (mbi.State != MEM_COMMIT || mbi.Protect & PAGE_NOACCESS) continue;

        vector<char> buffer(mbi.RegionSize);
        if (ReadProcessMemory(PROCESS_HANDLE, mbi.BaseAddress, buffer.data(), mbi.RegionSize, NULL))
        {
            for (i = 0; i < mbi.RegionSize; i++)
            {
                for (j = 0; j < PATTERN_LENGTH; j++)
                {
                    if (buffer[i + j] != PATTERN[j] && PATTERN[j] != -1)
                    {
                        break;
                    }
                }
                if (j == PATTERN_LENGTH)
                {
                    return start + i + EXTRA;
                }
            }
        }
    }
    return -100;
}

extern "C" __declspec(dllexport) size_t process(HANDLE PROCESS_HANDLE, LPCVOID START_ADDRESS, char* PATTERN, int PATTERN_LENGTH, int EXTRA)
{
    MEMORY_BASIC_INFORMATION mbi;
    mbi.RegionSize = 0x1000;
    size_t start;
    int i = 0;
    int j = 0;

    VirtualQueryEx(PROCESS_HANDLE, START_ADDRESS, &mbi, sizeof(mbi));
    for (start = (size_t)START_ADDRESS; VirtualQueryEx(PROCESS_HANDLE, (LPCVOID)start, &mbi, sizeof(mbi)); start += mbi.RegionSize)
    {
        if (!VirtualQueryEx(PROCESS_HANDLE, (LPCVOID)start, &mbi, sizeof(mbi))) continue;
        if (mbi.State != MEM_COMMIT || mbi.Protect & PAGE_NOACCESS) continue;

        vector<char> buffer(mbi.RegionSize);
        if (ReadProcessMemory(PROCESS_HANDLE, mbi.BaseAddress, buffer.data(), mbi.RegionSize, NULL))
        {
            for (i = 0; i < mbi.RegionSize; i++)
            {
                for (j = 0; j < PATTERN_LENGTH; j++)
                {
                    if (buffer[i + j] != PATTERN[j] && PATTERN[j] != -1)
                    {
                        break;
                    }
                }
                if (j == PATTERN_LENGTH)
                {
                    return start + i + EXTRA;
                }
            }
        }
    }
    return -100;
}
Основной AHK скрипт::
#SingleInstance Force
RUN_AS_ADMINISTRATOR(1)                             ; 1 = запуск скрипта от имени Администратора | 0 = обычный режим
LOAD_FAST_CODE()                                    ; Подгружает нужные .dll для работы скрипта
CONNECTING_TO_GAME("hl2.exe", "server.dll")         ; Первый параметр это процесс игры | Второй параметр это модуль игры (.exe | .dll)

;       ________________________________________________________________________________________________
;       |                                                                                               |
;       |     FAST_SEARCH_SIGNATURE_IN_PROCESS - это поиск сигнатуры по всему процессу игры             |
;       |     Пример:                                                                                   |
;       |     address := FAST_SEARCH_SIGNATURE_IN_PROCESS("02 48 ?? 76 A8", 0, 0x1234(по желанию))      |
;       |                                                                                               |
;       |     Функция возвращает адрес                                                                  |
;       |     Первый параметр                   - наш паттерн                                           |
;       |     Второй параметр                   - смещенние в байтах                                    |
;       |     Третий параметр(по желанию)       - адрес с которого нужно начать поиск по всему процессу |
;       |_______________________________________________________________________________________________|
;       ________________________________________________________________________________________________
;       |                                                                                               |
;       |     FAST_SEARCH_SIGNATURE_IN_MODULE - это поиск сигнатуры внутри указанного модуля игры       |
;       |     Пример:                                                                                   |
;       |     address := FAST_SEARCH_SIGNATURE_IN_MODULE("02 48 ?? 76 A8", 0, 0x620000, 0x10000)        |
;       |                                                                                               |
;       |     Функция возвращает адрес                                                                  |
;       |     Первый параметр       - наш паттерн                                                       |
;       |     Второй параметр       - смещенние в байтах                                                |
;       |     Третий параметр       - начальный адрес модуля в котором хранится наша сигнатура          |
;       |     Четвертый параметр    - размер модуля который мы указали в третьем параметре              |
;       |_______________________________________________________________________________________________|

CONNECTING_TO_GAME(NAME_GAME, DLL_GAME)
{
    global PID, Client, ProcessHandle
    if (!PID        := ProcessWait(NAME_GAME, 60))
    {
        MsgBox("Игра не найдена!")
        ExitApp()
    }
    Client          := GET_DLL_BASE(DLL_GAME, PID)
    ProcessHandle   := DllCall("OpenProcess", "int", 2035711, "char", 0, "UInt", PID, "UInt")
    return 1
}
GET_DLL_BASE(DLL_GAME, PID)
{
    static TH32CS_SNAPMODULE := 0x00000008
    static TH32CS_SNAPMODULE32 := 0x00000010
    global modBaseSize, hModule
    if (ProcessExist(PID))
    {
        if (hSnapshot := DllCall("CreateToolhelp32Snapshot", "UInt", (TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32), "UInt", PID, "Ptr"))
        {
            MODULEENTRY32 := Buffer(A_PtrSize = 8 ? 568 : 548, 0)
            NumPut("UInt", MODULEENTRY32.Size, MODULEENTRY32, 0)
            if (DllCall("Module32First", "UInt", hSnapshot, "UInt", MODULEENTRY32.Ptr))
            {
                while (DllCall("Module32Next", "UInt", hSnapshot, "UInt", MODULEENTRY32.Ptr))
                {
                    if (DLL_GAME = StrGet(MODULEENTRY32.Ptr + (A_PtrSize = 8 ? 48 : 32), 256, "CP0"))
                    {
                        modBaseAddr := NumGet(MODULEENTRY32, (A_PtrSize = 8 ? 24 : 20), "Ptr")
                        modBaseSize := NumGet(MODULEENTRY32, (A_PtrSize = 8 ? 32 : 24), "Ptr")
                        hModule     := NumGet(MODULEENTRY32, (A_PtrSize = 8 ? 40 : 32), "Ptr")
                    }
                }
            }
            DllCall("CloseHandle", "Ptr", hSnapshot)
            return modBaseAddr
        }
    }
    MsgBox("PID не найден!")
    ExitApp()
}
FAST_SEARCH_SIGNATURE_IN_MODULE(PATTERN := "", EXTRA := 0, START_ADDRESS := Client, END_ADDRESS := modBaseSize)
{
    PATTERN := StrSplit(StrReplace(RegExReplace(StrReplace(" " PATTERN, A_Space, " 0x"), A_Space, "", , 1, 1), "0x??", "-1"), A_Space)
    create_array_cpp := Buffer(PATTERN.Length * 1)
    loop PATTERN.Length
    {
        NumPut("Char", PATTERN[A_Index], create_array_cpp.Ptr, (A_Index - 1) * 1)
    }
    result := DllCall(A_PtrSize = 8 ? "Fcode64\module" : "Fcode32\module", "Ptr", ProcessHandle, "Ptr", START_ADDRESS, "Ptr", END_ADDRESS, "Ptr", create_array_cpp, "Ptr", PATTERN.Length, "Ptr", EXTRA, "CDecl Ptr")
    return result != -100 ? Format("0x{:02X}", result) : result
}
FAST_SEARCH_SIGNATURE_IN_PROCESS(PATTERN := "", EXTRA := 0, START_ADDRESS := 0)
{
    PATTERN := StrSplit(StrReplace(RegExReplace(StrReplace(" " PATTERN, A_Space, " 0x"), A_Space, "", , 1, 1), "0x??", "-1"), A_Space)
    create_array_cpp := Buffer(PATTERN.Length * 1)
    loop PATTERN.Length
    {
        NumPut("Char", PATTERN[A_Index], create_array_cpp.Ptr, (A_Index - 1) * 1)
    }
    result := DllCall(A_PtrSize = 8 ? "Fcode64\process" : "Fcode32\process", "Ptr", ProcessHandle, "Ptr", START_ADDRESS, "Ptr", create_array_cpp, "Ptr", PATTERN.Length, "Ptr", EXTRA, "CDecl Ptr")
    return result != -100 ? Format("0x{:02X}", result) : result
}
LOAD_FAST_CODE()
{
    #DllLoad "Kernel32.dll"
    #DllLoad "*i Fcode64.dll"
    #DllLoad "*i Fcode32.dll"
    if (!DllCall("GetModuleHandle", "Str", (A_PtrSize = 8 ? "Fcode64.dll" : "Fcode32.dll")))
    {
        MsgBox("Dll: Fcode не обнаружен!")
        ExitApp()
    }
}
RUN_AS_ADMINISTRATOR(KEY := 0)
{
    if (KEY != 0)
    {
        full_command_line := DllCall("GetCommandLine", "str")
        if not (A_IsAdmin or RegExMatch(full_command_line, " /restart(?!\S)"))
        {
            try
            {
                if A_IsCompiled
                    Run '*RunAs "' A_ScriptFullPath '" /restart'
                else
                    Run '*RunAs "' A_AhkPath '" /restart "' A_ScriptFullPath '"'
            }
            ExitApp
        }
    }
}
READ_MEMORY(ADDRESS, TYPE := "UInt")
{
    if (DllCall("Kernel32\ReadProcessMemory", "UInt", ProcessHandle, "UInt", ADDRESS, TYPE "*", &uint32 := 0, "Ptr", 4, "UInt", 0))
    {
        return uint32
    }
}
WRITE_MEMORY(ADDRESS, VALUE, TYPE := "UInt")
{
    return DllCall("Kernel32\WriteProcessMemory", "UInt", ProcessHandle, "UInt", ADDRESS, TYPE "*", VALUE, "UInt", 4, "UInt *", 0)
}
INS:: Pause -1
END:: ExitApp
- оптимизирован Fcode.dll
- функции поиска паттерна ускорены в 8 раз

Fcode.dll (64-bit | 32-bit):
#include "pch.h"

extern "C" __declspec(dllexport) int module(HANDLE PROCESS_HANDLE, LPCVOID START_ADDRESS, SIZE_T END_ADDRESS, char* PATTERN, int PATTERN_LENGTH, int EXTRA)
{
    int j;
    char* buffer = new char[END_ADDRESS];
    if (ReadProcessMemory(PROCESS_HANDLE, START_ADDRESS, buffer, END_ADDRESS, NULL))
    {
        for (int i = 0; i < END_ADDRESS; i++)
        {
            for (j = 0; j < PATTERN_LENGTH; j++)
            {
                if (buffer[i + j] != PATTERN[j] && PATTERN[j] != -1)
                {
                    break;
                }
            }
            if (j == PATTERN_LENGTH)
            {
                delete[] buffer;
                return (int)START_ADDRESS + i + EXTRA;
            }
        }
    }
    delete[] buffer;
    return -100;
}

extern "C" __declspec(dllexport) int process(HANDLE PROCESS_HANDLE, char* PATTERN, int PATTERN_LENGTH, int EXTRA)
{
    int j;
    LPCVOID old_RegionSize = 0;
    MEMORY_BASIC_INFORMATION mbi;
    while (VirtualQueryEx(PROCESS_HANDLE, old_RegionSize, &mbi, sizeof(mbi)))
    {
        if (mbi.State == MEM_COMMIT)
        {
            char* buffer = new char[mbi.RegionSize];
            if (ReadProcessMemory(PROCESS_HANDLE, (LPCVOID)mbi.BaseAddress, buffer, (SIZE_T)mbi.RegionSize, NULL))
            {
                for (int i = 0; i < mbi.RegionSize; i++)
                {
                    for (j = 0; j < PATTERN_LENGTH; j++)
                    {
                        if (buffer[i + j] != PATTERN[j] && PATTERN[j] != -1)
                        {
                            break;
                        }
                    }
                    if (j == PATTERN_LENGTH)
                    {
                        delete[] buffer;
                        return (int)((UINT_PTR)mbi.BaseAddress + i + EXTRA);
                    }
                }
            }
            delete[] buffer;
        }
        old_RegionSize = (LPVOID)((UINT_PTR)mbi.BaseAddress + mbi.RegionSize);
    }
    return -100;
}
Основной AHK скрипт::
#SingleInstance Force
RUN_AS_ADMINISTRATOR(1)                             ; 1 = запуск скрипта от имени Администратора | 0 = обычный режим
LOAD_FAST_CODE()                                    ; Подгружает нужные .dll для работы скрипта
CONNECTING_TO_GAME("hl2.exe", "server.dll")         ; Первый параметр это процесс игры | Второй параметр это модуль игры (.exe | .dll)


;       ____________________________________________________________________________________________
;       |                                                                                           |
;       |     FAST_SEARCH_SIGNATURE_IN_PROCESS - это поиск сигнатуры по всему процессу игры         |
;       |     Пример:                                                                               |
;       |     address := FAST_SEARCH_SIGNATURE_IN_PROCESS("02 48 ?? 76 A8", 0)                      |
;       |                                                                                           |
;       |     Функция возвращает адрес                                                              |
;       |     Первый параметр       - наш паттерн                                                   |
;       |     Второй параметр       - смещенние в байтах                                            |
;       |___________________________________________________________________________________________|
;       ____________________________________________________________________________________________
;       |                                                                                           |
;       |     FAST_SEARCH_SIGNATURE_IN_MODULE - это поиск сигнатуры внутри указанного модуля игры   |
;       |     Пример:                                                                               |
;       |     address := FAST_SEARCH_SIGNATURE_IN_MODULE("02 48 ?? 76 A8", 0, 0x620000, 0x10000)    |
;       |                                                                                           |
;       |     Функция возвращает адрес                                                              |
;       |     Первый параметр       - наш паттерн                                                   |
;       |     Второй параметр       - смещенние в байтах                                            |
;       |     Третий параметр       - начальный адрес модуля в котором хранится наша сигнатура      |
;       |     Четвертый параметр    - размер модуля который мы указали в третьем параметре          |
;       |___________________________________________________________________________________________|


CONNECTING_TO_GAME(NAME_GAME, DLL_GAME)
{
    global PID, Client, ProcessHandle
    if (!PID        := ProcessWait(NAME_GAME, 60))
    {
        MsgBox("Игра не найдена!")
        ExitApp()
    }
    Client          := GET_DLL_BASE(DLL_GAME, PID)
    ProcessHandle   := DllCall("OpenProcess", "int", 2035711, "char", 0, "UInt", PID, "UInt")
    return 1
}
GET_DLL_BASE(DLL_GAME, PID)
{
    static TH32CS_SNAPMODULE := 0x00000008
    static TH32CS_SNAPMODULE32 := 0x00000010
    global modBaseSize, hModule
    if (ProcessExist(PID))
    {
        if (hSnapshot := DllCall("CreateToolhelp32Snapshot", "UInt", (TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32), "UInt", PID, "Ptr"))
        {
            MODULEENTRY32 := Buffer(A_PtrSize = 8 ? 568 : 548, 0)
            NumPut("UInt", MODULEENTRY32.Size, MODULEENTRY32, 0)
            if (DllCall("Module32First", "UInt", hSnapshot, "UInt", MODULEENTRY32.Ptr))
            {
                while (DllCall("Module32Next", "UInt", hSnapshot, "UInt", MODULEENTRY32.Ptr))
                {
                    if (DLL_GAME = StrGet(MODULEENTRY32.Ptr + (A_PtrSize = 8 ? 48 : 32), 256, "CP0"))
                    {
                        modBaseAddr := NumGet(MODULEENTRY32, (A_PtrSize = 8 ? 24 : 20), "Ptr")
                        modBaseSize := NumGet(MODULEENTRY32, (A_PtrSize = 8 ? 32 : 24), "Ptr")
                        hModule     := NumGet(MODULEENTRY32, (A_PtrSize = 8 ? 40 : 32), "Ptr")
                    }
                }
            }
            DllCall("CloseHandle", "Ptr", hSnapshot)
            return modBaseAddr
        }
    }
    MsgBox("PID не найден!")
    ExitApp()
}
FAST_SEARCH_SIGNATURE_IN_MODULE(PATTERN := "", EXTRA := 0, START_ADDRESS := Client, END_ADDRESS := modBaseSize)
{
    PATTERN := StrSplit(StrReplace(RegExReplace(StrReplace(" " PATTERN, A_Space, " 0x"), A_Space, "", , 1, 1), "0x??", "-1"), A_Space)
    create_array_cpp := Buffer(PATTERN.Length * 1)
    loop PATTERN.Length
    {
        NumPut("Char", PATTERN[A_Index], create_array_cpp.Ptr, (A_Index - 1) * 1)
    }

    result := DllCall(A_PtrSize = 8 ? "Fcode64\module" : "Fcode32\module", "Ptr", ProcessHandle, "Ptr", START_ADDRESS, "Ptr", END_ADDRESS, "Ptr", create_array_cpp, "Int", PATTERN.Length, "Int", EXTRA, "CDecl")
    return result != -100 ? Format("0x{:02X}", result) : result
}
FAST_SEARCH_SIGNATURE_IN_PROCESS(PATTERN := "", EXTRA := 0)
{
    PATTERN := StrSplit(StrReplace(RegExReplace(StrReplace(" " PATTERN, A_Space, " 0x"), A_Space, "", , 1, 1), "0x??", "-1"), A_Space)
    create_array_cpp := Buffer(PATTERN.Length * 1)
    loop PATTERN.Length
    {
        NumPut("Char", PATTERN[A_Index], create_array_cpp.Ptr, (A_Index - 1) * 1)
    }
    result := DllCall(A_PtrSize = 8 ? "Fcode64\process" : "Fcode32\process", "Ptr", ProcessHandle, "Ptr", create_array_cpp, "Int", PATTERN.Length, "Int", EXTRA, "CDecl")
    return result != -100 ? Format("0x{:02X}", result) : result
}
LOAD_FAST_CODE()
{
    #DllLoad "Kernel32.dll"
    #DllLoad "*i Fcode64.dll"
    #DllLoad "*i Fcode32.dll"
    if (!DllCall("GetModuleHandle", "Str", (A_PtrSize = 8 ? "Fcode64.dll" : "Fcode32.dll")))
    {
        MsgBox("Dll: Fcode не обнаружен!")
        ExitApp()
    }
}
RUN_AS_ADMINISTRATOR(KEY := 0)
{
    if (KEY != 0)
    {
        full_command_line := DllCall("GetCommandLine", "str")
        if not (A_IsAdmin or RegExMatch(full_command_line, " /restart(?!\S)"))
        {
            try
            {
                if A_IsCompiled
                    Run '*RunAs "' A_ScriptFullPath '" /restart'
                else
                    Run '*RunAs "' A_AhkPath '" /restart "' A_ScriptFullPath '"'
            }
            ExitApp
        }
    }
}
READ_MEMORY(ADDRESS, TYPE := "UInt")
{
    if (DllCall("Kernel32\ReadProcessMemory", "UInt", ProcessHandle, "UInt", ADDRESS, TYPE "*", &uint32 := 0, "Ptr", 4, "UInt", 0))
    {
        return uint32
    }
}
WRITE_MEMORY(ADDRESS, VALUE, TYPE := "UInt")
{
    return DllCall("Kernel32\WriteProcessMemory", "UInt", ProcessHandle, "UInt", ADDRESS, TYPE "*", VALUE, "UInt", 4, "UInt *", 0)
}
INS:: Pause -1
END:: ExitApp
- добавлен вариант запуска скрипта от имени администратора
- добавлена возможность запуска скрипта AutoHotKey 32-bit
- код сканера был сильно переработан и оптимизирован
Fcode.dll (64-bit | 32-bit):
#include "pch.h"

using namespace std;

extern "C" __declspec(dllexport) int module(HANDLE PROCESS_HANDLE, LPCVOID START_ADDRESS, SIZE_T END_ADDRESS, char* PATTERN, int PATTERN_LENGTH, int EXTRA)
{
    char* buffer = new char[END_ADDRESS];
    if (ReadProcessMemory(PROCESS_HANDLE, START_ADDRESS, buffer, END_ADDRESS, NULL))
    {
        int counter = 0;
        for (int i = 0; i < END_ADDRESS; i++)
        {
            if (buffer[i] == PATTERN[0] || PATTERN[0] == -1)
            {
                for (int j = 0; j < PATTERN_LENGTH; j++)
                {
                    if (buffer[i + j] == PATTERN[j] || PATTERN[j] == -1)
                    {
                        counter++;
                        if (counter == PATTERN_LENGTH)
                        {
                            delete[] buffer;
                            return (int)START_ADDRESS + i + EXTRA;
                        }
                    }
                }
            }
            counter = 0;
        }
    }
    delete[] buffer;
    return -100;
}

extern "C" __declspec(dllexport) int process(HANDLE PROCESS_HANDLE, char* PATTERN, int PATTERN_LENGTH, int EXTRA)
{
    LPCVOID old_RegionSize = 0;
    MEMORY_BASIC_INFORMATION mbi;

    while (VirtualQueryEx(PROCESS_HANDLE, old_RegionSize, &mbi, sizeof(mbi)))
    {
        old_RegionSize = (LPVOID)((UINT_PTR)mbi.BaseAddress + mbi.RegionSize);
        if (mbi.State == MEM_COMMIT)
        {
            char* buffer = new char[mbi.RegionSize];
            if (ReadProcessMemory(PROCESS_HANDLE, (LPCVOID)mbi.BaseAddress, buffer, (SIZE_T)mbi.RegionSize, NULL))
            {
                int counter = 0;
                for (int i = 0; i < mbi.RegionSize; i++)
                {
                    if (buffer[i] == PATTERN[0] || PATTERN[0] == -1)
                    {
                        for (int j = 0; j < PATTERN_LENGTH; j++)
                        {
                            if (buffer[i + j] == PATTERN[j] || PATTERN[j] == -1)
                            {
                                counter++;
                                if (counter == PATTERN_LENGTH)
                                {
                                    delete[] buffer;
                                    return (int)mbi.BaseAddress + i + EXTRA;
                                }
                            }
                        }
                    }
                    counter = 0;
                }
            }
            delete[] buffer;
        }
    }
    return -100;
}
Основной AHK скрипт:
#SingleInstance Force
RUN_AS_ADMINISTRATOR(0)                             ; 1 = запуск скрипта от имени Администратора | 0 = обычный режим
LOAD_FAST_CODE()                                    ; Подгружает нужные .dll для работы скрипта
CONNECTING_TO_GAME("hl2.exe", "server.dll")         ; Первый параметр это процесс игры | Второй параметр это модуль игры (.exe | .dll)


;       ____________________________________________________________________________________________
;       |                                                                                           |
;       |     FAST_SEARCH_SIGNATURE_IN_PROCESS - это поиск сигнатуры по всему процессу игры         |
;       |     Пример:                                                                               |
;       |     address := FAST_SEARCH_SIGNATURE_IN_PROCESS("02 48 ?? 76 A8", 0)                      |
;       |                                                                                           |
;       |     Функция возвращает адрес                                                              |
;       |     Первый параметр       - наш паттерн                                                   |
;       |     Второй параметр       - смещенние в байтах                                            |
;       |___________________________________________________________________________________________|
;       ____________________________________________________________________________________________
;       |                                                                                           |
;       |     FAST_SEARCH_SIGNATURE_IN_MODULE - это поиск сигнатуры внутри указанного модуля игры   |
;       |     Пример:                                                                               |
;       |     address := FAST_SEARCH_SIGNATURE_IN_MODULE("02 48 ?? 76 A8", 0, 0x620000, 0x10000)    |
;       |                                                                                           |
;       |     Функция возвращает адрес                                                              |
;       |     Первый параметр       - наш паттерн                                                   |
;       |     Второй параметр       - смещенние в байтах                                            |
;       |     Третий параметр       - начальный адрес модуля в котором хранится наша сигнатура      |
;       |     Четвертый параметр    - размер модуля который мы указали в третьем параметре          |
;       |___________________________________________________________________________________________|


CONNECTING_TO_GAME(NAME_GAME, DLL_GAME)
{
    global PID, Client, ProcessHandle
    if (!PID        := ProcessWait(NAME_GAME, 60))
    {
        MsgBox("Игра не найдена!")
        ExitApp()
    }
    Client          := GET_DLL_BASE(DLL_GAME, PID)
    ProcessHandle   := DllCall("OpenProcess", "int", 2035711, "char", 0, "UInt", PID, "UInt")
    return 1
}
GET_DLL_BASE(DLL_GAME, PID)
{
    static TH32CS_SNAPMODULE := 0x00000008
    static TH32CS_SNAPMODULE32 := 0x00000010
    global modBaseSize, hModule
    if (ProcessExist(PID))
    {
        if (hSnapshot := DllCall("CreateToolhelp32Snapshot", "UInt", (TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32), "UInt", PID, "Ptr"))
        {
            MODULEENTRY32 := Buffer(A_PtrSize = 8 ? 568 : 548, 0)
            NumPut("UInt", MODULEENTRY32.Size, MODULEENTRY32, 0)
            if (DllCall("Module32First", "UInt", hSnapshot, "UInt", MODULEENTRY32.Ptr))
            {
                while (DllCall("Module32Next", "UInt", hSnapshot, "UInt", MODULEENTRY32.Ptr))
                {
                    if (DLL_GAME = StrGet(MODULEENTRY32.Ptr + (A_PtrSize = 8 ? 48 : 32), 256, "CP0"))
                    {
                        modBaseAddr := NumGet(MODULEENTRY32, (A_PtrSize = 8 ? 24 : 20), "Ptr")
                        modBaseSize := NumGet(MODULEENTRY32, (A_PtrSize = 8 ? 32 : 24), "Ptr")
                        hModule     := NumGet(MODULEENTRY32, (A_PtrSize = 8 ? 40 : 32), "Ptr")
                    }
                }
            }
            DllCall("CloseHandle", "Ptr", hSnapshot)
            return modBaseAddr
        }
    }
    MsgBox("PID не найден!")
    ExitApp()
}
FAST_SEARCH_SIGNATURE_IN_MODULE(PATTERN := "", EXTRA := 0, START_ADDRESS := Client, END_ADDRESS := modBaseSize)
{
    PATTERN := StrSplit(StrReplace(RegExReplace(StrReplace(" " PATTERN, A_Space, " 0x"), A_Space, "", , 1, 1), "0x??", "-1"), A_Space)
    create_array_cpp := Buffer(PATTERN.Length * 1)
    loop PATTERN.Length
    {
        NumPut("Char", PATTERN[A_Index], create_array_cpp.Ptr, (A_Index - 1) * 1)
    }

    result := DllCall(A_PtrSize = 8 ? "Fcode64\module" : "Fcode32\module", "Ptr", ProcessHandle, "Ptr", START_ADDRESS, "Ptr", END_ADDRESS, "Ptr", create_array_cpp, "Int", PATTERN.Length, "Int", EXTRA, "CDecl")
    return result != -100 ? Format("0x{:02X}", result) : result
}
FAST_SEARCH_SIGNATURE_IN_PROCESS(PATTERN := "", EXTRA := 0)
{
    PATTERN := StrSplit(StrReplace(RegExReplace(StrReplace(" " PATTERN, A_Space, " 0x"), A_Space, "", , 1, 1), "0x??", "-1"), A_Space)
    create_array_cpp := Buffer(PATTERN.Length * 1)
    loop PATTERN.Length
    {
        NumPut("Char", PATTERN[A_Index], create_array_cpp.Ptr, (A_Index - 1) * 1)
    }
    result := DllCall(A_PtrSize = 8 ? "Fcode64\process" : "Fcode32\process", "Ptr", ProcessHandle, "Ptr", create_array_cpp, "Int", PATTERN.Length, "Int", EXTRA, "CDecl")
    return result != -100 ? Format("0x{:02X}", result) : result
}
LOAD_FAST_CODE()
{
    #DllLoad "Kernel32.dll"
    #DllLoad "*i Fcode64.dll"
    #DllLoad "*i Fcode32.dll"
    if (!DllCall("GetModuleHandle", "Str", (A_PtrSize = 8 ? "Fcode64.dll" : "Fcode32.dll")))
    {
        MsgBox("Dll: Fcode не обнаружен!")
        ExitApp()
    }
}
RUN_AS_ADMINISTRATOR(KEY := 0)
{
    if (KEY != 0)
    {
        full_command_line := DllCall("GetCommandLine", "str")
        if not (A_IsAdmin or RegExMatch(full_command_line, " /restart(?!\S)"))
        {
            try
            {
                if A_IsCompiled
                    Run '*RunAs "' A_ScriptFullPath '" /restart'
                else
                    Run '*RunAs "' A_AhkPath '" /restart "' A_ScriptFullPath '"'
            }
            ExitApp
        }
    }
}
READ_MEMORY(ADDRESS, TYPE := "UInt")
{
    if (DllCall("Kernel32\ReadProcessMemory", "UInt", ProcessHandle, "UInt", ADDRESS, TYPE "*", &uint32 := 0, "Ptr", 4, "UInt", 0))
    {
        return uint32
    }
}
WRITE_MEMORY(ADDRESS, VALUE, TYPE := "UInt")
{
    return DllCall("Kernel32\WriteProcessMemory", "UInt", ProcessHandle, "UInt", ADDRESS, TYPE "*", VALUE, "UInt", 4, "UInt *", 0)
}
INS:: Pause -1
END:: ExitApp

Пример использования:
- Подключаем игру CONNECTING_TO_GAME("НАЗВАНИЕ ПРОЦЕССА ИГРЫ - csgo.exe (если брать кс)", "МОДУЛЬ ИГРЫ - client.dll (если брать кс)")
- Теперь давайте найдем адрес по сигнатуре которую мы нашли выше, первый способ: DayMond_P := FAST_SEARCH_SIGNATURE_IN_PROCESS("44 61 79 4D 6F 6E 64 00")
- Результат переменной DayMond_P = 0x083E7DB0, это наш адрес
- Второй способ, более быстрый, но для него необходимо знать в каком модуле находится сигнатура, для csgo вы можете использовать hazedamper, там написано к какому модулю принадлежит сигнатура (dwLocalPlayer находится в client.dll), пример: DayMond_M := FAST_SEARCH_SIGNATURE_IN_MODULE("44 61 79 4D 6F 6E 64 00", 0, 0x82D0000, 0x2D8000)
- Результат переменной DayMond_M = 0x083E7DB0, это также наш адрес.

Бывают случаи, когда вы нашли сигнатуру, но она меняет некоторые свои байты. Например, вот сигнатура: CF 82 E5 22 E4 8C 11 46. К примеру, байты 82 и E5 меняются. В таком случае мы записываем сигнатуру таким образом: CF ?? ?? 22 E4 8C 11 46. Точно также пишем и в наш скрипт. Он понимает, что это динамическая сигнатура и что байты, помеченные как ??, он пропускает при поиске. Поэтому рекомендую научиться искать сигнатуры с помощью программы OllyDbg и специального плагина, который создает маску уже с готовыми ?? вместо меняющихся байтов.

Как мы видим с помощью встроенной функции #DllLoad в AHK, мы можем загружать в .dll файлы в скрипт и выполнять более сложную математику уже на стороне C++. Это позволяет сократить время поиска нахождения нашего паттерна во много раз. Можно отказаться от такого лайфхака и вместо этого доставать байты по одному из буфера при помощи встроенной функции NumGet, но это займет намного больше времени, в игре Need for Speed - Most Wanted более 1600 страниц памяти, на одну страницу при помощи NumGet уходит около 3-х секунд, думаю считать разницу не стоит:tearsofjoy: К примеру, всеми любимый класс памяти RHCP (classMemory), использует вместо загрузки .dll в AHK функцию Mcode (machine code). Это тоже код C++ который загружен непосредственно уже в сам процессор при помощи AHK. Я пытался разобраться в Mcode, но эта тема уже для реальных мастеров, нам с вами и этого будет за глаза, по скорости Mcode будет немного быстрее:disappointed:

Надеюсь гайд вам понравился и вы поняли как пользоваться моим скриптом, если остались вопросы - пишите, постараюсь помочь.
Всем удачи и мирного неба над головой зайчики!
 
Последнее редактирование:
Начинающий
Статус
Оффлайн
Регистрация
19 Фев 2019
Сообщения
31
Реакции[?]
1
Поинты[?]
0
Так и не понял, в чем профит кода, если адрес сигнатуры можно найти и с помощью того же CE или OllyDBG? И как я понял, возваращет эта функция адрес, в формате строки, а не структуру с адресами, а если будет две одинаковые сигнатуры, что более чем вероятно при поиске целочисельных типов? Исходя из кода я предполагаю, что вернется адрес первой попавшейся? Автообновление оффсетов это конечно круто, но как оно тут реализовано?
 
Начинающий
Статус
Оффлайн
Регистрация
9 Дек 2021
Сообщения
244
Реакции[?]
23
Поинты[?]
18K
Всем привет, друзья! Наконец-то, у меня получилось реализовать собственный сканнер сигнатур (или, в простонародье, автоматические обновления оффсетов) на языке AutoHotKey.

Как вы можете помнить из прошлых моих тем, AutoHotKey это скриптовой язык программирования, который даст просраться другим языкам в геймхакинге при правильном его использовании. Плюсом AutoHotKey является его простота использования и неплохая скорость выполнения кода. По моим наблюдениям, он быстрее Python и даже в некоторых случаях по скорости обходит C#. Но, так как это всё-таки скриптовой язык, его скорость выполнения кода не сопоставима с легендарным C++. Казалось бы, хвалил - хвалил, но по итогам сам же его слил. Но что, если я скажу, что AutoHotKey может работать в связке с C++? Ооо, это уже совсем меняет суть вопроса, не так ли? Но вы скажете: "Чел, в чём понт в твоём AutoHotKey, если его фишка в простоте написании кода?" А я в таком случае отвечу: "Простота написания кода остаётся прежней, с помощью всего одной функции в AutoHotKey, мы можем загрузить в наш AHK код на C++, который не потребует особо много мозгов."

Заинтересовал? Погнали!

Содержимое темы:
1. Кратное объяснение, определения: массив байт, сигнатура, паттерн
2. Поиск сигнатуры с помощью стороннего софта (CheatEngine)
3. Пример нахождение сигнатуры с помощью готовой функции в скрипте


ВНИМАНИЕ! Тема предназначена для аудитории, которая разбирается в Cheat Engine или OllyDbg. Прежде чем вы начнете изучать материал, я настоятельно рекомендую вам изучить хотя бы основы Cheat Engine, а также хорошо ориентироваться в коде AutoHotkey. Если вам не интересен принцип работы скрипта, готовый код будет прилеплен в конце этой темы.

Кратное объяснение, определения: массив байт, сигнатура.

Если по-простому, массив байт - это последовательность какой-то информации в игре или программе. В таком массиве может храниться: наш баланс в игре, количество ХП, патронов и т.д. Своего рода такой массив байт - это и есть наша сигнатура. Массив байт | сигнатура, выглядит так: 59 61 20 6C 6F 68 20 65 62 61 6E 69 79 00. В данном примере, этот массив байт хранит в себе какой-то текст в игре. Паттерн - это массив байт | сигнатура, которую мы ищем с помощью разных программ, в нашем случае это скрипт. Наша задача - получить адрес на этот текст, но проблема в том, что после каждого перезахода в игру нам приходится заново искать адрес, так как игра загружает его в другое место под другим адресом. В такой ситуации логично будет создать указатель на этот адрес с помощью программы CheatEngine и это будет правильно. Но, когда разработчики выкатят какой-то патч | обнову указатель перестанет работать. По этому нам на помощь приходит сканнер сигнатур. Запомните, адрес может манятся сколько угодно, но текст как неотъемлемый элемент игры, будет находится практически всегда неизменным.

Теория конечно хорошо, но практика лучше. Посмотрим на примере игры Need for Speed - Most Wanted (2005 года). Задача, найти мой ник в игре и получить постоянную сигнатуру на мой ник.
1) Открываем CheatEngine и подключаем нашу игру.
2) В строке Value Type вместо 4 Bytes выбираем String (так как мы ищем текст, а не значение).
3) В строке поиска пишу свой ник DayMond, и нажимаю поиск.
4) Нашло только один адрес, кликаю два раза по нему, после, правой кнопкой по адресу который перенесли в нижнюю таблицу:
Посмотреть вложение 234116
5) Выбираем пункт Browse this memory region или комбинация клавиш CTRL + B.
6) Смотрим ниже, видим большое количество массивов байтов. Нас интересует самый верхний адрес: 083E7DB0 и его содержимое в виде сигнатуры:
Посмотреть вложение 234117
Готово! Мы нашли сигнатуру для поиска. Осталось эти данные записать в AHK скрипт и запустить его. После чего мы получим наш адрес - 0x083E7DB0.


Давайте для начала скачаем AutoHotKey второй версии необходимый для работы моего скрипта, который недавно релизнули разработчики у себя на официальном сайте:
Пожалуйста, авторизуйтесь для просмотра ссылки.
(no ad).

После того, как мы скачали и установили AHK v2. Создаём простой скрипт с таким содержимым - MsgBox("Привет!").
Теперь нам нужно заставить его запускаться на 64\32 bit.
Для этого делаем следующее:
1) по только что созданному скрипту, нажимаем правой кнопкой мыши ---> Свойства ---> Изменить... ---> "Листаем в самый низ" ---> Дополнительно ---> Найти другое приложение на этом ПК ---> "Ищем папку куда установили AHK" ---> "Открываем ее" ---> v2 ---> Открыть ---> AutoHotkey32 ---> Открыть.
2) теперь повторяем те же действия, только в папке v2 выбираем AutoHotkey64, после чего открываем.
После всех манипуляций, когда мы нажимаем Изменить, у нас на выбор появляется запуск AHK в 64 и 32 бита (осторожно, нам нужны ahk с новой иконкой). Для наших дел втыкаем AutoHotKey 64-bit и запускаем созданный файл, если вылезло слово: Привет! Значит мы все сделали правильно.

И так, надеюсь у вас все получилось. Самое время испытать наш скрипт. Переходим на мой
Пожалуйста, авторизуйтесь для просмотра ссылки.
(no ad) и качаем два .dll файла (
Пожалуйста, авторизуйтесь для просмотра ссылки.
и
Пожалуйста, авторизуйтесь для просмотра ссылки.
). Это и есть код написанный на языке С++, который выполняет математику.
Вот содержимое .dll файла для скептиков:
C++:
#include "pch.h"

using namespace std;

extern "C" __declspec(dllexport) int add(long long data_buf, int buf_length, char* pattern, int pattern_length)
{
    char* buffer = (char*)data_buf;
    int counter = 0;
    for (long long i = 0; i < buf_length; i++)
    {
        if (buffer[i] == pattern[0] || pattern[0] == -1)
        {
            for (long long j = 0; j < pattern_length; j++)
            {
                if (buffer[i + j] == pattern[j] || pattern[j] == -1)
                {
                    counter++;
                    if (counter == pattern_length)
                    {
                        return i;
                    }
                }
            }
        }
        counter = 0;
    }
    return -100;
}
Вот основной AHK скрипт:
Код:
#SingleInstance Force
LOAD_FAST_CODE()
CONNECTING_TO_GAME("speed.exe", "speed.exe")    ; Первый параметр - название процесса игры, второй - dll или exe в котором хранятся данные игрока (LocalPlayer)

; FAST_SEARCH_SIGNATURE_IN_PROCESS ищет сигнатуру во всех модулях игры о чем говорят слова IN_PROCESS
; работает почти моментально, использовать если не известно в каком модуле храниться сига.
DayMond_P := FAST_SEARCH_SIGNATURE_IN_PROCESS("44 61 79 4D 6F 6E 64 00")
; FAST_SEARCH_SIGNATURE_IN_MODULE ищет сигнатуру к конкретном модуле, если такой известен
; работает моментально в отличии от FAST_SEARCH_SIGNATURE_IN_PROCESS.
; Первый параметр - сигнатура, второй - extra (сколько добавить байтов к адресу (hazedamper)), третий - модуль в котором храниться наша сига, четвёртый - размер модуля.
DayMond_M := FAST_SEARCH_SIGNATURE_IN_MODULE("44 61 79 4D 6F 6E 64 00", 0, 0x82D0000, 0x2D8000)
MsgBox(DayMond_P "   " DayMond_M) ; 0x083E7DB0   0x083E7DB0
ExitApp()


CONNECTING_TO_GAME(NAME_GAME, DLL_GAME)
{
    global PID, Client, ProcessHandle
    if (!PID        := ProcessWait(NAME_GAME, 60))
    {
        MsgBox("Игра не найдена!")
        ExitApp()
    }
    Client          := GET_DLL_BASE(DLL_GAME, PID)
    ProcessHandle   := DllCall("OpenProcess", "int", 2035711, "char", 0, "UInt", PID, "UInt")
    return 1
}

GET_DLL_BASE(DLL_GAME, PID)
{
    static TH32CS_SNAPMODULE := 0x00000008
    static TH32CS_SNAPMODULE32 := 0x00000010
    global modBaseSize, hModule

    if (ProcessExist(PID))
    {
        if (hSnapshot := DllCall("CreateToolhelp32Snapshot", "UInt", (TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32), "UInt", PID, "Ptr"))
        {
            MODULEENTRY32 := Buffer(A_PtrSize = 8 ? 568 : 548, 0)
            NumPut("UInt", MODULEENTRY32.Size, MODULEENTRY32, 0)
            if (DllCall("Module32First", "UInt", hSnapshot, "UInt", MODULEENTRY32.Ptr))
            {
                while (DllCall("Module32Next", "UInt", hSnapshot, "UInt", MODULEENTRY32.Ptr))
                {
                    if (DLL_GAME = StrGet(MODULEENTRY32.Ptr + (A_PtrSize = 8 ? 48 : 32), 256, "CP0"))
                    {
                        modBaseAddr := NumGet(MODULEENTRY32, (A_PtrSize = 8 ? 24 : 20), "Ptr")
                        modBaseSize := NumGet(MODULEENTRY32, (A_PtrSize = 8 ? 32 : 24), "Ptr")
                        hModule     := NumGet(MODULEENTRY32, (A_PtrSize = 8 ? 40 : 32), "Ptr")
                    }
                }
            }
            DllCall("CloseHandle", "Ptr", hSnapshot)
            return modBaseAddr
        }
    }
    MsgBox("PID не найден!")
    ExitApp()
}

FAST_SEARCH_SIGNATURE_IN_MODULE(PATTERN := "", EXTRA := 0, START_ADDRESS := Client, END_ADDRESS := modBaseSize)
{
    space_PATTERN := " " PATTERN
    format_PATTERN := StrReplace(RegExReplace(StrReplace(space_PATTERN, A_Space, " 0x"), A_Space, "", , 1, 1), "0x??", "-1")
    PATTERN := StrSplit(format_PATTERN, A_Space)

    create_array_cpp := Buffer(PATTERN.Length * 1)
    loop PATTERN.Length
    {
        NumPut("Char", PATTERN[A_Index], create_array_cpp.Ptr, (A_Index - 1) * 1)
    }

    bytes := Buffer(END_ADDRESS, 0)
    if (DllCall("Kernel32\ReadProcessMemory", "UInt", ProcessHandle, "UInt", START_ADDRESS, "Ptr", bytes, "Ptr", END_ADDRESS, "UInt", 0))
    {
        result := DllCall(A_PtrSize = 8 ? "Fcode64\add" : "Fcode32\add", "Ptr", bytes, "UInt", END_ADDRESS, "Ptr", create_array_cpp, "Int", PATTERN.Length)
        if (result != -100)
        {
            return Format("0x{:02X}", START_ADDRESS + result + EXTRA)
        }
        else
        {
            return -100
        }
    }
}

FAST_SEARCH_SIGNATURE_IN_PROCESS(PATTERN := "", EXTRA := 0)
{
    static MEM_COMMIT := 0x1000
    static MEM_FREE := 0x10000
    static MEM_RESERVE := 0x2000

    _MEMORY_BASIC_INFORMATION := Buffer(A_PtrSize = 8 ? 48 : 28, 0)

    old_RegionSize := 0
    RegionSize := 0

    loop
    {
        if (DllCall("VirtualQueryEx", "Ptr", ProcessHandle, "Ptr", old_RegionSize += RegionSize, "Ptr", _MEMORY_BASIC_INFORMATION, "Ptr", _MEMORY_BASIC_INFORMATION.Size))
        {
            BaseAddress := Format("0x{:02X}", NumGet(_MEMORY_BASIC_INFORMATION, 0, "Int64"))
            AllocationBase := Format("0x{:02X}", NumGet(_MEMORY_BASIC_INFORMATION, 8, "Int64"))
            RegionSize := Format("0x{:02X}", NumGet(_MEMORY_BASIC_INFORMATION, 24, "Int64"))
            State := Format("0x{:02X}", NumGet(_MEMORY_BASIC_INFORMATION, 32, "UInt"))

            if (State == MEM_COMMIT)
            {
                result := FAST_SEARCH_SIGNATURE_IN_MODULE(PATTERN, EXTRA, BaseAddress, RegionSize)
                if (result != -100 and result != "")
                {
                    return result
                }
            }
        }
    }
}

LOAD_FAST_CODE()
{
    #DllLoad "Kernel32.dll"
    #DllLoad "*i Fcode64.dll"
    #DllLoad "*i Fcode32.dll"

    if (!DllCall("GetModuleHandle", "Str", (A_PtrSize = 8 ? "Fcode64.dll" : "Fcode32.dll")))
    {
        MsgBox("Dll: Fcode не обнаружен!")
        ExitApp()
    }
}

READ_MEMORY(ADDRESS, TYPE := "UInt")
{
    if (DllCall("Kernel32\ReadProcessMemory", "UInt", ProcessHandle, "UInt", ADDRESS, TYPE "*", &uint32 := 0, "Ptr", 4, "UInt", 0))
    {
        return uint32
    }
}

WRITE_MEMORY(ADDRESS, VALUE, TYPE := "UInt")
{
    return DllCall("Kernel32\WriteProcessMemory", "UInt", ProcessHandle, "UInt", ADDRESS, TYPE "*", VALUE, "UInt", 4, "UInt *", 0)
}

INS:: Pause -1
END:: ExitApp
Пример использования:
- Подключаем игру CONNECTING_TO_GAME("НАЗВАНИЕ ПРОЦЕССА ИГРЫ - csgo.exe (если брать кс)", "МОДУЛЬ ИГРЫ - client.dll (если брать кс)")
- Теперь давайте найдем адрес по сигнатуре которую мы нашли выше, первый способ: DayMond_P := FAST_SEARCH_SIGNATURE_IN_PROCESS("44 61 79 4D 6F 6E 64 00")
- Результат переменной DayMond_P = 0x083E7DB0, это наш адрес
- Второй способ, более быстрый, но для него необходимо знать в каком модуле находится сигнатура, для csgo вы можете использовать hazedamper, там написано к какому модулю принадлежит сигнатура (dwLocalPlayer находится в client.dll), пример: DayMond_M := FAST_SEARCH_SIGNATURE_IN_MODULE("44 61 79 4D 6F 6E 64 00", 0, 0x82D0000, 0x2D8000)
- Результат переменной DayMond_M = 0x083E7DB0, это также наш адрес.

Бывают случаи, когда вы нашли сигнатуру, но она меняет некоторые свои байты. Например, вот сигнатура: CF 82 E5 22 E4 8C 11 46. К примеру, байты 82 и E5 меняются. В таком случае мы записываем сигнатуру таким образом: CF ?? ?? 22 E4 8C 11 46. Точно также пишем и в наш скрипт. Он понимает, что это динамическая сигнатура и что байты, помеченные как ??, он пропускает при поиске. Поэтому рекомендую научиться искать сигнатуры с помощью программы OllyDbg и специального плагина, который создает маску уже с готовыми ?? вместо меняющихся байтов.

Как мы видим с помощью встроенной функции #DllLoad в AHK, мы можем загружать в .dll файлы в скрипт и выполнять более сложную математику уже на стороне C++. Это позволяет сократить время поиска нахождения нашего паттерна во много раз. Можно отказаться от такого лайфхака и вместо этого доставать байты по одному из буфера при помощи встроенной функции NumGet, но это займет намного больше времени, в игре Need for Speed - Most Wanted более 1600 страниц памяти, на одну страницу при помощи NumGet уходит около 3-х секунд, думаю считать разницу не стоит:tearsofjoy: К примеру, всеми любимый класс памяти RHCP (classMemory), использует вместо загрузки .dll в AHK функцию Mcode (machine code). Это тоже код C++ который загружен непосредственно уже в сам процессор при помощи AHK. Я пытался разобраться в Mcode, но эта тема уже для реальных мастеров, нам с вами и этого будет за глаза, по скорости Mcode будет немного быстрее:disappointed:

Надеюсь гайд вам понравился и вы поняли как пользоваться моим скриптом, если остались вопросы - пишите, постараюсь помочь.
Всем удачи и мирного неба над головой зайчики!
выложи на гит или одним архивом
 
Пользователь
Статус
Оффлайн
Регистрация
30 Апр 2019
Сообщения
143
Реакции[?]
34
Поинты[?]
5K
Так и не понял, в чем профит кода, если адрес сигнатуры можно найти и с помощью того же CE или OllyDBG? И как я понял, возваращет эта функция адрес, в формате строки, а не структуру с адресами, а если будет две одинаковые сигнатуры, что более чем вероятно при поиске целочисельных типов? Исходя из кода я предполагаю, что вернется адрес первой попавшейся? Автообновление оффсетов это конечно круто, но как оно тут реализовано?
Сигнатура это масив байт. С помощью CheatEngine или OllyDBG ты ищешь сигнатуру которая не имеет аналогов, для этого ты можешь захватить ближайшие инструкции игры или программы и уже в параметр EXTRA передать нужное количество байт чтобы в конечном итоге ты получил адрес на интересующий код. Также, динамические байты нужно заменить на ??. Рекомендую проверять сигнатуру с помощью OllyDBG с использованием специального плагина - Sig Maker. В OllyDBG переходишь по адресу ---> Выделяешь инструкции ---> Правой кнопкой мыши ---> Make Sig ---> Test Sig ---> Scan. Потом смотришь на сколько твоя сигнатура уникальна, если нашелся только один адрес - всё сделал правильно. Подгоняешь результат сканирования сигнатуры с маской под мой скрипт.
Например:
OllyDBG Signature: \x89\x00\x04\x0B\xFF\x74\x1E
OllyDBG Mask: x?xxxxx

Вот так: "89 ?? 04 0B FF 74 1E"

Всё, поздравляю, ты получил почти вечный "указатель" на правильный адрес. Больше тебе не нужно будет искать его с помощью CheatEngine или OllyDBG. Если ты не разобрался, можем разобрать это на твоем примере или на моём.
выложи на гит или одним архивом
В теме уже есть все ссылки:
Пожалуйста, авторизуйтесь для просмотра ссылки.

И кстати друзья. Периодически буду дорабатывать свой код, так что не забывайте время от времени смотреть на дату изменения темы. Моя задача сделать код ещё более быстрым. В голову пришло ещё пару задвигов, я думаю это возможно реализовать.
 
Последнее редактирование:
vk.com/ahkcsgocheat
Пользователь
Статус
Оффлайн
Регистрация
21 Апр 2020
Сообщения
380
Реакции[?]
64
Поинты[?]
2K
Например:
OllyDBG Signature: \x89\x00\x04\x0B\xFF\x74\x1E
OllyDBG Mask: x?xxxxx
Всё, поздравляю, ты получил почти вечный "указатель" на правильный адрес
Не-а, нужно еще пропустить один байт +1, т.е. дописать к \x89\x00\x04\x0B\xFF\x74\x1E, а вообще,
старую тему поднял. По-нормальному, нужно без dll, и RHCP находить адреса. Есть чeловек, который пытался такое сделать:
Пожалуйста, авторизуйтесь для просмотра ссылки.
но с багами там все.
 
Последнее редактирование:
Пользователь
Статус
Оффлайн
Регистрация
30 Апр 2019
Сообщения
143
Реакции[?]
34
Поинты[?]
5K
Не-а, нужно еще пропустить один байт (код операции называется) +1, т.е. дописать к \x89\x00\x04\x0B\xFF\x74\x1E, а вообще,
страшную тему поднял. По-нормальному нужно без dll, и RHCP находить адреса. Есть чeловек, который пытался такое сделать:
Пожалуйста, авторизуйтесь для просмотра ссылки.
но с багами там все.
Ты вероятнее всего не понял сути моего примера. В примере выше, я показал как получить, к примеру, адрес на счётчик патронов, а не саму игровую функцию к которой обращаются другие функции, хотя с помощью этого скрипта, тоже можно с легкость выключить математику подсчета патронов для NPC указав байты на эту игровую функцию, как я это сделал в Half - Life 2. Параметр EXTRA нужен не всегда. Лично я нахожу адреса не использующий этот параметр. К примеру, если брать сигнатуру из hazedumper, в некоторых сигнатурах используется этот параметр, по этому я его добавил сюда для удобства. По-нормальному, нужно использовать Mcode, но как я и писал выше в этой теме, я не разобрался как его приспособить здесь и ссылку что ты мне дал, поверь, прежде чем выкладывать этот гайд сюда, я прошуршал все темы связанные с ним. А что на счёт моей реализации, скажу лишь то, что я сделал это так как вижу сам. Учитывая отсутствие изобилия AHK скриптов на эту тематику, а в особенности на данном форуме, я могу сделать вывод, что это очень даже неплохая реализация паттерн сканера как для любителя.
 
vk.com/ahkcsgocheat
Пользователь
Статус
Оффлайн
Регистрация
21 Апр 2020
Сообщения
380
Реакции[?]
64
Поинты[?]
2K
Да понял все я, если бы ты таким способов сделал, например, netvarmanager для cs:go - (других способов нет просто, или их очень сложно сделать на ахк), тогда да, смысл был бы, и то, я бы подумал, т.к. все-таки это костыль.
я не разобрался как его приспособить здесь и ссылку что ты мне дал
Я проверял, для блокнота все работает, для других приложений нужно оптимизировать код просто. Я лично остановился на этом, дальше не лез уже.

Учитывая отсутствие изобилия AHK скриптов на эту тематику
почему отсутствие :confused:, вот моя статья, например, на эту тему:
Пожалуйста, авторизуйтесь для просмотра ссылки.
я ее обновляю тоже...

неплохая реализация паттерн сканера как для любителя.
Да, для новичков будет интересно конечно и тех, кто хочет учиться ахк, но не "полетит" ли этот сканер при обновах игры, или, например для какой-нибудь игры вообще работать не будет, вот так взять этот сканер вместо RHCP, имхо не очень идея..
 
Последнее редактирование:
Пользователь
Статус
Оффлайн
Регистрация
30 Апр 2019
Сообщения
143
Реакции[?]
34
Поинты[?]
5K
Да понял все я, если бы ты таким способов сделал, например, netvarmanager для cs:go - (других способов нет просто, или их очень сложно сделать), тогда да, смысл был бы, и то, я бы подумал, т.к. все-таки это костыль.

Я проверял, для блокнота все работает, для других приложений нужно оптимизировать код просто. Я лично остановился на этом, дальше не лез уже, если тому разработчику нет дела для своего кода, то ты, или я, например, не вытянешь уже или долго долго разбираться нужно.


почему отсутствие :confused:, вот моя статья, например, на эту тему:
Пожалуйста, авторизуйтесь для просмотра ссылки.
я его обновляю тоже...


Да, для новичков будет интересно конечно и тех, кто хочет учиться ахк, спорить не буду, но не "полетит" ли этот сканер при обновах игры, или, например для какой-нибудь игры вообще работать не будет, вот так взять этот сканер вместо RHCP, согласись не очень идея..
Мой сканер как и другие работающие по похожему принципу, могут полететь только в случае, если мы не сможем получить дескриптор процесса игры. Но это Valve, если говорить о csgo, не скоро они введут такую защиту. К RHCP, претензий лично у меня нет, этот человек уже в утробе матери знал AHK лучше нас всех. Но учитывая, что я не первый день делаю скрипты на AHK, мне лично было сложно ганять по коду состоящего из 1438 строк чтобы понять как работает хотя бы одна из его ф-й. По этому, моей целью было тупо взять основные для нас ф-и (скан по модулю и скан по всему процессу) и по простому передать суть для остальных. А каждый уже сам для себя решит что ему использовать, класс RHCP или мой скрипт. Лично я так мыслю, AHK V1 уже морально устарел, если найдется человек который конвертирует класс RHCP на AHK V2 будет не плохо, но добавлять к радархаку (например) состоящему из пару строк еще примерно 1000, ну такое себе удовольствие.
 
Сверху Снизу