-
Автор темы
- #1
ПОСЛЕДНИЕ ИЗМЕНЕНИЯ - 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) Нашло только один адрес, кликаю два раза по нему, после, правой кнопкой по адресу который перенесли в нижнюю таблицу:
5) Выбираем пункт Browse this memory region или комбинация клавиш CTRL + B.
6) Смотрим ниже, видим большое количество массивов байтов. Нас интересует самый верхний адрес: 083E7DB0 и его содержимое в виде сигнатуры:
Готово! Мы нашли сигнатуру для поиска. Осталось эти данные записать в AHK скрипт и запустить его. После чего мы получим наш адрес - 0x083E7DB0.
Давайте для начала скачаем AutoHotKey второй версии необходимый для работы моего скрипта, который недавно релизнули разработчики у себя на официальном сайте:
После того, как мы скачали и установили AHK v2. Создаём простой скрипт с таким содержимым -
Теперь нам нужно заставить его запускаться на 64\32 bit.
Для этого делаем следующее:
1) по только что созданному скрипту, нажимаем правой кнопкой мыши ---> Свойства ---> Изменить... ---> "Листаем в самый низ" ---> Дополнительно ---> Найти другое приложение на этом ПК ---> "Ищем папку куда установили AHK" ---> "Открываем ее" ---> v2 ---> Открыть ---> AutoHotkey32 ---> Открыть.
2) теперь повторяем те же действия, только в папке v2 выбираем AutoHotkey64, после чего открываем.
После всех манипуляций, когда мы нажимаем Изменить, у нас на выбор появляется запуск AHK в 64 и 32 бита (осторожно, нам нужны ahk с новой иконкой). Для наших дел втыкаем AutoHotKey 64-bit и запускаем созданный файл, если вылезло слово: Привет! Значит мы все сделали правильно.
И так, надеюсь у вас все получилось. Самое время испытать наш скрипт. Переходим на мой
Исходники:
Пример использования:
- Подключаем игру
- Теперь давайте найдем адрес по сигнатуре которую мы нашли выше, первый способ:
- Результат переменной DayMond_P = 0x083E7DB0, это наш адрес
- Второй способ, более быстрый, но для него необходимо знать в каком модуле находится сигнатура, для csgo вы можете использовать hazedamper, там написано к какому модулю принадлежит сигнатура (dwLocalPlayer находится в client.dll), пример:
- Результат переменной 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-х секунд, думаю считать разницу не стоит К примеру, всеми любимый класс памяти RHCP (classMemory), использует вместо загрузки .dll в AHK функцию Mcode (machine code). Это тоже код C++ который загружен непосредственно уже в сам процессор при помощи AHK. Я пытался разобраться в Mcode, но эта тема уже для реальных мастеров, нам с вами и этого будет за глаза, по скорости Mcode будет немного быстрее
Надеюсь гайд вам понравился и вы поняли как пользоваться моим скриптом, если остались вопросы - пишите, постараюсь помочь.
Всем удачи и мирного неба над головой зайчики!
- переработан оптимизирован 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) Нашло только один адрес, кликаю два раза по нему, после, правой кнопкой по адресу который перенесли в нижнюю таблицу:
5) Выбираем пункт Browse this memory region или комбинация клавиш CTRL + B.
6) Смотрим ниже, видим большое количество массивов байтов. Нас интересует самый верхний адрес: 083E7DB0 и его содержимое в виде сигнатуры:
Готово! Мы нашли сигнатуру для поиска. Осталось эти данные записать в 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
- добавлен новый (необязательный) параметр для 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 раз
- функции поиска паттерна ускорены в 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
- код сканера был сильно переработан и оптимизирован
- добавлена возможность запуска скрипта 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-х секунд, думаю считать разницу не стоит К примеру, всеми любимый класс памяти RHCP (classMemory), использует вместо загрузки .dll в AHK функцию Mcode (machine code). Это тоже код C++ который загружен непосредственно уже в сам процессор при помощи AHK. Я пытался разобраться в Mcode, но эта тема уже для реальных мастеров, нам с вами и этого будет за глаза, по скорости Mcode будет немного быстрее
Надеюсь гайд вам понравился и вы поняли как пользоваться моим скриптом, если остались вопросы - пишите, постараюсь помочь.
Всем удачи и мирного неба над головой зайчики!
Последнее редактирование: