Перейти к содержанию

[CS:GO] Поиск видовой матрицы + сигнатура


Рекомендуемые сообщения

Я не буду здесь объяснять, что такое видовая матрица и для чего она нужна - для этого есть специальные книжки и статьи (раз, два)

Обычный Step-by-Step гайд с пояснениями.

Искать будем в Counter-Strike: Global Offensive, но в принципе подойдет и любая другая игра.

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

 

Значит так, сначала цепляемся к процессу игры, затем устанавливаем параметры поиска:

    Scan Type: Value between... 

    Value Type: Float

Почему именно так? Дело в том, что точное значение искать довольно рискованно.

Во-первых, флоат вообще довольно неточная штука, а во-вторых - мы не знаем точного угла нашей камеры.

Например, в csgo угол наклона (pitch) варьируется от -89.0 до 89.0 градусов , а в других играх может и прямой угол получаться, т.е. 90 градусов.

Теперь опускаем камеру до упора вниз, так что наш угол наклона получится 89 градусов. По идее, в видовой матрице это значение отобразится как ~0,999, но мы все равно будем искать диапазон.

Ну а теперь выставляем диапазон: от 0.9 до 1.1 и сканируем

u2DxHtH.png

Нам тут же нашло овер9000 значений, но ща мы их отсеим. Камеру до упора вверх и сканим теперь следующий диапазон:

8jiWAt8.png

Оп-па. Теперь значений осталось всего-ничего: 521. Нет, это конечно много, но не настолько. Можно конечно по приколу отсеить те значения, что изменились за время чтения данной строки, но тут может поджидать сюрприз: в некоторых играх камера может немного "ходить" туда-сюда. Но если вы уверены в том, что она статична, то флаг вам в руки.

Сейчас осталось только и делов: отсеить весь хлам, ну а дальше будем смотреть на найденное и анализировать.

Можно так же до упора туда-сюда камеру водить, но я сделал так: направил ее примерно параллельно земле и прожал скан от -0.2 до 0.2.

После пары-тройки дополнительных сканов у меня осталось примерно 60 значений. Многовато для ручного перебора, но я знаю, что мне нужны только "зеленые", т.е. статические адреса.

Пролистав чуть ниже, я тут же натолкнулся на целый ряд таких значений:

OrB4FX0.png

Выносим все эти ячейки в список адресов и начинаем их перебирать: ПКМ по первому же адресу -> Browse this memory region ( Ctrl + B )

Выставляем тип отображаемых значений на float, чтобы проще было наблюдать за всеми значениями матрицы. ПКМ -> Display Type -> Float ( Ctrl + 9 )

Смотрим на первую матрицу:

OMiU8MF.png

На всю матрицу у нас должно быть только 3-4 больших значения и все они должны быть в одной колонке. Ну или так: каждое четвертое значение имеет большую величину. Желательно сразу их выровнять по 4 колонке. Остальные значения находятся в диапазоне от 0 до +-1.33

Тут мы видим, что она явно какая-то не такая: Nan-значения, остальные нули и тд.

Но даже если она выглядит более-менее правдиво, то достаточно подвигать туда сюда камерой ( + побегать! ), чтобы убедиться в том что это не она: только два значения меняются, а остальные статичны.

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

Проделываем те же действия с другими матрицами: оцениваем их визуально + меняем состояние камеры в игре и смотрим на значения.

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

YSQKKDF.png

Фиолетовым я выделил адреса второй и третьей матрицы, а между ними зеленым - интересующий участок. Как видите, здесь есть 4 довольно больших значения ( на фоне других ) и одно из них (0x1F97E26C) остается неизменным. Но в других играх оно может немного меняться: оно отвечает за угол поворота ( по часовой ). Это в тех играх, где камера туда-сюда наклоняется, например, при передвижении пресонажа, но оно в таком случае все равно не будет сильно большим.

Теперь выделяем предполагаемое начало матрицы, жмем ПКМ->Add this to address list и копируем адрес. Теперь Ctrl + G и переходим по скопированному адресу. Смотрим:

AYoooHH.png

Ба! Выглядит отлично. Значения не сравнивайте между скринами, за это время я немного подвигал камеру опять, поэтому они изменились.

Ну, оффсет для статического адреса сделать вообще не сложно: берем адрес матрицы и вычитаем из него базовый адрес модуля: 

0x1F97E264 - client.dll 0x4A7E264

Если все еще не понятно, как таки найти view matrix, то можете посмотреть видеоурок от Guided Hacking: https://www.youtube.com/watch?v=-WL1Gpe9VRo

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

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

Добавляем первое значение нашей матрицы в список адресов и ставим бряк.

n7x3jaq.png

Тут же ловим кучу инструкций, которые взаимодействуют с нашей матрицей:

MmUpBRh.png

Берем самую первую и смотрим:

X7hfeE5.png

Это похоже на какой-то метод класса матрицы, который может использоваться не только нашей матрицей, но и другими. Что-то типа оператора присвоения.

Если поставить бряк на эту инструкцию, то можно в этом убедиться: в регистр ecx попадает не только "наш" адрес, но и куча других. Что же делать? Все просто: будем "раскручивать" цепочку вызовов функций.

