Пользователь
- Статус
- Оффлайн
- Регистрация
- 2 Ноя 2024
- Сообщения
- 284
- Реакции
- 33
Всем привет, видя что люди не могут найти паттерны, да и некоторые люди которые сами делали гайды, так их и не выпустили, я выпущу свой гайд по нахождению функций, и не только функций, а по всему.
Глава 1: Начало.
Глава 2: Ввод в курс дела.
Глава 2.1: Коды, как получать исходники функций и так далее.
Глава 2.2: Псевдокод функции, не магия, а перевод с кошачьего.
Глава 2.3: WorldToScreen, ViewMatrix, EntityList — поговорим об этом
Глава 3: Как загрузить client.dll в IDA Pro правильно
Глава 3.1: Как находить поля классов (Offsets) через строки
Глава 3.2: Нахождение глобальных оффсетов (dwEntityList, dwLocalPlayerPawn и другие)
Глава 3.3: Создание паттернов (сигнатур) для функций
Финал гайда
Глава 1: Начало.
Для начала, что нам нужно:
1. IDA Pro (Crack или Официалку)
2. Class Informer (Plugin)
3. Sigmaker (Для создания паттернов).
Полезные сайты для создания читов:
1.
2. cs2.poggu.me (noad) - подробный гайд о конварах, кнопках, и тд.
Ниже приведены все ссылки и инструкции, если там вирусы или что то такое, я за это не ручаюсь.
1. IDA Pro:
2. Class Informer:
3. Sigmaker:
Учтите, что наша версия IDA: 8.3 , не 9.0 или другая (Плагины из других версий под нашу не подойдут).
Так же есть 2 версии IDA: x64 и x86 (желательно качать плагины под обе версии).
Additional to Class Informer:
Как скачать плагин: У вас должна быть установлена IDA Pro, и быть в какой то папке.
Далее заходим в папку с IDA Pro, и там папка "plugins", и туда кидаем .dll.
Готово, наша IDA Pro скачана!
Теперь немного про плагины.
Для того чтоб открыть Class Informer (Для показа классов/vtable, которые содержат функции/индексы), нам надо уже в готовой client.dll, кликнуть слева сверху Edit -> Plugins -> Class Informer, у вас должно появится окно, просто подтверждайте и у вас появятся классы.
Для создания паттерна, выделяем строки -> CTRL+ALT+S -> Ok.
1. IDA Pro (Crack или Официалку)
2. Class Informer (Plugin)
3. Sigmaker (Для создания паттернов).
Полезные сайты для создания читов:
1.
Пожалуйста, авторизуйтесь для просмотра ссылки.
(noad) - сайт движка, и всего что там можно найти.2. cs2.poggu.me (noad) - подробный гайд о конварах, кнопках, и тд.
Ниже приведены все ссылки и инструкции, если там вирусы или что то такое, я за это не ручаюсь.
1. IDA Pro:
Пожалуйста, авторизуйтесь для просмотра ссылки.
(8.3) (NOAD) - Crack by wiliam13372. Class Informer:
Пожалуйста, авторизуйтесь для просмотра ссылки.
(noad)3. Sigmaker:
Пожалуйста, авторизуйтесь для просмотра ссылки.
(noad)Учтите, что наша версия IDA: 8.3 , не 9.0 или другая (Плагины из других версий под нашу не подойдут).
Так же есть 2 версии IDA: x64 и x86 (желательно качать плагины под обе версии).
Additional to Class Informer:
IDA Class Informer plugin for IDA Pro 9 and older versions.
This plugin has been tested with IDA Pro 9.0 Beta, 7.7 and 8.3, both 32-bit and 64-bit versions.
You no longer need a separate version of the plugin for working on 32-bit files in 64-bit IDA.
For IDA 9, you just need to install ClassInformer64.dll.
For IDA 8 and 7, you need to install both ClassInformer64.dll and ClassInformer.dll.
This plugin has been tested with IDA Pro 9.0 Beta, 7.7 and 8.3, both 32-bit and 64-bit versions.
You no longer need a separate version of the plugin for working on 32-bit files in 64-bit IDA.
For IDA 9, you just need to install ClassInformer64.dll.
For IDA 8 and 7, you need to install both ClassInformer64.dll and ClassInformer.dll.
Как скачать плагин: У вас должна быть установлена IDA Pro, и быть в какой то папке.
Далее заходим в папку с IDA Pro, и там папка "plugins", и туда кидаем .dll.
Готово, наша IDA Pro скачана!
Теперь немного про плагины.
Для того чтоб открыть Class Informer (Для показа классов/vtable, которые содержат функции/индексы), нам надо уже в готовой client.dll, кликнуть слева сверху Edit -> Plugins -> Class Informer, у вас должно появится окно, просто подтверждайте и у вас появятся классы.
Для создания паттерна, выделяем строки -> CTRL+ALT+S -> Ok.
Глава 2: Ввод в курс дела.
Как вы знаете, с ложечки кормить вас никто не будет, если даже вы попросите снизу "Ыыы дайте пажалуста гаид на дабл папу пжпжж" вам никто не даст, это комьюнити не собирается друг другу помогать, только новички помогают другим новичкам.
Сейчас я кратко обьясню все по IDA Pro и плагинам:
1. Xref: Это можно сказать переменная, и нажав по ней "X" один раз, можно узнать кто к ней обращается.
Пример строки с хрефом: .rdata:0000000181B41BE0 aGetcrosshairco db 'GetCrosshairCode',0 ; DATA XREF: sub_180F20D30+CD7↑o
Как вы видите, есть строки "GetCrosshairCode", это наш Strings, а наш Xref желтым цветом - "aGetcrosshairco", нажав по нему можно увидеть примерно такое:
Это уже хрефы (Кто обращается к ней).
2. Функции.
Функция к примеру такая - sub_84120.
В ней содержится код самой функции и ее подфункций.
К примеру возьмем ксго инпут из класс информера, как вы видите, тут есть такое:
Вы спросите "Э, а шо это такое", это Vtable, коротко - таблица с функциями, которая содержит таблица "CCSGOInput", индексы начинаются с голой строки "dq offset ...".
например индекс нашего CreateMove будет пятым - "sub_AC64B0". Для того чтоб перейти в функцию, надо просто навестись на ее название и два раза кликнуть.
Сейчас я кратко обьясню все по IDA Pro и плагинам:
1. Xref: Это можно сказать переменная, и нажав по ней "X" один раз, можно узнать кто к ней обращается.
Пример строки с хрефом: .rdata:0000000181B41BE0 aGetcrosshairco db 'GetCrosshairCode',0 ; DATA XREF: sub_180F20D30+CD7↑o
Как вы видите, есть строки "GetCrosshairCode", это наш Strings, а наш Xref желтым цветом - "aGetcrosshairco", нажав по нему можно увидеть примерно такое:
Это уже хрефы (Кто обращается к ней).
2. Функции.
Функция к примеру такая - sub_84120.
В ней содержится код самой функции и ее подфункций.
К примеру возьмем ксго инпут из класс информера, как вы видите, тут есть такое:
например индекс нашего CreateMove будет пятым - "sub_AC64B0". Для того чтоб перейти в функцию, надо просто навестись на ее название и два раза кликнуть.
Глава 2.1: Коды, как получать исходники функций и так далее.
1. Краткий ликбез по ассемблеру (для тех, кто учил только Python)
Смотри, IDA показывает тебе код на ассемблере (x64). Если ты шаришь за "суб_84120" но не шаришь, что такое cmp — ты не далеко ушел от тех, кто просит "гаид на дабл папу". Исправляем ситуацию.
Разберем мой пример:
mov (Move) — берет значение из правого операнда и кладет в левый. Просто копирование.
lea (Load Effective Address) — хитрый зверь. В данном случае делает edx = ecx - 1. Не пугайся, это просто математека.
cmp (Compare) — Это самое важное. Инструкция вычитает два числа, но не сохраняет результат, а только меняет флаги в процессоре (Zero Flag, Sign Flag и т.д.).
Если числа равны -> флаг ZF (Zero Flag) = 1.
Если не равны -> ZF = 0.
jnz (Jump if Not Zero) — Смотрит на флаг ZF. Если ZF == 0 (то есть числа НЕ равны), то прыгаем по адресу. Если равны — идем дальше.
Как читать это как книгу?
Код выше переводится на русский так:
*"Достань число из стека. Вычти из него 1 и приготовь. А теперь главное: если это число НЕ равно -1 (0xFFFFFFFF), то прыгай в другое место. Если равно -1 — продолжай тут."*
Краткий словарь для самых маленьких (мам, я в хакере):
test edx, edx — То же самое, что cmp edx, 0. Проверяет, ноль ли это. (Любимая оптимизация компилятора).
jz (Jump if Zero) — Прыгаем, если предыдущее сравнение дало равенство (или результат был ноль).
jmp (Jump) — Безусловный прыжок. Всегда прыгаем. Типа goto в си.
call — Заходим в другую функцию (sub_...).
ret — Выходим из функции.
Как это связано с реверсом CS2?
Когда ты ищешь функцию CreateMove или PaintTraverse, ты увидишь кучу cmp, jnz, jz. Это условия. В игре полно проверок: "Если игрок в спектаторах — не двигай камеру", "Если мы в меню — не рисуй HUD". Эти проверки в ассемблере — и есть cmp + jcc (условный прыжок).
Совет от Deepseek: Не пытайся выучить все инструкции. Выучи эти 5-6 (mov, cmp, jmp, call, test, lea, ret) и просто гугли остальные по мере появления. Через неделю ты будешь читать lea rdx, [rcx-1] как edx = ecx - 1 даже не задумываясь.
Смотри, IDA показывает тебе код на ассемблере (x64). Если ты шаришь за "суб_84120" но не шаришь, что такое cmp — ты не далеко ушел от тех, кто просит "гаид на дабл папу". Исправляем ситуацию.
Разберем мой пример:
Код:
mov ecx, dword ptr [rsp+1E0h+var_1A8] ; 1. Кладем значение из памяти в регистр ECX
lea edx, [rcx-1] ; 2. Кладем в EDX значение (RCX - 1)
cmp ecx, 0FFFFFFFFh ; 3. Сравниваем ECX с -1 (0xFFFFFFFF)
jnz short loc_180ADFA6D ; 4. Прыгаем, если НЕ равно (Not Zero)
mov (Move) — берет значение из правого операнда и кладет в левый. Просто копирование.
lea (Load Effective Address) — хитрый зверь. В данном случае делает edx = ecx - 1. Не пугайся, это просто математека.
cmp (Compare) — Это самое важное. Инструкция вычитает два числа, но не сохраняет результат, а только меняет флаги в процессоре (Zero Flag, Sign Flag и т.д.).
Если числа равны -> флаг ZF (Zero Flag) = 1.
Если не равны -> ZF = 0.
jnz (Jump if Not Zero) — Смотрит на флаг ZF. Если ZF == 0 (то есть числа НЕ равны), то прыгаем по адресу. Если равны — идем дальше.
Как читать это как книгу?
Код выше переводится на русский так:
*"Достань число из стека. Вычти из него 1 и приготовь. А теперь главное: если это число НЕ равно -1 (0xFFFFFFFF), то прыгай в другое место. Если равно -1 — продолжай тут."*
Краткий словарь для самых маленьких (мам, я в хакере):
test edx, edx — То же самое, что cmp edx, 0. Проверяет, ноль ли это. (Любимая оптимизация компилятора).
jz (Jump if Zero) — Прыгаем, если предыдущее сравнение дало равенство (или результат был ноль).
jmp (Jump) — Безусловный прыжок. Всегда прыгаем. Типа goto в си.
call — Заходим в другую функцию (sub_...).
ret — Выходим из функции.
Как это связано с реверсом CS2?
Когда ты ищешь функцию CreateMove или PaintTraverse, ты увидишь кучу cmp, jnz, jz. Это условия. В игре полно проверок: "Если игрок в спектаторах — не двигай камеру", "Если мы в меню — не рисуй HUD". Эти проверки в ассемблере — и есть cmp + jcc (условный прыжок).
Совет от Deepseek: Не пытайся выучить все инструкции. Выучи эти 5-6 (mov, cmp, jmp, call, test, lea, ret) и просто гугли остальные по мере появления. Через неделю ты будешь читать lea rdx, [rcx-1] как edx = ecx - 1 даже не задумываясь.
Глава 2.2: Псевдокод функции, не магия, а перевод с кошачьего.
Для того чтоб открыть псевдокод, достаточно нажать "F5", или "TAB" когда ты внутри функции или графы,
и вместо ассемблера появилось это:
Поздравляю, ты посмотрел на псевдокод (Hex-Rays decompiler).
Но не обольщайся. Это НЕ исходник CS2. Это приблизительный перевод ассемблера на Си, который делает декомпилятор. Он часто ошибается, путает типы данных и называет переменные v1, v2, v69 (потому что оригинальные имена стерты компилятором).
Что за язык в псевдокоде?
Это Си (C). Но сильно упрощенный и иногда странный. Почему Си? Потому что Source 2 написан на Си++ (который компилируется в машинный код), а декомпилятор пытается восстановить максимально похожую структуру.
Как читать эту портянку кода? (Разбор на примере)
Вот кусок из моей функции:
Что здесь происходит?
sub_1808C2DA0 — это какая-то функция, которую IDA не смогла распознать. Мы видим только ее адрес. Твоя задача — понять, что она делает, по контексту или перейти в нее (X -> Enter).
*(_DWORD *)(v9 + 22800) — Вот это важно. Это означает: "Взять адрес v9, прибавить к нему 22800 (смещение), и прочитать оттуда 4 байта как DWORD (беззнаковое целое)".
1. В мире реверса это называется чтение по смещению. Часто так обращаются к полям классов/структур.
2. 22800 в десятичной = 0x5910 в шестнадцатеричной. Видишь в ассемблере [rsi+5910h]? Вот он, родной!
Типы данных в псевдокоде (шпаргалка)
Тип в псевдокоде Размер (x64) Что хранит Аналог в ассемблере
char 1 байт Буква или маленькое число BYTE, db
int 4 байта Целое число (обычно) DWORD, dd
__int64 8 байт Большое целое (или указатель) QWORD, dq
float 4 байта Число с плавающей точкой dd (но для float)
double 8 байт Дробное число двойной точности dq (но для double)
* (звездочка) 8 байт Указатель! (например, __int64* — указатель на число) dq с адресом
__fastcall — Соглашение вызова (аргументы в rcx, rdx, r8, r9) mov rcx, ...
Самые частые конструкции в псевдокоде CS2
1. Разыменование указателя (чтение по адресу)
В читерском коде это будет выглядеть как:
2. Вызов виртуальной функции (VTable)
Страшно выглядит? Давай разберем:
v58 — указатель на объект.
*v58 — первый QWORD в объекте. Это указатель на VTable.
*v58 + 2712 — берем VTable, прыгаем на 2712 байт (это 2712 / 8 = 339-й индекс функции, потому что в x64 указатель 8 байт).
(void (__fastcall **)(...)) — приводим это к указателю на функцию.
Дальше вызываем ее с аргументами v58 (this) и v152.
Как понять, что это? 2712 — это смещение в VTable. Открываешь Class Informer, находишь класс, ищешь функцию с индексом 339. Или просто гуглишь "Source 2 vtable index 339".
3. Работа с флагами (битовые маски)
Это типичная работа с флагами команд или состояний. В ассемблере это выглядит как or dword ptr [rax+10h], 20h.
4. Условные переходы (if)
Помнишь наши cmp и jnz из прошлой главы? Вот они в виде if.
Как понять, что делает функция? (Алгоритм)
Не пытайся прочитать все 200 строк. Ищи ключевые моменты:
Что пришло на вход? Смотрим аргументы: __int64 *a1 (скорее всего this указатель), int a2, char a3.
Какие строки использует? Поищи "CreateMove", "GetCrosshairCode" и т.д. — это подсказки.
Какие VTable вызывает? &CUserCmd::vftable, &CUserCmdBase::vftable — о, это работа с CUserCmd (команда игрока).
Что возвращает? void — ничего не возвращает, значит, что-то изменяет внутри.
Вывод: sub_180ADF9D0 — это функция, которая создает/заполняет CUserCmd (команду игрока), используя какие-то данные из a1 (скорее всего CCSGOInput). А еще она пишет в логи и работает с мышью/клавиатурой. Звучит как CreateMove?
Золотое правило реверсера
Псевдокод — это подсказка, а не истина.
Если декомпилятор написал что-то странное:
Нажми на переменную → Y → переименуй её в pInput, pCmd, weaponPtr.
Нажми на тип → Y → исправь __int64 на CUserCmd*.
Нажми на число → H → переведи его в hex (удобнее искать по дампу).
и вместо ассемблера появилось это:
Код:
void __fastcall sub_180ADF9D0(__int64 *a1, int a2, char a3)
{
int v4; // r15d
const char *v5; // rax
// ... еще 150 строк такого ...
}
Поздравляю, ты посмотрел на псевдокод (Hex-Rays decompiler).
Но не обольщайся. Это НЕ исходник CS2. Это приблизительный перевод ассемблера на Си, который делает декомпилятор. Он часто ошибается, путает типы данных и называет переменные v1, v2, v69 (потому что оригинальные имена стерты компилятором).
Что за язык в псевдокоде?
Это Си (C). Но сильно упрощенный и иногда странный. Почему Си? Потому что Source 2 написан на Си++ (который компилируется в машинный код), а декомпилятор пытается восстановить максимально похожую структуру.
Как читать эту портянку кода? (Разбор на примере)
Вот кусок из моей функции:
Код:
v9 = sub_1808C2DA0(off_182034818, v8);
v10 = *(_DWORD *)(v9 + 22800);
v155 = (void ***)sub_1808C2BA0(v6, v10);
Что здесь происходит?
sub_1808C2DA0 — это какая-то функция, которую IDA не смогла распознать. Мы видим только ее адрес. Твоя задача — понять, что она делает, по контексту или перейти в нее (X -> Enter).
*(_DWORD *)(v9 + 22800) — Вот это важно. Это означает: "Взять адрес v9, прибавить к нему 22800 (смещение), и прочитать оттуда 4 байта как DWORD (беззнаковое целое)".
1. В мире реверса это называется чтение по смещению. Часто так обращаются к полям классов/структур.
2. 22800 в десятичной = 0x5910 в шестнадцатеричной. Видишь в ассемблере [rsi+5910h]? Вот он, родной!
Типы данных в псевдокоде (шпаргалка)
Тип в псевдокоде Размер (x64) Что хранит Аналог в ассемблере
char 1 байт Буква или маленькое число BYTE, db
int 4 байта Целое число (обычно) DWORD, dd
__int64 8 байт Большое целое (или указатель) QWORD, dq
float 4 байта Число с плавающей точкой dd (но для float)
double 8 байт Дробное число двойной точности dq (но для double)
* (звездочка) 8 байт Указатель! (например, __int64* — указатель на число) dq с адресом
__fastcall — Соглашение вызова (аргументы в rcx, rdx, r8, r9) mov rcx, ...
Самые частые конструкции в псевдокоде CS2
1. Разыменование указателя (чтение по адресу)
Код:
v10 = *(_DWORD *)(v9 + 22800); // Читаем DWORD по адресу (v9 + 0x5910)
v11 = *(_QWORD *)v9; // Читаем QWORD по адресу v9 (первое поле класса)
В читерском коде это будет выглядеть как:
Код:
int v10 = *(int*)(v9 + 0x5910);
Код:
(*(void (__fastcall **)(__int64, __int64))(*v58 + 2712))(v58, v152);
v58 — указатель на объект.
*v58 — первый QWORD в объекте. Это указатель на VTable.
*v58 + 2712 — берем VTable, прыгаем на 2712 байт (это 2712 / 8 = 339-й индекс функции, потому что в x64 указатель 8 байт).
(void (__fastcall **)(...)) — приводим это к указателю на функцию.
Дальше вызываем ее с аргументами v58 (this) и v152.
Как понять, что это? 2712 — это смещение в VTable. Открываешь Class Informer, находишь класс, ищешь функцию с индексом 339. Или просто гуглишь "Source 2 vtable index 339".
3. Работа с флагами (битовые маски)
Код:
*(_DWORD *)(v20 + 16) |= 0x20u; // Установить 6-й бит (0x20 = 32) в 1
*(_DWORD *)(v20 + 16) &= ~0x40u; // Снять 7-й бит (0x40 = 64)
Это типичная работа с флагами команд или состояний. В ассемблере это выглядит как or dword ptr [rax+10h], 20h.
4. Условные переходы (if)
Код:
if ( v60 && *(float *)(v7 + 2328) >= 0.0 )
{
v187 = 1;
}
Помнишь наши cmp и jnz из прошлой главы? Вот они в виде if.
Как понять, что делает функция? (Алгоритм)
Не пытайся прочитать все 200 строк. Ищи ключевые моменты:
Что пришло на вход? Смотрим аргументы: __int64 *a1 (скорее всего this указатель), int a2, char a3.
Какие строки использует? Поищи "CreateMove", "GetCrosshairCode" и т.д. — это подсказки.
Какие VTable вызывает? &CUserCmd::vftable, &CUserCmdBase::vftable — о, это работа с CUserCmd (команда игрока).
Что возвращает? void — ничего не возвращает, значит, что-то изменяет внутри.
Вывод: sub_180ADF9D0 — это функция, которая создает/заполняет CUserCmd (команду игрока), используя какие-то данные из a1 (скорее всего CCSGOInput). А еще она пишет в логи и работает с мышью/клавиатурой. Звучит как CreateMove?
Золотое правило реверсера
Псевдокод — это подсказка, а не истина.
Если декомпилятор написал что-то странное:
Нажми на переменную → Y → переименуй её в pInput, pCmd, weaponPtr.
Нажми на тип → Y → исправь __int64 на CUserCmd*.
Нажми на число → H → переведи его в hex (удобнее искать по дампу).
Глава 2.3: WorldToScreen, ViewMatrix, EntityList — поговорим об этом
Многие новички слышали эти слова, но мало кто понимает, как это работает внутри CS2. Сейчас разберемся.
Что такое EntityList?
EntityList — это массив (или список) всех объектов в игре: игроков, оружия, гранат, дропа и т.д. В CS2 он хранится в client.dll по определенному смещению (dwEntityList).
Но есть нюанс: в CS2 разработчики разделили controller и pawn.
Controller — информация об игроке (ник, статус, счет, скин и т.д.). Существует всегда, даже если игрок мертв или вышел.
Pawn — физический объект игрока на карте (позиция, здоровье, оружие в руках, углы обзора). Существует только когда игрок живой и находится на карте.
Зачем разделили? Чтобы экономить ресурсы. Когда игрок мертв или в spectators, его pawn удаляется, но controller остается (чтобы показывать счет в табло).
Как найти всех игроков (Pawn)
Вот пример того, как это выглядит в коде:
Что тут происходит?
Операция Что значит
i & 0x7FFF Обрезаем индекс до 15 бит (максимум 32767)
>> 9 Сдвиг вправо на 9 бит (деление на 512) — определяем номер корзины в хеш-таблице
(8 * ... >> 9) + 16 Вычисляем смещение до записи в EntityList
112 * (i & 0x1FF) Смещение внутри записи (112 байт на элемент, 0x1FF = 511)
Что такое ViewMatrix и WorldToScreen?
ViewMatrix (матрица обзора) — это математическая матрица 4x4, которая преобразует 3D координаты мира в 2D координаты на твоем экране.
Без нее ты не сможешь понять, где находится игрок относительно тебя — слева, справа, выше, ниже, виден ли он вообще.
В CS2 матрица обзора хранится где-то в памяти, и чтобы получить экранные координаты, используется функция вроде WorldToScreen или ScreenTransform.
Пример реверснутой ScreenTransform
Вот как выглядит эта функция в IDA (псевдокод):
Как использовать ScreenTransform?
Что такое EntityList?
EntityList — это массив (или список) всех объектов в игре: игроков, оружия, гранат, дропа и т.д. В CS2 он хранится в client.dll по определенному смещению (dwEntityList).
Но есть нюанс: в CS2 разработчики разделили controller и pawn.
Controller — информация об игроке (ник, статус, счет, скин и т.д.). Существует всегда, даже если игрок мертв или вышел.
Pawn — физический объект игрока на карте (позиция, здоровье, оружие в руках, углы обзора). Существует только когда игрок живой и находится на карте.
Зачем разделили? Чтобы экономить ресурсы. Когда игрок мертв или в spectators, его pawn удаляется, но controller остается (чтобы показывать счет в табло).
Как найти всех игроков (Pawn)
Вот пример того, как это выглядит в коде:
Код:
import pymem
pm = pymem.Pymem("cs2.exe")
client = pymem.process.module_from_name(pm.process_handle, "client.dll").lpBaseOfDll
# Берем базу EntityList
entity_list = pm.read_longlong(client + dwEntityList)
# Проходим по всем слотам (обычно 64 игрока)
for i in range(1, 64):
# Получаем запись в списке (хеш-таблица)
list_entry = pm.read_longlong(entity_list + ((8 * (i & 0x7FFF) >> 9) + 16))
if not list_entry:
continue
# Читаем контроллер из записи
controller = pm.read_longlong(list_entry + 112 * (i & 0x1FF))
if not controller:
continue
# Получаем хендл павна из контроллера
pawn_handle = pm.read_longlong(controller + m_hPlayerPawn)
if not pawn_handle:
continue
# Вторая запись для павна (по хендлу)
entry2 = pm.read_longlong(entity_list + (0x8 * ((pawn_handle & 0x7FFF) >> 9) + 16))
if not entry2:
continue
# Читаем сам павн
pawn = pm.read_longlong(entry2 + 112 * (pawn_handle & 0x1FF))
if pawn:
health = pm.read_int(pawn + m_iHealth) & 0xFF
if health > 0: # Живой игрок
print(f"Pawn: {hex(pawn)}, Health: {health}")
Что тут происходит?
Операция Что значит
i & 0x7FFF Обрезаем индекс до 15 бит (максимум 32767)
>> 9 Сдвиг вправо на 9 бит (деление на 512) — определяем номер корзины в хеш-таблице
(8 * ... >> 9) + 16 Вычисляем смещение до записи в EntityList
112 * (i & 0x1FF) Смещение внутри записи (112 байт на элемент, 0x1FF = 511)
Что такое ViewMatrix и WorldToScreen?
ViewMatrix (матрица обзора) — это математическая матрица 4x4, которая преобразует 3D координаты мира в 2D координаты на твоем экране.
Без нее ты не сможешь понять, где находится игрок относительно тебя — слева, справа, выше, ниже, виден ли он вообще.
В CS2 матрица обзора хранится где-то в памяти, и чтобы получить экранные координаты, используется функция вроде WorldToScreen или ScreenTransform.
Пример реверснутой ScreenTransform
Вот как выглядит эта функция в IDA (псевдокод):
Код:
__int64 __fastcall ScreenTransform(float *world_pos, float *screen_out)
{
float *matrix; // rdx
__int64 result; // rax
float x, y, z, w; // xmm регистры
char temp; // [rsp+40h] [rbp+18h]
if ( !g_ViewMatrix ) // qword_21CD768 - глобальный указатель на матрицу
return 0i64;
// Получаем матрицу обзора (сложная логика с индексами)
matrix = GetCurrentViewMatrix(); // упрощенно
// Умножение вектора (world_pos) на матрицу 4x4
x = matrix[0] * world_pos[0] + matrix[1] * world_pos[1] + matrix[2] * world_pos[2] + matrix[3];
y = matrix[4] * world_pos[0] + matrix[5] * world_pos[1] + matrix[6] * world_pos[2] + matrix[7];
z = matrix[12] * world_pos[0] + matrix[13] * world_pos[1] + matrix[14] * world_pos[2] + matrix[15];
screen_out[0] = x;
screen_out[1] = y;
// Если z (глубина) слишком маленькая — точка за камерой
if ( z >= 0.001 )
{
// Проекция: делим x и y на глубину
float inv_z = 1.0 / z;
screen_out[0] = x * inv_z;
screen_out[1] = y * inv_z;
return 0;
}
else
{
screen_out[0] = x * 100000.0; // Костыль для очень далеких точек
screen_out[1] = y * 100000.0;
return 1; // Возвращаем 1 если точка не на экране
}
}
Код:
__int64 __fastcall sub_B82F90(float *a1, __int64 a2)
{
float *v4; // rdx
__int64 result; // rax
float v6; // xmm2_4
float v7; // xmm3_4
float v8; // xmm4_4
float v9; // xmm0_4
char v10; // [rsp+40h] [rbp+18h] BYREF
if ( !qword_21CD768 )
return 0i64;
v4 = (float *)((char *)&unk_2310F10
+ 64
* (__int64)*(int *)(*(__int64 (__fastcall **)(__int64, char *))(*(_QWORD *)qword_230AF10 + 768i64))(
qword_230AF10,
&v10));
result = 0i64;
v6 = (float)((float)((float)(v4[1] * a1[1]) + (float)(*v4 * *a1)) + (float)(v4[2] * a1[2])) + v4[3];
*(float *)a2 = v6;
v7 = (float)((float)((float)(v4[5] * a1[1]) + (float)(v4[4] * *a1)) + (float)(v4[6] * a1[2])) + v4[7];
*(float *)(a2 + 4) = v7;
v8 = (float)((float)((float)(v4[13] * a1[1]) + (float)(v4[12] * *a1)) + (float)(v4[14] * a1[2])) + v4[15];
*(_DWORD *)(a2 + 8) = 0;
if ( v8 >= 0.001 )
{
v9 = 1.0 / v8;
}
else
{
v9 = 100000.0;
result = 1i64;
}
*(float *)a2 = v6 * v9;
*(float *)(a2 + 4) = v7 * v9;
return result;
}
Как использовать ScreenTransform?
Код:
// Получаем позицию игрока
Vector world_pos = GetPawnPosition(pawn);
// Преобразуем в экранные координаты (NDC: от -1 до 1 по X и Y)
Vector2D screen_ndc;
if (ScreenTransform(&world_pos, &screen_ndc)) {
// Точка за камерой (позади тебя или слишком далеко)
return;
}
// Преобразуем NDC в пиксели экрана
float screen_x = (screen_ndc.x + 1.0) * 0.5 * screen_width;
float screen_y = (1.0 - (screen_ndc.y + 1.0) * 0.5) * screen_height;
// Теперь screen_x, screen_y — это координаты на экране
DrawEsp(screen_x, screen_y, health, team);
Глава 3: Как загрузить client.dll в IDA Pro правильно
Рано или поздно дамперы перестанут работать (обновление игры, смена защиты и т.д.), и ты не сможешь найти паттерны или оффсеты. Для этого случая нам и нужна IDA Pro — мы будем дизассемблировать саму client.dll.
Шаг 1: Открываем файл
Запускаем ida64.exe (для CS2 нужна именно 64-битная версия, потому что игра 64-битная).
Нажимаем "New" (или File → New).
Ищем client.dll. Где она лежит?
C:\Program Files (x86)\Steam\steamapps\common\Counter-Strike Global Offensive\game\bin\win64\client.dll
Или в папке с CS2 (если у тебя другой путь).
Важно: client.dll весит более 30 МБ (обычно 40-60 МБ). Если ты нашел файл меньше 30 МБ — это либо client.dll из CS:GO, либо что-то не то.
Шаг 2: Загрузка
После выбора файла IDA начнет анализ. На все всплывающие окна просто нажимай "OK" или "Yes". IDA сама определит архитектуру (x64) и начнет дизассемблирование.
Процесс анализа может занять от 5 до 20 минут — зависит от твоего процессора. Не пугайся, это нормально.
Шаг 3: Два подхода к работе
После загрузки у тебя есть два варианта работы с адресами функций:
Вариант Адрес вида Пример Когда использовать
Вариант 1 0x1806CE798 (полный) sub_1806CE798 Для создания паттернов, поиска по Xref, анализа кода
Вариант 2 0x6CE798 (смещение/RVA) sub_6CE798 Для получения реальных оффсетов, которые не меняются при перезагрузке IDA
Пояснение: 0x180000000 — это база, с которой IDA загрузила DLL. Если ты хочешь получить оффсеты (смещения от начала файла), нужно отнять эту базу.
Как переключиться на вариант 2 (реальные оффсеты)
Если ты хочешь видеть смещения типа 0x6CE798 (а не полные адреса), сделай следующее:
В верхнем меню нажми Edit
Наведись на Segments
Выбери Rebase program
В поле Value поставь 0x0 (ноль)
Нажми OK
После этого все адреса сдвинутся, и sub_1806CE798 превратится в sub_6CE798. Теперь ты можешь использовать эти значения как оффсеты для чтения памяти.
Как вернуть обратно? Если захочешь вернуть полные адреса, снова сделай Rebase program и поставь значение 0x180000000 (или то, что было изначально).
Какой вариант выбрать?
Для новичка: Начинай с варианта 1 (ничего не меняй). Так проще читать гайды и сравнивать с чужими скринами.
Для профи: Используй вариант 2, если хочешь получить реальные RVA для паттернов или внешнего чита.
Совет: Если не уверен — оставь как есть. Создавать паттерны можно в любом варианте, адреса внутри сигнатуры всё равно подставляются по-другому.
Что делать после загрузки?
Нажми Shift+F12 — откроются строки (Strings). Ищи там "CreateMove", "PaintTraverse", "LockCursor".
Нажми Ctrl+F — поиск по всему бинарнику.
Установи плагин Class Informer (как в Главе 1) — он поможет найти VTable нужных классов.
Шаг 1: Открываем файл
Запускаем ida64.exe (для CS2 нужна именно 64-битная версия, потому что игра 64-битная).
Нажимаем "New" (или File → New).
Ищем client.dll. Где она лежит?
C:\Program Files (x86)\Steam\steamapps\common\Counter-Strike Global Offensive\game\bin\win64\client.dll
Или в папке с CS2 (если у тебя другой путь).
Важно: client.dll весит более 30 МБ (обычно 40-60 МБ). Если ты нашел файл меньше 30 МБ — это либо client.dll из CS:GO, либо что-то не то.
Шаг 2: Загрузка
После выбора файла IDA начнет анализ. На все всплывающие окна просто нажимай "OK" или "Yes". IDA сама определит архитектуру (x64) и начнет дизассемблирование.
Процесс анализа может занять от 5 до 20 минут — зависит от твоего процессора. Не пугайся, это нормально.
Шаг 3: Два подхода к работе
После загрузки у тебя есть два варианта работы с адресами функций:
Вариант Адрес вида Пример Когда использовать
Вариант 1 0x1806CE798 (полный) sub_1806CE798 Для создания паттернов, поиска по Xref, анализа кода
Вариант 2 0x6CE798 (смещение/RVA) sub_6CE798 Для получения реальных оффсетов, которые не меняются при перезагрузке IDA
Пояснение: 0x180000000 — это база, с которой IDA загрузила DLL. Если ты хочешь получить оффсеты (смещения от начала файла), нужно отнять эту базу.
Как переключиться на вариант 2 (реальные оффсеты)
Если ты хочешь видеть смещения типа 0x6CE798 (а не полные адреса), сделай следующее:
В верхнем меню нажми Edit
Наведись на Segments
Выбери Rebase program
В поле Value поставь 0x0 (ноль)
Нажми OK
После этого все адреса сдвинутся, и sub_1806CE798 превратится в sub_6CE798. Теперь ты можешь использовать эти значения как оффсеты для чтения памяти.
Как вернуть обратно? Если захочешь вернуть полные адреса, снова сделай Rebase program и поставь значение 0x180000000 (или то, что было изначально).
Какой вариант выбрать?
Для новичка: Начинай с варианта 1 (ничего не меняй). Так проще читать гайды и сравнивать с чужими скринами.
Для профи: Используй вариант 2, если хочешь получить реальные RVA для паттернов или внешнего чита.
Совет: Если не уверен — оставь как есть. Создавать паттерны можно в любом варианте, адреса внутри сигнатуры всё равно подставляются по-другому.
Что делать после загрузки?
Нажми Shift+F12 — откроются строки (Strings). Ищи там "CreateMove", "PaintTraverse", "LockCursor".
Нажми Ctrl+F — поиск по всему бинарнику.
Установи плагин Class Informer (как в Главе 1) — он поможет найти VTable нужных классов.
Глава 3.1: Как находить поля классов (Offsets) через строки
После того как ты загрузил client.dll в IDA (Глава 3), самое время научиться находить смещения полей классов — например, m_iHealth, m_vecOrigin, m_hPlayerPawn и другие.
В CS2 (как и в Source 2 в целом) все названия полей классов хранятся в виде строк в бинарнике. Это наш главный козырь.
Пошаговая инструкция
Шаг 1: Открываем окно строк
Нажми Shift+F12 (или View → Open subviews → Strings). Откроется окно со всеми строками из client.dll.
Шаг 2: Ищем нужное поле
Нажми Ctrl+F и введи название поля, которое хочешь найти. Например, m_pClippingWeapon.
Важно: Искать нужно точное название, как в игре. Список полей можно посмотреть в SDK от Valve или в старых дампах CS:GO (но учти, многие смещения изменились).
Шаг 3: Проверяем результат
Если нашлась одна строка — отлично, это то, что нужно.
Если нашлось несколько — возможно, поле используется в разных контекстах, но чаще всего нужна первая.
Шаг 4: Переходим к строке
Дважды кликни по найденной строке. IDA покажет тебе эту строку в дизассемблере.
Выглядеть это будет примерно так:
Наведись на aMPclippingweap (это метка, которую IDA сгенерировала автоматически) и нажми X. Откроется окно со списком мест, где используется эта строка (xrefs).
Шаг 5: Смотрим, где используется строка
Обычно таких мест несколько. Нам нужно то, где строка находится в секции .data (не в .rdata). Выглядеть это будет так:
Что мы видим?
dq offset aMPclippingweap — указатель на строку с названием поля
dq 0 — какое-то служебное поле (обычно 0)
dw 3DC0h — это и есть наше смещение!
Шаг 6: Получаем оффсет
В данном примере смещение = 0x3DC0 (в десятичной это 15808).
Теперь в своем чите ты можешь читать это поле так:
Что делать, если строка не найдена?
Бывает, что поле называется иначе в бинарнике или его нет в виде строки. В таком случае:
Попробуй поискать части названия (например, "Health" вместо "m_iHealth")
Используй другие методы поиска (по функциям геттерам/сеттерам) — об этом в следующих главах
Проверь, что ты ищешь в правильной DLL (некоторые поля лежат в engine.dll или server.dll)
Примеры полей, которые можно найти этим способом
Название поля Что хранит Примерный тип
m_iHealth Здоровье игрока int (4 байта)
m_iTeamNum Номер команды int (4 байта)
m_vecOrigin Позиция на карте Vector (3 x float)
m_hPlayerPawn Хендл павна (из контроллера) unsigned long (8 байт)
m_lifeState Состояние жизни (0 = жив) char (1 байт)
m_angEyeAngles Углы обзора игрока QAngle (2 x float)
Важное замечание
Этот гайд показывает, как находить поля классов (fields), а не глобальные оффсеты типа dwEntityList или dwLocalPlayerPawn. Глобальные оффсеты ищутся по-другому — об этом в следующей главе.
Краткий итог
Открываешь Shift+F12 (Strings)
Ищешь название поля (Ctrl+F)
Переходишь по Xref в .data секцию
Смотришь на dw или dd — это смещение
Готово, можешь использовать в чите
В CS2 (как и в Source 2 в целом) все названия полей классов хранятся в виде строк в бинарнике. Это наш главный козырь.
Пошаговая инструкция
Шаг 1: Открываем окно строк
Нажми Shift+F12 (или View → Open subviews → Strings). Откроется окно со всеми строками из client.dll.
Шаг 2: Ищем нужное поле
Нажми Ctrl+F и введи название поля, которое хочешь найти. Например, m_pClippingWeapon.
Важно: Искать нужно точное название, как в игре. Список полей можно посмотреть в SDK от Valve или в старых дампах CS:GO (но учти, многие смещения изменились).
Шаг 3: Проверяем результат
Если нашлась одна строка — отлично, это то, что нужно.
Код:
.rdata:0000000181AD6368 aMPclippingweap db 'm_pClippingWeapon',0
Если нашлось несколько — возможно, поле используется в разных контекстах, но чаще всего нужна первая.
Шаг 4: Переходим к строке
Дважды кликни по найденной строке. IDA покажет тебе эту строку в дизассемблере.
Выглядеть это будет примерно так:
Код:
.rdata:0000000181AD6368 aMPclippingweap db 'm_pClippingWeapon',0
Наведись на aMPclippingweap (это метка, которую IDA сгенерировала автоматически) и нажми X. Откроется окно со списком мест, где используется эта строка (xrefs).
Шаг 5: Смотрим, где используется строка
Обычно таких мест несколько. Нам нужно то, где строка находится в секции .data (не в .rdata). Выглядеть это будет так:
Код:
.data:0000000182077D80 dq offset aMPclippingweap ; "m_pClippingWeapon"
.data:0000000182077D88 dq 0
.data:0000000182077D90 dw 3DC0h
Что мы видим?
dq offset aMPclippingweap — указатель на строку с названием поля
dq 0 — какое-то служебное поле (обычно 0)
dw 3DC0h — это и есть наше смещение!
Шаг 6: Получаем оффсет
В данном примере смещение = 0x3DC0 (в десятичной это 15808).
Теперь в своем чите ты можешь читать это поле так:
Код:
// Получаем указатель на оружие
uintptr_t clippingWeapon = (uintptr_t)(pawn + 0x3DC0);
Что делать, если строка не найдена?
Бывает, что поле называется иначе в бинарнике или его нет в виде строки. В таком случае:
Попробуй поискать части названия (например, "Health" вместо "m_iHealth")
Используй другие методы поиска (по функциям геттерам/сеттерам) — об этом в следующих главах
Проверь, что ты ищешь в правильной DLL (некоторые поля лежат в engine.dll или server.dll)
Примеры полей, которые можно найти этим способом
Название поля Что хранит Примерный тип
m_iHealth Здоровье игрока int (4 байта)
m_iTeamNum Номер команды int (4 байта)
m_vecOrigin Позиция на карте Vector (3 x float)
m_hPlayerPawn Хендл павна (из контроллера) unsigned long (8 байт)
m_lifeState Состояние жизни (0 = жив) char (1 байт)
m_angEyeAngles Углы обзора игрока QAngle (2 x float)
Важное замечание
Этот гайд показывает, как находить поля классов (fields), а не глобальные оффсеты типа dwEntityList или dwLocalPlayerPawn. Глобальные оффсеты ищутся по-другому — об этом в следующей главе.
Краткий итог
Открываешь Shift+F12 (Strings)
Ищешь название поля (Ctrl+F)
Переходишь по Xref в .data секцию
Смотришь на dw или dd — это смещение
Готово, можешь использовать в чите
Глава 3.2: Нахождение глобальных оффсетов (dwEntityList, dwLocalPlayerPawn и другие)
( советую после этой главы посмотреть https://yougame.biz/threads/357362/post-3331628 (noad ) )
Поиск глобальных оффсетов (вроде dwEntityList, dwLocalPlayerPawn) отличается от поиска полей классов. Глобальные оффсеты — это указатели на какие-то глобальные структуры или переменные. Они чаще всего встречаются внутри функций, которые их используют.
Как это работает: Если какой-то функции нужно, например, узнать, заложена ли бомба — она будет обращаться к энтити-листу. Мы находим такую функцию, смотрим, откуда она читает данные, и получаем оффсет.
Пример 1: Находим dwLocalPlayerPawn (через строку)
Этот способ показал TheXSVV, за что ему спасибо.
Шаг 1: Ищем строку
Открываем Shift+F12 (Strings), ищем строку:
Строка должна найтись одна. Дважды кликаем по ней.
Шаг 2: Переходим по Xref
Наводимся на метку строки (например, aGetTheLocalPla), жмем X. Переходим в то место, где эта строка используется.
Шаг 3: Открываем псевдокод
Нажимаем F5 (или Tab), чтобы перейти в псевдокод. Видим примерно такой код:
Шаг 4: Переходим в функцию
Нас интересует sub_1803AFC10. Переходим в нее (дважды кликнуть по названию). Снова открываем псевдокод (F5):
Шаг 5: Спускаемся глубже
Нас интересует sub_1808E7880. Переходим в нее:
Шаг 6: Получаем оффсеты
Смотрим на код:
Переменная Оффсет Что это
qword_1821B1988 0x21B1988 dwEntityList (база энтити-листа)
v3 = *(_DWORD *)(v2 + 1732) 0x6C4 (1732 в десятичной) m_hPlayerPawn (хендл павна из контроллера)
Важно: Эти оффсеты указаны относительно базы client.dll. Если ты делал Rebase program (Глава 3) на 0x0, то оффсеты будут именно такими. Если нет — нужно вычесть 0x180000000.
Пример 2: Быстрый способ найти dwLocalPlayerPawn
Шаг 1: Ищем строку
Открываем Shift+F12 (Strings), ищем:
Шаг 2: Переходим по Xref
Наводимся на любую строку из результатов, жмем X. Переходим по одному из xref.
Шаг 3: Смотрим ассемблер
В дизассемблере видим такой код:
Шаг 4: Переходим в функцию
Видишь call sub_1808E7880? Переходим в эту функцию (дважды кликнуть по sub_1808E7880). И вуаля — мы внутри той же функции, что и в первом примере.
Бонус: Таким способом ты находишь не только dwEntityList, но и dwLocalPlayerController (локальный контроллер).
Пример 3: Находим ViewMatrix
Шаг 1: Открываем окно имен
Нажимаем Shift+F4 (или View → Open subviews → Names).
Шаг 2: Ищем VTable
В поиске вводим:
Шаг 3: Ищем нужную функцию
Листаем VTable до конца. Нас интересует 64-й индекс (или около того). Выглядеть это будет примерно так:
Шаг 4: Переходим в функцию
Дважды кликаем по sub_1803E9750. Смотрим код:
Шаг 5: Что здесь?
Функция sub_180B7D5F0 — это и есть получение ViewMatrix. Дальше нужно анализировать эту функцию (поиск глобальной переменной с матрицей, смещений). ViewMatrix лежит в client.dll по определенному смещению.
Поиск глобальных оффсетов (вроде dwEntityList, dwLocalPlayerPawn) отличается от поиска полей классов. Глобальные оффсеты — это указатели на какие-то глобальные структуры или переменные. Они чаще всего встречаются внутри функций, которые их используют.
Как это работает: Если какой-то функции нужно, например, узнать, заложена ли бомба — она будет обращаться к энтити-листу. Мы находим такую функцию, смотрим, откуда она читает данные, и получаем оффсет.
Пример 1: Находим dwLocalPlayerPawn (через строку)
Этот способ показал TheXSVV, за что ему спасибо.
Шаг 1: Ищем строку
Открываем Shift+F12 (Strings), ищем строку:
Код:
"Get the local player pawn."
Строка должна найтись одна. Дважды кликаем по ней.
Шаг 2: Переходим по Xref
Наводимся на метку строки (например, aGetTheLocalPla), жмем X. Переходим в то место, где эта строка используется.
Шаг 3: Открываем псевдокод
Нажимаем F5 (или Tab), чтобы перейти в псевдокод. Видим примерно такой код:
Код:
*(_QWORD *)(v42 + 16) = "Get the local player pawn.";
*(_QWORD *)(v42 + 48) = v2;
*(_QWORD *)v42 = "GetLocalPlayerPawn";
*(_QWORD *)(v42 + 8) = "GetLocalPlayerPawn";
LODWORD(v40) = *(char *)(v42 + 26) - 1;
*(_WORD *)(v42 + 24) = 31;
v43 = (int)v40;
if ( (int)v40 >= 0 )
{
v44 = 0;
v45 = (_BYTE *)(v42 + v43 + 27);
do
{
if ( *v45 != 32 )
break;
++v44;
--v45;
--v43;
*(_BYTE *)(v42 + 25) = v44;
}
while ( v43 >= 0 );
}
*(_QWORD *)(v42 + 56) = sub_18039E840;
*(_QWORD *)(v42 + 64) = sub_1803AFC10; // <-- Нас интересует эта функция
Шаг 4: Переходим в функцию
Нас интересует sub_1803AFC10. Переходим в нее (дважды кликнуть по названию). Снова открываем псевдокод (F5):
Код:
__int64 sub_1803AFC10()
{
__int64 result; // rax
__int64 v1; // rbx
result = sub_1808E7880(0i64);
v1 = result;
if ( result )
{
if ( !*(_QWORD )((_QWORD *)(result + 16) + 40i64) )
sub_1814C7A30(result, 0i64);
return *(_QWORD )((_QWORD *)(v1 + 16) + 40i64);
}
return result;
}
Шаг 5: Спускаемся глубже
Нас интересует sub_1808E7880. Переходим в нее:
Код:
__int64 __fastcall sub_1808E7880(int a1)
{
__int64 v1; // rdx
__int64 v2; // r8
int v3; // r8d
__int64 v4; // r10
_DWORD *v5; // rax
char v7; // [rsp+30h] [rbp+8h] BYREF
if ( a1 == -1 )
a1 = *(_DWORD )((__int64 (__fastcall **)(__int64, char ))((_QWORD *)qword_18230DF10 + 768i64))(
qword_18230DF10,
&v7);
v1 = 0i64;
v2 = qword_1822F8028[a1];
if ( v2 )
{
v3 = *(_DWORD *)(v2 + 1732); // <-- Это оффсет m_hPlayerPawn (хендл павна из контроллера)
if ( v3 != -1 )
{
if ( qword_1821B1988 ) // <-- Это оффсет EntityList
{
if ( v3 != -2
&& (v4 = *(_QWORD *)(qword_1821B1988 + 8i64 * ((unsigned __int16)(v3 & 0x7FFF) >> 9))) != 0
&& (v5 = (_DWORD *)(v4 + 112i64 * (v3 & 0x1FF))) != 0i64 )
{
if ( v5[4] != v3 )
v5 = 0i64;
}
else
{
v5 = 0i64;
}
if ( v5 )
return *(_QWORD *)v5;
}
}
}
return v1;
}
Шаг 6: Получаем оффсеты
Смотрим на код:
Переменная Оффсет Что это
qword_1821B1988 0x21B1988 dwEntityList (база энтити-листа)
v3 = *(_DWORD *)(v2 + 1732) 0x6C4 (1732 в десятичной) m_hPlayerPawn (хендл павна из контроллера)
Важно: Эти оффсеты указаны относительно базы client.dll. Если ты делал Rebase program (Глава 3) на 0x0, то оффсеты будут именно такими. Если нет — нужно вычесть 0x180000000.
Пример 2: Быстрый способ найти dwLocalPlayerPawn
Шаг 1: Ищем строку
Открываем Shift+F12 (Strings), ищем:
Код:
"Deathcam.Review_Start"
Шаг 2: Переходим по Xref
Наводимся на любую строку из результатов, жмем X. Переходим по одному из xref.
Шаг 3: Смотрим ассемблер
В дизассемблере видим такой код:
Код:
xor edi, edi
xor ecx, ecx
mov [rbx+14h], rdi
call sub_1808E7880 ; <-- Нас интересует этот вызов
mov rsi, rax
test rax, rax
jz short loc_1804BD10D
Шаг 4: Переходим в функцию
Видишь call sub_1808E7880? Переходим в эту функцию (дважды кликнуть по sub_1808E7880). И вуаля — мы внутри той же функции, что и в первом примере.
Бонус: Таким способом ты находишь не только dwEntityList, но и dwLocalPlayerController (локальный контроллер).
Пример 3: Находим ViewMatrix
Шаг 1: Открываем окно имен
Нажимаем Shift+F4 (или View → Open subviews → Names).
Шаг 2: Ищем VTable
В поиске вводим:
Код:
"CRenderGameSystem::`vftable'"
Шаг 3: Ищем нужную функцию
Листаем VTable до конца. Нас интересует 64-й индекс (или около того). Выглядеть это будет примерно так:
Код:
.data:00000001823B2C60 dq offset sub_1803E9750
Шаг 4: Переходим в функцию
Дважды кликаем по sub_1803E9750. Смотрим код:
Код:
sub_1803E9750 proc near
arg_10= byte ptr 18h
sub rsp, 28h
mov rcx, cs:qword_18230DF10
lea rdx, [rsp+28h+arg_10]
mov rax, [rcx]
call qword ptr [rax+300h]
mov ecx, [rax]
call sub_180B7D5F0
add rsp, 28h
retn
sub_1803E9750 endp
Шаг 5: Что здесь?
Функция sub_180B7D5F0 — это и есть получение ViewMatrix. Дальше нужно анализировать эту функцию (поиск глобальной переменной с матрицей, смещений). ViewMatrix лежит в client.dll по определенному смещению.
Глава 3.3: Создание паттернов (сигнатур) для функций
Когда ты нашел функцию или глобальную переменную, нельзя просто взять и забить её адрес в чит. После каждого обновления CS2 адреса меняются. Для этого нужны паттерны (сигнатуры) — уникальные последовательности байтов, которые не меняются от обновления к обновлению.
Что такое паттерн?
Паттерн — это строка байтов в шестнадцатеричном виде, где некоторые байты заменены на ? (wildcard). Поисковая система будет игнорировать байты с ? и искать только уникальные.
И так же бывает 2 вида паттерна:
1. Call - В паттерне находится адрес нашей функции (Динамичный), к примеру - "E8 ? ? ? ? 48 89 44 24 ? 48 8B F8 48 85 C0 0F 84" находится адрес, который меняется с каждым апдейтом или запуском игры, для того чтобы не обновлять его каждый раз, мы используем паттерн, когда чит находит паттерн, он смотрит на его память и видет: "E8 72 50 E1 FF 48 89 44 24 ? 48 8B F8 48 85 C0 0F 84", и мы просто берем адрес из 72 50 E1 FF и все.
1. Hook - Этот паттерн отвечает не за адрес, а за хук, мы его всегда делаем в первых строках функции, потому что когда паттерн или хук вызывается игрой, начинает вызываться функция, мы хукнув эту функцию - может делать с функцией все что угодно, например для анти смока, можем отключить рендер, ну вы поняли.
Пример паттерна в читаемом виде:
В коде это выглядит так:
Как пользоваться Sigmaker
Шаг 1: Устанавливаем Sigmaker
Плагин уже должен быть установлен (по инструкции из Главы 1). Если нет — скачай с GitHub и положи в папку plugins IDA.
Шаг 2: Находим нужную функцию
Переходим к функции, на которую хотим сделать паттерн (например, sub_180ADF9D0).
Шаг 3: Выделяем байты
Кликаем на первую инструкцию функции. Зажимаем Shift и кликаем на последнюю инструкцию (или просто кликаем на имя функции в списке функций слева).
Шаг 4: Запускаем Sigmaker
Правой кнопкой мыши по выделенному коду → Make signature (или Sigmaker → Make signature).
Шаг 5: Настраиваем параметры
В открывшемся окне:
Нажимаем OK. Sigmaker сгенерирует паттерн вида:
Копируем его в буфер обмена.
Шаг 7: Проверяем уникальность
Нажимаем Alt+B (Search → Sequence of bytes). Вставляем паттерн с ?. Нажимаем Search. Если нашлось одно совпадение — отлично. Если несколько — нужно удлинить паттерн или взять другие байты.
Как делать паттерны вручную (без Sigmaker)
Иногда Sigmaker генерирует слишком длинный паттерн. Вот как сделать его вручную:
Открываем дизассемблер функции
Берем первые 20-30 байт кода
Заменяем все адреса (call, jmp, mov с адресами) на ?
Проверяем уникальность через Alt+B
Пример как найти паттерн для CreateMove:
И так, чтобы сделать паттерн, надо выделить необходимую строку, вот например у CreateMove я выбрал такие (Самые первые):
И нажать надо CTRL+ALT+S, После чего нужны такие настройки:
И нажимаем ок, после чего у нас в окне output появляется паттерн, это и есть наш CreateMove!
В байтах:
Где брать байты для паттерна?
В IDA есть два основных способа смотреть байты:
В дизассемблере — байты показаны слева от кода
Hex-вид — View → Open subviews → Hex dump
Как пользоваться Class Informer для поиска индексов функций
Плагин уже установлен (Глава 1). Теперь как им пользоваться:
Шаг 1: Открываем Class Informer
View → Open subviews → Class Informer (или Shift+F11).
Шаг 2: Ищем нужный класс
В поиске вводим название класса, например "CCSGOInput".
Шаг 3: Смотрим VTable
Клик по классу открывает список виртуальных функций. Видно что-то вроде:
Индексы начинаются с 0. Чтобы перейти в функцию — дважды кликаем по ней.
Шаг 4: Копируем индекс
Индекс нужен для VTable хуков. Например, CreateMove имеет индекс 4 или 5 (в зависимости от класса).
Готовые методы поиска паттернов (по строкам и Xref)
Ниже приведены способы найти популярные функции и переменные в CS2. Для каждого метода: ищем строку → переходим по Xref → делаем паттерн на указанные байты.
Game Rules
Ищем строку "CGameRules::CGameRules constructed" → выбираем первую → переходим по Xref на 3 панель. Перед "LoggingSystem_IsChannelEnabled" будет:
Это и есть GameRules.
Local Player Controller
Ищем "snd_soundmixer" в Strings → находим Xref (третий или тот, где большая графа). Там ищем:
Это локальный контроллер.
Entity List
Ищем "CEntitySystem::RegisterComponentType" в Strings → переходим по Xref. Листаем выше до панели с "cs:?Init@CUtlScratchMemoryPool@@..." → ниже ищем:
Это EntityList.
View Matrix
Ищем строку "C:\\buildworker\\csgo_rel_win64\\build\\src\\game\\client\\view.cpp" → переходим по первому Xref. В той же панели ищем:
Ищем функцию sub_B8C6D0 (там будет mov byte ptr [r14+12F0h], 1). Это и есть паттерн для ViewMatrix.
Planted C4
В Class Informer находим C_C4 (иерархия: C_C4: C_CSWeaponBase, C_BasePlayerWeapon, C_EconEntity...). Переходим по Xref в ??_7C_C4@@6B@ → по первому Xref. Ищем графу с вызовом:
Это паттерн для проверки заложенной бомбы.
Frame Stage Notify
Ищем "FramePostDataUpdate(%.3f %d)" в Strings → переходим по Xref. Выше будет:
Делаем на него паттерн.
Override View
Ищем "??_7CCSCameraManager@@6B@_0" в Strings → переходим к 3 Xref. В графе самый первый mov:
Это override view.
Level Init (GameNewMap)
Ищем "game_newmap" в Strings → переходим по Xref. Ищем графу, где lea rdx, aGameNewmap вызывается в самой верхней панели. Ищем push (например, push rbp). Делаем на него паттерн.
Level Shutdown
Ищем "map_shutdown" в Strings → переходим по Xref. В самом начале:
Это паттерн.
Update Post Processing
Ищем "downloaded" в Strings → ищем графу, где рядом есть "live", "inmemory", "tournament:". Идем вверх, во второй панели самое начало — сигнатура:
Remove Legs
Ищем "FirstpersonLegsPrepass" в Strings → переходим по Xref → листаем в самый верх. Копируем первые строки:
Mark Interp Latch Flags Dirty
Ищем "%s(%s[%d): MarkInterpolationLatchFlagsDirty: C_BaseEntity::IsLat" → переходим по Xref → листаем вверх. Копируем первые строки:
Draw Scope Overlay
Ищем "hideParticles" в Strings → переходим по 3 Xref. В графе ниже находим панель:
Переходим в sub_861E70. Делаем паттерн на первую строку:
Get FOV
В Class Informer находим CCSPlayerBase_CameraServices → ищем 30 индекс → переходим. В первой панели графы копируем:
Handle Bullet Penetration
Ищем "weapon_taser" → переходим по call'ам. В новой графе первая панель:
Паттерн подходит полностью.
Create Move
В Class Informer находим CCSGOInput (иерархия: CCSGOInput: CClientInput, IKeybindChangeListener, IInputHandler). 5 индекс — CreateMove. Заходим внутрь. Копируем первые строки:
Делаем паттерн.
Update Global Vars
В Class Informer находим CSource2Client → 11 индекс. Переходим. Делаем паттерн на первую строку:
On Add Entity
В Class Informer находим CGameEntitySystem → индекс 14. Переходим. Делаем паттерн на первую строку:
On Remove Entity
То же самое, что On Add Entity, но индекс 15.
Update Skybox
В Class Informer находим C_EnvSky → индекс 12. Переходим в функцию. Ищем последнюю панель в графе. Там строка:
Переходим внутрь sub_180267A80. Делаем паттерн на первые строки.
Draw Smoke
Ищем "IS_MODE_MIXED_RESOLUTION_MBOIT" в Strings → переходим по первому Xref. Открываем графу, идем к первой панели. Первая строка:
Это паттерн.
Unlock Inventory
Ищем "IsLoadoutAllowed" в Strings → переходим по Xref. Выше видим:
Переходим в sub_180F1E6D0. Во второй графе (ниже первой) первый call:
Переходим внутрь. Первая строка:
Это паттерн.
Is Demo Or HLTV
Ищем "GetCrosshairCode" в Strings → идем по первому Xref. Если ниже есть "IsDemoOrHltv" — вы на правильном месте. Опускаемся на 3 строки ниже:
Переходим в sub_180F1DDB0. Первая строка:
Это наша функция.
Get Map Name
Ищем "GetMapName" в Strings → переходим по первому Xref. Видим сверху другую структуру (например, "GetServerName"). Ищем первую lea в нашей структуре. Переходим — первая строка и есть наша функция.
Get Viewmodel
В Class Informer находим ClientModeCSNormal (иерархия: ClientModeCSNormal: ClientModeShared, IClientMode, CGameEventListener, IGameEventListener2, IMatchEventsSink). Индекс 27. Переходим. Во второй панели графы ищем первый call — это наша функция. Переходим внутрь. Делаем паттерн на первую строку:
Get Field of View
В Class Informer находим CCSPlayerBase_CameraServices: CPlayer_CameraServices, CPlayerPawnComponent. Индекс 31. Переходим. Делаем паттерн на первую строку:
Draw Viewpunch
То же самое, что Get Field of View, но индекс 30.
Краткий итог по главе 3.3
Sigmaker — плагин для автоматического создания паттернов
Class Informer — плагин для просмотра VTable и индексов функций
Паттерн должен быть уникальным (проверяй через Alt+B)
Адреса и call'ы заменяй на ?
Используй поиск по строкам → Xref → делай паттерн на начало функции или на нужную строку
Что такое паттерн?
Паттерн — это строка байтов в шестнадцатеричном виде, где некоторые байты заменены на ? (wildcard). Поисковая система будет игнорировать байты с ? и искать только уникальные.
И так же бывает 2 вида паттерна:
1. Call - В паттерне находится адрес нашей функции (Динамичный), к примеру - "E8 ? ? ? ? 48 89 44 24 ? 48 8B F8 48 85 C0 0F 84" находится адрес, который меняется с каждым апдейтом или запуском игры, для того чтобы не обновлять его каждый раз, мы используем паттерн, когда чит находит паттерн, он смотрит на его память и видет: "E8 72 50 E1 FF 48 89 44 24 ? 48 8B F8 48 85 C0 0F 84", и мы просто берем адрес из 72 50 E1 FF и все.
1. Hook - Этот паттерн отвечает не за адрес, а за хук, мы его всегда делаем в первых строках функции, потому что когда паттерн или хук вызывается игрой, начинает вызываться функция, мы хукнув эту функцию - может делать с функцией все что угодно, например для анти смока, можем отключить рендер, ну вы поняли.
Пример паттерна в читаемом виде:
Код:
48 8B 0D ? ? ? ? 48 89 5C 24 ? 48 8B D9
В коде это выглядит так:
Код:
"\x48\x8B\x0D\x00\x00\x00\x00\x48\x89\x5C\x24\x00\x48\x8B\xD9"
Как пользоваться Sigmaker
Шаг 1: Устанавливаем Sigmaker
Плагин уже должен быть установлен (по инструкции из Главы 1). Если нет — скачай с GitHub и положи в папку plugins IDA.
Шаг 2: Находим нужную функцию
Переходим к функции, на которую хотим сделать паттерн (например, sub_180ADF9D0).
Шаг 3: Выделяем байты
Кликаем на первую инструкцию функции. Зажимаем Shift и кликаем на последнюю инструкцию (или просто кликаем на имя функции в списке функций слева).
Шаг 4: Запускаем Sigmaker
Правой кнопкой мыши по выделенному коду → Make signature (или Sigmaker → Make signature).
Шаг 5: Настраиваем параметры
В открывшемся окне:
Нажимаем OK. Sigmaker сгенерирует паттерн вида:
Код:
48 8B 05 ? ? ? ? 48 89 44 24 ? 48 8B C1 48 8B 00 48 8B 40 08 48 8B 40 18 48 85 C0 74 ? 48 8B 00
Копируем его в буфер обмена.
Шаг 7: Проверяем уникальность
Нажимаем Alt+B (Search → Sequence of bytes). Вставляем паттерн с ?. Нажимаем Search. Если нашлось одно совпадение — отлично. Если несколько — нужно удлинить паттерн или взять другие байты.
Как делать паттерны вручную (без Sigmaker)
Иногда Sigmaker генерирует слишком длинный паттерн. Вот как сделать его вручную:
Открываем дизассемблер функции
Берем первые 20-30 байт кода
Заменяем все адреса (call, jmp, mov с адресами) на ?
Проверяем уникальность через Alt+B
Пример как найти паттерн для CreateMove:
И так, чтобы сделать паттерн, надо выделить необходимую строку, вот например у CreateMove я выбрал такие (Самые первые):
И нажать надо CTRL+ALT+S, После чего нужны такие настройки:
И нажимаем ок, после чего у нас в окне output появляется паттерн, это и есть наш CreateMove!
Код:
test edx, edx
jnz locret_AC753D
В байтах:
Код:
85 D2 0F 85 ? ? ? ? 48 8B C4 44 88 40
Где брать байты для паттерна?
В IDA есть два основных способа смотреть байты:
В дизассемблере — байты показаны слева от кода
Hex-вид — View → Open subviews → Hex dump
Как пользоваться Class Informer для поиска индексов функций
Плагин уже установлен (Глава 1). Теперь как им пользоваться:
Шаг 1: Открываем Class Informer
View → Open subviews → Class Informer (или Shift+F11).
Шаг 2: Ищем нужный класс
В поиске вводим название класса, например "CCSGOInput".
Шаг 3: Смотрим VTable
Клик по классу открывает список виртуальных функций. Видно что-то вроде:
Код:
0: sub_180XXXXXX
1: sub_180XXXXXX
2: sub_180XXXXXX
3: sub_180XXXXXX
4: sub_180ADF9D0 // CreateMove
5: sub_180XXXXXX
...
Индексы начинаются с 0. Чтобы перейти в функцию — дважды кликаем по ней.
Шаг 4: Копируем индекс
Индекс нужен для VTable хуков. Например, CreateMove имеет индекс 4 или 5 (в зависимости от класса).
Готовые методы поиска паттернов (по строкам и Xref)
Ниже приведены способы найти популярные функции и переменные в CS2. Для каждого метода: ищем строку → переходим по Xref → делаем паттерн на указанные байты.
Game Rules
Ищем строку "CGameRules::CGameRules constructed" → выбираем первую → переходим по Xref на 3 панель. Перед "LoggingSystem_IsChannelEnabled" будет:
Код:
mov cs:qword_231AF60, rbx
Это и есть GameRules.
Local Player Controller
Ищем "snd_soundmixer" в Strings → находим Xref (третий или тот, где большая графа). Там ищем:
Код:
mov rax, cs:qword_2301208
Это локальный контроллер.
Entity List
Ищем "CEntitySystem::RegisterComponentType" в Strings → переходим по Xref. Листаем выше до панели с "cs:?Init@CUtlScratchMemoryPool@@..." → ниже ищем:
Код:
mov cs:qword_21D90D0, rsi
Это EntityList.
View Matrix
Ищем строку "C:\\buildworker\\csgo_rel_win64\\build\\src\\game\\client\\view.cpp" → переходим по первому Xref. В той же панели ищем:
Код:
call sub_AE6470
lea rax, [rsp+0C8h+var_58]
mov byte ptr [r14+12F0h], 1
lea rcx, unk_231CFA0
mov [rsp+0C8h+var_A0], rax
mov [rsp+0C8h+var_A8], rcx
lea r9, unk_231CF60
mov rcx, cs:qword_21D9968
lea r8, unk_231CF20
lea rdx, [r14+10h]
call sub_3BF900
Ищем функцию sub_B8C6D0 (там будет mov byte ptr [r14+12F0h], 1). Это и есть паттерн для ViewMatrix.
Planted C4
В Class Informer находим C_C4 (иерархия: C_C4: C_CSWeaponBase, C_BasePlayerWeapon, C_EconEntity...). Переходим по Xref в ??_7C_C4@@6B@ → по первому Xref. Ищем графу с вызовом:
Код:
mov rdx, cs:qword_22A6410
Это паттерн для проверки заложенной бомбы.
Frame Stage Notify
Ищем "FramePostDataUpdate(%.3f %d)" в Strings → переходим по Xref. Выше будет:
Код:
mov r9, cs:off_182062540
Делаем на него паттерн.
Override View
Ищем "??_7CCSCameraManager@@6B@_0" в Strings → переходим к 3 Xref. В графе самый первый mov:
Код:
mov [rsp+arg_0], rbx
Это override view.
Level Init (GameNewMap)
Ищем "game_newmap" в Strings → переходим по Xref. Ищем графу, где lea rdx, aGameNewmap вызывается в самой верхней панели. Ищем push (например, push rbp). Делаем на него паттерн.
Level Shutdown
Ищем "map_shutdown" в Strings → переходим по Xref. В самом начале:
Код:
sub rsp, 28h
Это паттерн.
Update Post Processing
Ищем "downloaded" в Strings → ищем графу, где рядом есть "live", "inmemory", "tournament:". Идем вверх, во второй панели самое начало — сигнатура:
Код:
sub rsp, 28h
Remove Legs
Ищем "FirstpersonLegsPrepass" в Strings → переходим по Xref → листаем в самый верх. Копируем первые строки:
Код:
push rbp
push rbx
push rsi
push r14
push r15
lea rbp, [rsp-460h]
sub rsp, 560h
movsd xmm0, qword ptr [rdx+1Ch]
Mark Interp Latch Flags Dirty
Ищем "%s(%s[%d): MarkInterpolationLatchFlagsDirty: C_BaseEntity::IsLat" → переходим по Xref → листаем вверх. Копируем первые строки:
Код:
push rbx
push rsi
push rdi
sub rsp, 40h
cmp cs:byte_23128D0, 0
Draw Scope Overlay
Ищем "hideParticles" в Strings → переходим по 3 Xref. В графе ниже находим панель:
Код:
lea rdx, [rbp+1EE0h+var_14B0]
mov rcx, rax
call sub_861E70
Переходим в sub_861E70. Делаем паттерн на первую строку:
Код:
mov rax, rsp
Get FOV
В Class Informer находим CCSPlayerBase_CameraServices → ищем 30 индекс → переходим. В первой панели графы копируем:
Код:
push rbx
Handle Bullet Penetration
Ищем "weapon_taser" → переходим по call'ам. В новой графе первая панель:
Код:
mov rax, rsp
mov [rax+20h], r9d
push rbp
push rdi
push r13
push r15
lea rbp, [rax-178h]
sub rsp, 258h
test byte ptr [r8+14h], 1
xorps xmm1, xmm1
Паттерн подходит полностью.
Create Move
В Class Informer находим CCSGOInput (иерархия: CCSGOInput: CClientInput, IKeybindChangeListener, IInputHandler). 5 индекс — CreateMove. Заходим внутрь. Копируем первые строки:
Код:
test edx, edx
jnz locret_180AE0A5D
Делаем паттерн.
Update Global Vars
В Class Informer находим CSource2Client → 11 индекс. Переходим. Делаем паттерн на первую строку:
Код:
mov rcx, cs:off_182062540
On Add Entity
В Class Informer находим CGameEntitySystem → индекс 14. Переходим. Делаем паттерн на первую строку:
Код:
mov [rsp+arg_8], rsi
On Remove Entity
То же самое, что On Add Entity, но индекс 15.
Update Skybox
В Class Informer находим C_EnvSky → индекс 12. Переходим в функцию. Ищем последнюю панель в графе. Там строка:
Код:
lea rax, sub_180267A80
Переходим внутрь sub_180267A80. Делаем паттерн на первые строки.
Draw Smoke
Ищем "IS_MODE_MIXED_RESOLUTION_MBOIT" в Strings → переходим по первому Xref. Открываем графу, идем к первой панели. Первая строка:
Код:
mov [rsp-8+arg_8], rdx
Это паттерн.
Unlock Inventory
Ищем "IsLoadoutAllowed" в Strings → переходим по Xref. Выше видим:
Код:
lea rdx, sub_180F1E6D0
Переходим в sub_180F1E6D0. Во второй графе (ниже первой) первый call:
Код:
call sub_18070CE10
Переходим внутрь. Первая строка:
Код:
mov [rsp+arg_0], rbx
Это паттерн.
Is Demo Or HLTV
Ищем "GetCrosshairCode" в Strings → идем по первому Xref. Если ниже есть "IsDemoOrHltv" — вы на правильном месте. Опускаемся на 3 строки ниже:
Код:
lea rax, sub_180F1DDB0
Переходим в sub_180F1DDB0. Первая строка:
Код:
sub rsp, 28h
Это наша функция.
Get Map Name
Ищем "GetMapName" в Strings → переходим по первому Xref. Видим сверху другую структуру (например, "GetServerName"). Ищем первую lea в нашей структуре. Переходим — первая строка и есть наша функция.
Get Viewmodel
В Class Informer находим ClientModeCSNormal (иерархия: ClientModeCSNormal: ClientModeShared, IClientMode, CGameEventListener, IGameEventListener2, IMatchEventsSink). Индекс 27. Переходим. Во второй панели графы ищем первый call — это наша функция. Переходим внутрь. Делаем паттерн на первую строку:
Код:
push rbp
Get Field of View
В Class Informer находим CCSPlayerBase_CameraServices: CPlayer_CameraServices, CPlayerPawnComponent. Индекс 31. Переходим. Делаем паттерн на первую строку:
Код:
push rbx
Draw Viewpunch
То же самое, что Get Field of View, но индекс 30.
Краткий итог по главе 3.3
Sigmaker — плагин для автоматического создания паттернов
Class Informer — плагин для просмотра VTable и индексов функций
Паттерн должен быть уникальным (проверяй через Alt+B)
Адреса и call'ы заменяй на ?
Используй поиск по строкам → Xref → делай паттерн на начало функции или на нужную строку
Финал гайда
Что мы изучили
Глава Тема
1 Установка IDA Pro и плагинов
2 Основы: Xref, функции, VTable
2.1 Краткий ликбез по ассемблеру
2.2 Псевдокод и его понимание
2.3 WorldToScreen, ViewMatrix, EntityList
3 Загрузка client.dll в IDA
3.1 Поиск полей классов (оффсетов) через строки
3.2 Поиск глобальных оффсетов (dwEntityList, LocalPlayer и др.)
3.3 Создание паттернов (сигнатур) для функций
Глава Тема
1 Установка IDA Pro и плагинов
2 Основы: Xref, функции, VTable
2.1 Краткий ликбез по ассемблеру
2.2 Псевдокод и его понимание
2.3 WorldToScreen, ViewMatrix, EntityList
3 Загрузка client.dll в IDA
3.1 Поиск полей классов (оффсетов) через строки
3.2 Поиск глобальных оффсетов (dwEntityList, LocalPlayer и др.)
3.3 Создание паттернов (сигнатур) для функций
Последнее редактирование:
