- Статус
- Онлайн
- Регистрация
- 22 Авг 2022
- Сообщения
- 1,184
- Реакции
- 164
В прошлом гайде мы научились получать сущностей. В этом гайде я расскажу как создать ESP бокс используя Bounds (AKA AxisAlignedBoundingBox). Подробнее о том, как это устроено я рассказывал в этой теме
Камера
Камера лежит в статичном поле класса MainCamera. Как всегда они сюда накидали несколько камер, трансформов и тд:
Работаем по старой схеме:
Переходим по RVA
Заходим в любой хреф и получаем необходимый оффсет.
Теперь у нас есть камера. Для World to Screen функции проекции точки на экран необходима view * projection матрица. Я нашёл projection matrix:
и остальные. Но при переходе в данную функцию я вижу resolve icall:
icall ищет адрес функции по названию из UnityPlayer.dll. Данная dll лежит рядом с игрой и для неё доступны debug символы - pdb. Скачать их можно через любую утилиту, я лично
После загрузки длл с дебаг символами нам доступны все названия функций.
На самом деле есть матрица, которая сразу считает view * projection - она называется World to Clip. Сохраняется она в функции Camera::GetCullingMatrix по оффсету 0x30C:
В функции GetWorldToClipMatrix идёт перемножение view матрицы (по оффсету 0x80) на projection (полученной в результате функции
Перемножение тут с использованием SSE. Первым аргументом если что идёт внутренний указатель на камеру, как его получать я писал ниже (уже лень переписывать)
Сортировка сущностей
Каждый раз при чтении сущности из массива мы получаем указатель на структуру BaseNetworkable_o:
Самое первое поле - klass - это указатель на typeinfo класса. Таким образом мы можем прочитать этот klass и сравнить его с typeinfo BasePlayer'а.
Коробка
Основной элемент есп это конечно же 2д (а иногда 3д) коробка. Многие читы добиваются её эффекта вычисляя позицию ног и головы, однако я считаю такой способ кринжовым и поддерживаю более верный способ через хитбоксы. В unity, Axis Aligned Bounding Box называется структурой Bounds, которая состоит из center и extents вектора. Там также есть функции получения вершин минимума и максимума. Не вижу смысла показывать некрасивый SSE код, покажу сразу их реализацию:
Поле bounds лежит в классе BaseEntity.
Далее по принципу из, повторюсь, этой темы вычисляем коробку. Единственное с чем вы можете столкнуться, так это с получением поворота и позиции Transform.
В unity компонент Transform есть у каждого игрового объекта. Для его получения мы можем изучить функцию GetTransformFromComponentOrGameObject
Сразу скажу, что функция принимает внутренний object и отдаёт тоже внутреннюю структуру Transform, а не тот класс, что мы видим в il2cpp дампе. Получить внутренний object от сущности можно прочитав его через entity+0x10. 0x10 это самое первое поле любого класса. Объясню почему так, как я уже сказал, каждая сущность - это указатель на BaseNetworkable_o:
Структура BaseNetworkable_Fields имеет очень много наследований:
BaseNetworkable_Fields -> BaseMonoBehaviour_Fields -> FacepunchBehaviour_Fields -> UnityEngine_MonoBehaviour_Fields -> UnityEngine_Behaviour_Fields -> UnityEngine_Component_Fields -> UnityEngine_Object_Fields
что в конечном итоге приведёт нас к UnityEngine_Object_Fields без наследований, где первое поле это m_CachedPtr - указатель на внутренний object игры.
Получить позицию и ротацию можно через функцию Transform::GetPositionAndRotation:
которая внутри вызывает функцию CalculateGlobalPositionAndRotation, передавая в первый аргумент transform+0x38. Результат должен записаться по указателям во второй и третий аргумент.
Внутри функция выглядит как-то так, дописываю с телефона, поэтому полного скриншота нету. Но логика такая:
1. В a1+8 лежит индекс текущего трансформа в массиве трансформов
2. Сам a1 указывает на внутреннюю структуру в которой +0x18 возвращает массив трансформов по 48 байт каждый (vector4f позиция, quaternionf поворот и vector4f размер)
3. Из v4+0x20 мы найдем индексы прошлых трансформов (например если бы это была рука, то надо было найти прошлые трансформы: человека, мира и тд). Используя индекс полученный из пункта 1 мы получим индекс прошлого трансформа
4. Если индекс прошлого трансформа >= 0 то мы перечисляем все дочерние трансформы, прибавляем позицию, перемножаем поворот и применяем размер, пока предыдущий трансформ ещё существует
5. Возвращаем результат после всех операций
Таким образом мы сможем использовать эти значения для воссоздания матрицы трансформа, чтобы в дальнейшем перемножить её на вершину в aabb.
Камера
Камера лежит в статичном поле класса MainCamera. Как всегда они сюда накидали несколько камер, трансформов и тд:
Работаем по старой схеме:
Переходим по RVA
Заходим в любой хреф и получаем необходимый оффсет.
Теперь у нас есть камера. Для World to Screen функции проекции точки на экран необходима view * projection матрица. Я нашёл projection matrix:
и остальные. Но при переходе в данную функцию я вижу resolve icall:
icall ищет адрес функции по названию из UnityPlayer.dll. Данная dll лежит рядом с игрой и для неё доступны debug символы - pdb. Скачать их можно через любую утилиту, я лично
Пожалуйста, авторизуйтесь для просмотра ссылки.
После загрузки длл с дебаг символами нам доступны все названия функций.
На самом деле есть матрица, которая сразу считает view * projection - она называется World to Clip. Сохраняется она в функции Camera::GetCullingMatrix по оффсету 0x30C:
В функции GetWorldToClipMatrix идёт перемножение view матрицы (по оффсету 0x80) на projection (полученной в результате функции
Camera::GetProjectionMatrix):Перемножение тут с использованием SSE. Первым аргументом если что идёт внутренний указатель на камеру, как его получать я писал ниже (уже лень переписывать)
Сортировка сущностей
Каждый раз при чтении сущности из массива мы получаем указатель на структуру BaseNetworkable_o:
Самое первое поле - klass - это указатель на typeinfo класса. Таким образом мы можем прочитать этот klass и сравнить его с typeinfo BasePlayer'а.
Коробка
Основной элемент есп это конечно же 2д (а иногда 3д) коробка. Многие читы добиваются её эффекта вычисляя позицию ног и головы, однако я считаю такой способ кринжовым и поддерживаю более верный способ через хитбоксы. В unity, Axis Aligned Bounding Box называется структурой Bounds, которая состоит из center и extents вектора. Там также есть функции получения вершин минимума и максимума. Не вижу смысла показывать некрасивый SSE код, покажу сразу их реализацию:
Поле bounds лежит в классе BaseEntity.
Далее по принципу из, повторюсь, этой темы вычисляем коробку. Единственное с чем вы можете столкнуться, так это с получением поворота и позиции Transform.
В unity компонент Transform есть у каждого игрового объекта. Для его получения мы можем изучить функцию GetTransformFromComponentOrGameObject
Сразу скажу, что функция принимает внутренний object и отдаёт тоже внутреннюю структуру Transform, а не тот класс, что мы видим в il2cpp дампе. Получить внутренний object от сущности можно прочитав его через entity+0x10. 0x10 это самое первое поле любого класса. Объясню почему так, как я уже сказал, каждая сущность - это указатель на BaseNetworkable_o:
Структура BaseNetworkable_Fields имеет очень много наследований:
BaseNetworkable_Fields -> BaseMonoBehaviour_Fields -> FacepunchBehaviour_Fields -> UnityEngine_MonoBehaviour_Fields -> UnityEngine_Behaviour_Fields -> UnityEngine_Component_Fields -> UnityEngine_Object_Fields
что в конечном итоге приведёт нас к UnityEngine_Object_Fields без наследований, где первое поле это m_CachedPtr - указатель на внутренний object игры.
Получить позицию и ротацию можно через функцию Transform::GetPositionAndRotation:
которая внутри вызывает функцию CalculateGlobalPositionAndRotation, передавая в первый аргумент transform+0x38. Результат должен записаться по указателям во второй и третий аргумент.
Внутри функция выглядит как-то так, дописываю с телефона, поэтому полного скриншота нету. Но логика такая:
1. В a1+8 лежит индекс текущего трансформа в массиве трансформов
2. Сам a1 указывает на внутреннюю структуру в которой +0x18 возвращает массив трансформов по 48 байт каждый (vector4f позиция, quaternionf поворот и vector4f размер)
3. Из v4+0x20 мы найдем индексы прошлых трансформов (например если бы это была рука, то надо было найти прошлые трансформы: человека, мира и тд). Используя индекс полученный из пункта 1 мы получим индекс прошлого трансформа
4. Если индекс прошлого трансформа >= 0 то мы перечисляем все дочерние трансформы, прибавляем позицию, перемножаем поворот и применяем размер, пока предыдущий трансформ ещё существует
5. Возвращаем результат после всех операций
Таким образом мы сможем использовать эти значения для воссоздания матрицы трансформа, чтобы в дальнейшем перемножить её на вершину в aabb.