Ставим Breakpoint ( F5 ) на инструкцию push ebp. Это начало функции. Затем ПКМ по ней же и выбираем "Set/Change break condition" и выставляем фильтр на брейкпоинте, чтобы он остановился когда функция будет работать с нашей матрицей.

oJLmxuz.png

Тут, конечно же, адрес вашей матрицы вместо моего.

И в тот же миг "ловим" наш поток.

Смотрим на регистры и на стек:

LqD90tU.png

В регистре ecx наша матрица, стек разворачиваем на "полную" и видим такую картину:

tBWS12z.png

Это адрес функции, которая нас вызвала. В чем суть вообще того, что мы сейчас делаем? Все дело в том, что нам надо выйти на ту единственную функцию, которая работает ТОЛЬКО с нашим адресом. Т.е. там, где все начинается. Там же, вероятно, мы и сможем узнать откуда игра берет адрес этой матрицы и мы сделаем сигнатурку. Значит так, теперь снимаем наш брейкпоинт и прыгаем по адресу из нашего стека: client.dll + 67379B

Отлично. Что мы видим?

nG49HTO.png

А, ну это соответственно тот самый вызов функции, из которой мы пришли. Можно тут так же поставить брейкпоинт и убедиться, что функция всё так же работает с несколькими матрицами.

Так, а с каким регистром-то у нас там работали инструкции? С ecx, точно. Смотрим буквально на пару строк выше и видим инструкцию:

lea ecx, [edi+00000284]

Похоже, тут в наш регистр загружается адрес матрицы. Хм, а сама матрица тоже лежит в какой-то структуре, адрес которой лежит в edi. А значит теперь нам надо искать ту инструкцию, которая загружает в регистр edi адрес 0x1F97DFE0.

Это уже адрес нашей структуры, т.е. ( 0x1F97E264 - 0x284 ). Теперь ищем по нему. Прокрутив в самый верх функции можно увидеть, что в edi значение перекладывается из регистра ecx.

client.dll+673740 - 55                    - push ebp
client.dll+673741 - 8B EC                 - mov ebp,esp
client.dll+673743 - 81 EC 80000000        - sub esp,00000080
client.dll+673749 - 56                    - push esi
client.dll+67374A - 57                    - push edi
client.dll+67374B - 8B F9                 - mov edi,ecx

Теперь делаем то же самое, что и в предыдущий раз: брейкпоинт (на push ebp), condition, и прыгаем на предыдущую функцию. 

Тут наблюдаем такую картину:

nbnEui9.png

Фигассе, сказал я се.

Приехали, значение вытаскивается из стека) Можно попрыгать по функции и найти когда значение пушится в стек, но мне лень, поэтому я просто прокрутил функцию в самый верх и не прогадал:

поставив бряк на начало функции я тут же поймал в ecx прежний адрес структуры, которую мы пытаемся выследить. Но эта функция все еще работает с каким-то адресом помимо нашего, поэтому раскручиваем дальше: те же действия, прыгаем на предыдущую функцию.

Тут такая же ситуёвина, все делаем точь в точь, пока не наткнемся на ту самую функцию, где у нас бряк будет ловить в регистре только наш адрес.

А вот и она:

qazJg1X.png

Думаю, тут все ясно. Мы поймали разрабов с поличным: адресок-то прямо вот он, в ecx кладется. Двойной клик по строчке чтоб увидеть полный адрес:

client.dll+1F2C68 - B9 E0DF971F           - mov ecx,1F97DFE0

А вот и адрес нашей структуры.

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

104ykcl.png

Получаем такую строчку:

B9 E0 DF 97 1F 50 6A 00 6A 03 83 EC 08 8D 45 DC F3 0F 11 44 24 04 F3 0F 10 45 F8 F3 0F 11 04 24

И теперь "замазываем" наш адрес, который после перезапуска игры 100% поменяется.

B9 ?? ?? ?? ?? 50 6A 00 6A 03 83 EC 08 8D 45 DC F3 0F 11 44 24 04 F3 0F 10 45 F8 F3 0F 11 04 24

Все, сигнатура готова. У меня примерно так выглядит всё в коде:

pViewMatrix = (D3DXMATRIX*) CEngine::FindPattern( "client.dll", "B9 ?? ?? ?? ?? 50 6A 00 6A 03 83 EC 08 8D 45 DC F3 0F 11 44 24 04" );

if( pViewMatrix )
{
	pViewMatrix = (D3DXMATRIX*)( *(DWORD*)( (DWORD) pViewMatrix + 1 ) + 0x284 );
}

Да, не забываем, что это адрес СТРУКТУРЫ, но не матрицы. Матрица имеет своё, внутреннее смещение по структуре: 0x284.

Сканер сигнатур сами как-нибудь сделаете, это уже к кодингу относится ;) Да и на самом деле, эту сигнатуру можно использовать даже в CE.

Изменено пользователем uhx
  • Понравилось 1
  • Плюс 8
Ссылка на комментарий
Поделиться на другие сайты

  • 3 года спустя...
  • 2 недели спустя...
  • 1 месяц спустя...
  • 5 месяцев спустя...
×
×
  • Создать...

Важная информация

Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.