Подписывайтесь на наш Telegram и не пропускайте важные новости! Перейти

Гайд №1 BaseNetworkable - Поиск необходимого для создания экстернал чита на официальный Rust

PoC Life
Премиум
Премиум
Статус
Оффлайн
Регистрация
22 Авг 2022
Сообщения
1,177
Реакции
158
Оффтоп
2026 пиздато начинается. мне нравится. Гайд будет мелкий, я заебался всё это писать. Вторая часть хз когда, возможно через неделю.

Предисловие
Всем привет. Недавно мне улыбнулась удача написать чит на игру Rust. Игра собрана с помощью il2cpp и имеет рандомизацию имён в метадате il2cpp и шифрование некоторых вещей.

Как было раньше
Ранние версии игры (девблоги) не имели шифрования имён и читы там писать намного легче. Я сомневаюсь, что с того времени много что поменялось, поэтому давайте посмотрим как всё было устроено раньше.
Существует класс BaseNetworkable со статичным полем clientEntities:
old_basenetworkable.png

Внутри класса BaseNetworkable.EntityRealm содержится список из сущностей:
old_entityrealm.png

И из него уже можно вытащить все networkable объекты (сущности)

Информация из дампа il2cpp
Для начала я сдамплю il2cpp метадату используя инструмент
Пожалуйста, авторизуйтесь для просмотра ссылки.
. Как оказалось игра абсолютно не имеет никакого шифрования метадаты, как делают это другие игры по типу standoff 2 или genshin impact.
В последней версии игры нету никакого статичного поля, которое содержало бы clientEntities
new_basenetworkable.png

Однако BaseNetworkable.EntityRealm всё ещё существует в дампе:
new_entityrealm.png

И статически он находится в новом классе
new_basenetworkable_holder.png

С ходу бросаются пару мыслей:
- Почему некоторые типы, которые мы видели раньше (BaseNetworkable.EntityRealm, clientEntities) обёрнуты в какие-то классы?
- Какое конкретно поле с BaseNetworkable.EntityRealm выбрать из класса BaseNetworkable.%ca5d42befe66446a3fbc6568b3756c3773e80757?

Поиск и дешифрование BaseNetworkable экземпляра
Для того, чтобы найти чтение поля из BaseNetworkable.%ca5d42befe66446a3fbc6568b3756c3773e80757 (тот самый, что имеет несколько BaseNetworkable.EntityRealm) нужно посмотреть референсы к его TypeInfo

Для тех, кто не в курсе
В il2cpp для обращения к статическому полю необходимо найти указатель на TypeInfo класса, прочитать static_fields и от него обратиться к нужному статическому полю. Все необходимые структуры дампер создаёт в файле il2cpp.h:
basenetworkable_type_from_il2cpp_header.png


Продолжаем. Адрес с typeinfo лежит в файле script.json:
typeinfo_address.png

Не забываем перевести число в шестнадцатеричную систему счисления.

Снова для тех, кто не в курсе
Дампер имеет встроенные скрипты, для определения названий функций и типов для IDA (и не только). В замечательной папке с дампом лежит файл ida_with_struct_py3.py:
dump_folder.png

Загружаем его из контекстного меню:
ida_context_menu_load_script.png

Ожидаем примерно минут 10, пока не увидим следующее окно:
bad_declaration_error_after_plugin_logic_end.png

Просто нажимаем "Ок" и получаем красивое отображение в IDA.


Подготавливаем иду к работе
Для начала меняем image base на 0, чтобы переходить сразу по RVA. Сделать это можно в контекстком меню иды -> Edit -> Segments -> Rebase program -> вводим 0 -> Ок. Затем открываем вкладку экспортов и вводим il2cpp_gchandle_get_target. После перехода вас встретит джамп на некий sub:
il2cpp_gchandle_jmp.png

Переименновываем саб в j_il2cpp_gchandle_get_target


Теперь я буду иногда показывать по несколько скриншотов: со скриптом и без.
Переходим на адрес с typeinfo и видим следующее:
BaseNetworkable_data.png

Берём любой попавшийся xref и декомпилируем:
В 100% случаев вы увидите похожую картину:
(со скриптом)
with_script_BaseNetworkable_TypeInfo.png

(без)
static_fields_offset.png

Выделил 0xB8, я показывал его ранее - это оффсет на static_fields

Открываем эту функцию
(со скриптом)
decrypt_function_with_script.png


(без)
decrypt-function-no-script.png


Сразу понимаем, что поле, которое нам надо использовать лежит по оффсету 0x38

Нас интересует данный промежуток - это расшифровка:
decryption-routine.png

Ида может делать ошибки в определии типов, поэтому она добавляет свои уебанские макросы: HIDWORD, LODWORD и тд.
Поэтому выполняем следующие действия при попадании в функцию с дешифровкой:
1. Убираем типы с указателем в аргументах. В моём случае второй аргумент это указатель на __int64. Кликаем на него и жмём клавишу 'Y', чтобы убрать указатель (для тупых это звёздочка).
2. Если увидите HIDWORD(переменная), то жмём клавишу 'Y' на переменной и меняем её тип на unsigned int.
В итоге у вас должно получиться:
decryption-routine-fixed-result.png

В свой код переносим локальные переменные (голубой цвет) и необходимую область (красный цвет). В результат функции помещаем результат со скрина (фиолетовый цвет).
Мой код на данном этапе выглядит так:
decryption-routine-inmycode.png

Здесь тоже необходимо применить парочку изменений:
1.Заменяем тип _BYTE на unsigned char, _WORD на unsigned short, _DWORD на unsigned long, _QWORD на unsigned long long
2.Используем свою функцию чтения памяти (вероятно у вас свой 1337 гипервизор или драйвер) в тех местах, где идёт разыменнование чего либо с аргументом. Говоря по дебильному: везде где есть выражение *(тип*)(a1 + что-нибудь или вообще ничего) заменяем на своё чтение памяти
Результат:
decryption-routine-inmycode-fixed-2.png

Называем функцию в стиле decrypt_client_entities_wrapper (мне вообще без разницы как вы её назовёте).
Не обращайте внимание на красный il2cpp_gchandle_get_target - мы к нему ещё вернёмся.

По такому же принципу расшифровываем entity_list. Нам осталось понять как из %fd9c39723c3f962c0308a43e4106cb774701a27c (в прошлом ListDictionary) получить список сущностей и массив из них. ListDictionary это generic класс - то есть обобщённый класс с разными типами в <>. Поэтому у полей все оффсеты равны 0, а все методы имеют rva -1
listdictionary-latest.png

Под методами дампер собрал нам GenericInstMethod - реализации функций для конкретных типов. Например: функция с rva 0x5930B70 применяется для типов с <object, object>, <object, float>, <uint, object>, а функция с rva 0x7F4FA10 применяется только для типа <__Il2CppFullySharedGenericType, __Il2CppFullySharedGenericType>
Напомню, у нас с вами тип <%1c24d316ef52282e54d325685a623c64a0124e28, BaseNetworkable>:
realmclient-cliententities-generic-type.png

В классе ListDictionary мне хотелось бы посмотреть реализацию функции int %c11b6f5f8ca36f1b5661153e32660fc2f41ea536(). По её пустому количеству аргументов и возвращаемому типу мне кажется, что это функция count. Конкретного типа реализации для <%1c24d316ef52282e54d325685a623c64a0124e28, BaseNetworkable> нету, но есть <%1c24d316ef52282e54d325685a623c64a0124e28, object> (ведь BaseNetworkable обычный объект по лору C#)

Переходим по нужному RVA и наблюдаем следующее:
(со скриптом)
getcount-from-list-function.png


(без)
countfunction-from-list-no-script.png


Со скриптом мы прекрасно понимаем, что эта функция действительно является количеством элементов в списке. Т.к. fields это просто вложенная структура, то мы складываем оффсеты.
fields оффсет - 0x10
_size оффсет - 0x8 (0x10 + 0x8 = 0x18 итог)
_items (массив) оффсет - 0x0 (0x10 + 0x0 = 0x10 итог)
image.png

generic-list-fields-struct-from-il2cpp-header.png


Долгожданный il2cpp_gchandle_get_target
На этом долго останавливаться не буду, эта функция не менялась со времён эры динозавров за исключием одного оффсета:
gchandle-get-target-inmycode.png

Данный оффсет берём с иды:
gchandle-get-target-from-ida.png


На этом всё. Жду мнения
 
Последнее редактирование:
Оффтоп
2026 пиздато начинается. мне нравится. Гайд будет мелкий, я заебался всё это писать. Вторая часть хз когда, возможно через неделю.

Предисловие
Всем привет. Недавно мне улыбнулась удача написать чит на игру Rust. Игра собрана с помощью il2cpp и имеет рандомизацию имён в метадате il2cpp и шифрование некоторых вещей.

Как было раньше
Ранние версии игры (девблоги) не имели шифрования имён и читы там писать намного легче. Я сомневаюсь, что с того времени много что поменялось, поэтому давайте посмотрим как всё было устроено раньше.
Существует класс BaseNetworkable со статичным полем clientEntities:
Посмотреть вложение 327126
Внутри класса BaseNetworkable.EntityRealm содержится список из сущностей:
Посмотреть вложение 327127
И из него уже можно вытащить все networkable объекты (сущности)

Информация из дампа il2cpp
Для начала я сдамплю il2cpp метадату используя инструмент
Пожалуйста, авторизуйтесь для просмотра ссылки.
. Как оказалось игра абсолютно не имеет никакого шифрования метадаты, как делают это другие игры по типу standoff 2 или genshin impact.
В последней версии игры нету никакого статичного поля, которое содержало бы clientEntities
Посмотреть вложение 327128
Однако BaseNetworkable.EntityRealm всё ещё существует в дампе:
Посмотреть вложение 327129
И статически он находится в новом классе
Посмотреть вложение 327130
С ходу бросаются пару мыслей:
- Почему некоторые типы, которые мы видели раньше (BaseNetworkable.EntityRealm, clientEntities) обёрнуты в какие-то классы?
- Какое конкретно поле с BaseNetworkable.EntityRealm выбрать из класса BaseNetworkable.%ca5d42befe66446a3fbc6568b3756c3773e80757?

Поиск и дешифрование BaseNetworkable экземпляра
Для того, чтобы найти чтение поля из BaseNetworkable.%ca5d42befe66446a3fbc6568b3756c3773e80757 (тот самый, что имеет несколько BaseNetworkable.EntityRealm) нужно посмотреть референсы к его TypeInfo

Для тех, кто не в курсе
В il2cpp для обращения к статическому полю необходимо найти указатель на TypeInfo класса, прочитать static_fields и от него обратиться к нужному статическому полю. Все необходимые структуры дампер создаёт в файле il2cpp.h:
Посмотреть вложение 327131

Продолжаем. Адрес с typeinfo лежит в файле script.json:
Посмотреть вложение 327132
Не забываем перевести число в шестнадцатеричную систему счисления.

Снова для тех, кто не в курсе
Дампер имеет встроенные скрипты, для определения названий функций и типов для IDA (и не только). В замечательной папке с дампом лежит файл ida_with_struct_py3.py:
Посмотреть вложение 327133
Загружаем его из контекстного меню:
Посмотреть вложение 327134
Ожидаем примерно минут 10, пока не увидим следующее окно:
Посмотреть вложение 327135
Просто нажимаем "Ок" и получаем красивое отображение в IDA.


Подготавливаем иду к работе
Для начала меняем image base на 0, чтобы переходить сразу по RVA. Сделать это можно в контекстком меню иды -> Edit -> Segments -> Rebase program -> вводим 0 -> Ок. Затем открываем вкладку экспортов и вводим il2cpp_gchandle_get_target. После перехода вас встретит джамп на некий sub:
Посмотреть вложение 327136
Переименновываем саб в j_il2cpp_gchandle_get_target


Теперь я буду иногда показывать по несколько скриншотов: со скриптом и без.
Переходим на адрес с typeinfo и видим следующее:
Посмотреть вложение 327137
Берём любой попавшийся xref и декомпилируем:
В 100% случаев вы увидите похожую картину:
(со скриптом)
Посмотреть вложение 327138
(без)
Посмотреть вложение 327139
Выделил 0xB8, я показывал его ранее - это оффсет на static_fields

Открываем эту функцию
(со скриптом)
Посмотреть вложение 327140

(без)
decrypt-function-no-script.png


Нас интересует данный промежуток - это расшифровка:
decryption-routine.png

Ида может делать ошибки в определии типов, поэтому она добавляет свои уебанские макросы: HIDWORD, LODWORD и тд.
Поэтому выполняем следующие действия при попадании в функцию с дешифровкой:
1. Убираем типы с указателем в аргументах. В моём случае второй аргумент это указатель на __int64. Кликаем на него и жмём клавишу 'Y', чтобы убрать указатель (для тупых это звёздочка).
2. Если увидите HIDWORD(переменная), то жмём клавишу 'Y' на переменной и меняем её тип на unsigned int.
В итоге у вас должно получиться:
decryption-routine-fixed-result.png

В свой код переносим локальные переменные (голубой цвет) и необходимую область (красный цвет). В результат функции помещаем результат со скрина (фиолетовый цвет).
Мой код на данном этапе выглядит так:
decryption-routine-inmycode.png

Здесь тоже необходимо применить парочку изменений:
1.Заменяем тип _BYTE на unsigned char, _WORD на unsigned short, _DWORD на unsigned long, _QWORD на unsigned long long
2.Используем свою функцию чтения памяти (вероятно у вас свой 1337 гипервизор или драйвер) в тех местах, где идёт разыменнование чего либо с аргументом. Говоря по дебильному: везде где есть выражение *(тип*)(a1 + что-нибудь или вообще ничего) заменяем на своё чтение памяти
Результат:
decryption-routine-inmycode-fixed-2.png

Называем функцию в стиле decrypt_client_entities_wrapper (мне вообще без разницы как вы её назовёте).
Не обращайте внимание на красный il2cpp_gchandle_get_target - мы к нему ещё вернёмся.

По такому же принципу расшифровываем entity_list. Нам осталось понять как из %fd9c39723c3f962c0308a43e4106cb774701a27c (в прошлом ListDictionary) получить список сущностей и массив из них. ListDictionary это generic класс - то есть обобщённый класс с разными типами в <>. Поэтому у полей все оффсеты равны 0, а все методы имеют rva -1
listdictionary-latest.png

Под методами дампер собрал нам GenericInstMethod - реализации функций для конкретных типов. Например: функция с rva 0x5930B70 применяется для типов с <object, object>, <object, float>, <uint, object>, а функция с rva 0x7F4FA10 применяется только для типа <__Il2CppFullySharedGenericType, __Il2CppFullySharedGenericType>
Напомню, у нас с вами тип <%1c24d316ef52282e54d325685a623c64a0124e28, BaseNetworkable>:
realmclient-cliententities-generic-type.png

В классе ListDictionary мне хотелось бы посмотреть реализацию функции int %c11b6f5f8ca36f1b5661153e32660fc2f41ea536(). По её пустому количеству аргументов и возвращаемому типу мне кажется, что это функция count. Конкретного типа реализации для <%1c24d316ef52282e54d325685a623c64a0124e28, BaseNetworkable> нету, но есть <%1c24d316ef52282e54d325685a623c64a0124e28, object> (ведь BaseNetworkable обычный объект по лору C#)

Переходим по нужному RVA и наблюдаем следующее:
(со скриптом)
getcount-from-list-function.png


(без)
countfunction-from-list-no-script.png


Со скриптом мы прекрасно понимаем, что эта функция действительно является количеством элементов в списке. Т.к. fields это просто вложенная структура, то мы складываем оффсеты.
fields оффсет - 0x10
_size оффсет - 0x8 (0x10 + 0x8 = 0x18 итог)
_items (массив) оффсет - 0x0 (0x10 + 0x0 = 0x10 итог)
image.png

generic-list-fields-struct-from-il2cpp-header.png


Долгожданный il2cpp_gchandle_get_target
На этом долго останавливаться не буду, эта функция не менялась со времён эры динозавров за исключием одного оффсета:
gchandle-get-target-inmycode.png

Данный оффсет берём с иды:
gchandle-get-target-from-ida.png


На этом всё. Жду мнения
спасибо брат
 
Оффтоп
2026 пиздато начинается. мне нравится. Гайд будет мелкий, я заебался всё это писать. Вторая часть хз когда, возможно через неделю.

Предисловие
Всем привет. Недавно мне улыбнулась удача написать чит на игру Rust. Игра собрана с помощью il2cpp и имеет рандомизацию имён в метадате il2cpp и шифрование некоторых вещей.

Как было раньше
Ранние версии игры (девблоги) не имели шифрования имён и читы там писать намного легче. Я сомневаюсь, что с того времени много что поменялось, поэтому давайте посмотрим как всё было устроено раньше.
Существует класс BaseNetworkable со статичным полем clientEntities:
Посмотреть вложение 327126
Внутри класса BaseNetworkable.EntityRealm содержится список из сущностей:
Посмотреть вложение 327127
И из него уже можно вытащить все networkable объекты (сущности)

Информация из дампа il2cpp
Для начала я сдамплю il2cpp метадату используя инструмент
Пожалуйста, авторизуйтесь для просмотра ссылки.
. Как оказалось игра абсолютно не имеет никакого шифрования метадаты, как делают это другие игры по типу standoff 2 или genshin impact.
В последней версии игры нету никакого статичного поля, которое содержало бы clientEntities
Посмотреть вложение 327128
Однако BaseNetworkable.EntityRealm всё ещё существует в дампе:
Посмотреть вложение 327129
И статически он находится в новом классе
Посмотреть вложение 327130
С ходу бросаются пару мыслей:
- Почему некоторые типы, которые мы видели раньше (BaseNetworkable.EntityRealm, clientEntities) обёрнуты в какие-то классы?
- Какое конкретно поле с BaseNetworkable.EntityRealm выбрать из класса BaseNetworkable.%ca5d42befe66446a3fbc6568b3756c3773e80757?

Поиск и дешифрование BaseNetworkable экземпляра
Для того, чтобы найти чтение поля из BaseNetworkable.%ca5d42befe66446a3fbc6568b3756c3773e80757 (тот самый, что имеет несколько BaseNetworkable.EntityRealm) нужно посмотреть референсы к его TypeInfo

Для тех, кто не в курсе
В il2cpp для обращения к статическому полю необходимо найти указатель на TypeInfo класса, прочитать static_fields и от него обратиться к нужному статическому полю. Все необходимые структуры дампер создаёт в файле il2cpp.h:
Посмотреть вложение 327131

Продолжаем. Адрес с typeinfo лежит в файле script.json:
Посмотреть вложение 327132
Не забываем перевести число в шестнадцатеричную систему счисления.

Снова для тех, кто не в курсе
Дампер имеет встроенные скрипты, для определения названий функций и типов для IDA (и не только). В замечательной папке с дампом лежит файл ida_with_struct_py3.py:
Посмотреть вложение 327133
Загружаем его из контекстного меню:
Посмотреть вложение 327134
Ожидаем примерно минут 10, пока не увидим следующее окно:
Посмотреть вложение 327135
Просто нажимаем "Ок" и получаем красивое отображение в IDA.


Подготавливаем иду к работе
Для начала меняем image base на 0, чтобы переходить сразу по RVA. Сделать это можно в контекстком меню иды -> Edit -> Segments -> Rebase program -> вводим 0 -> Ок. Затем открываем вкладку экспортов и вводим il2cpp_gchandle_get_target. После перехода вас встретит джамп на некий sub:
Посмотреть вложение 327136
Переименновываем саб в j_il2cpp_gchandle_get_target


Теперь я буду иногда показывать по несколько скриншотов: со скриптом и без.
Переходим на адрес с typeinfo и видим следующее:
Посмотреть вложение 327137
Берём любой попавшийся xref и декомпилируем:
В 100% случаев вы увидите похожую картину:
(со скриптом)
Посмотреть вложение 327138
(без)
Посмотреть вложение 327139
Выделил 0xB8, я показывал его ранее - это оффсет на static_fields

Открываем эту функцию
(со скриптом)
Посмотреть вложение 327140

(без)
decrypt-function-no-script.png


Сразу понимаем, что поле, которое нам надо использовать лежит по оффсету 0x38

Нас интересует данный промежуток - это расшифровка:
decryption-routine.png

Ида может делать ошибки в определии типов, поэтому она добавляет свои уебанские макросы: HIDWORD, LODWORD и тд.
Поэтому выполняем следующие действия при попадании в функцию с дешифровкой:
1. Убираем типы с указателем в аргументах. В моём случае второй аргумент это указатель на __int64. Кликаем на него и жмём клавишу 'Y', чтобы убрать указатель (для тупых это звёздочка).
2. Если увидите HIDWORD(переменная), то жмём клавишу 'Y' на переменной и меняем её тип на unsigned int.
В итоге у вас должно получиться:
decryption-routine-fixed-result.png

В свой код переносим локальные переменные (голубой цвет) и необходимую область (красный цвет). В результат функции помещаем результат со скрина (фиолетовый цвет).
Мой код на данном этапе выглядит так:
decryption-routine-inmycode.png

Здесь тоже необходимо применить парочку изменений:
1.Заменяем тип _BYTE на unsigned char, _WORD на unsigned short, _DWORD на unsigned long, _QWORD на unsigned long long
2.Используем свою функцию чтения памяти (вероятно у вас свой 1337 гипервизор или драйвер) в тех местах, где идёт разыменнование чего либо с аргументом. Говоря по дебильному: везде где есть выражение *(тип*)(a1 + что-нибудь или вообще ничего) заменяем на своё чтение памяти
Результат:
decryption-routine-inmycode-fixed-2.png

Называем функцию в стиле decrypt_client_entities_wrapper (мне вообще без разницы как вы её назовёте).
Не обращайте внимание на красный il2cpp_gchandle_get_target - мы к нему ещё вернёмся.

По такому же принципу расшифровываем entity_list. Нам осталось понять как из %fd9c39723c3f962c0308a43e4106cb774701a27c (в прошлом ListDictionary) получить список сущностей и массив из них. ListDictionary это generic класс - то есть обобщённый класс с разными типами в <>. Поэтому у полей все оффсеты равны 0, а все методы имеют rva -1
listdictionary-latest.png

Под методами дампер собрал нам GenericInstMethod - реализации функций для конкретных типов. Например: функция с rva 0x5930B70 применяется для типов с <object, object>, <object, float>, <uint, object>, а функция с rva 0x7F4FA10 применяется только для типа <__Il2CppFullySharedGenericType, __Il2CppFullySharedGenericType>
Напомню, у нас с вами тип <%1c24d316ef52282e54d325685a623c64a0124e28, BaseNetworkable>:
realmclient-cliententities-generic-type.png

В классе ListDictionary мне хотелось бы посмотреть реализацию функции int %c11b6f5f8ca36f1b5661153e32660fc2f41ea536(). По её пустому количеству аргументов и возвращаемому типу мне кажется, что это функция count. Конкретного типа реализации для <%1c24d316ef52282e54d325685a623c64a0124e28, BaseNetworkable> нету, но есть <%1c24d316ef52282e54d325685a623c64a0124e28, object> (ведь BaseNetworkable обычный объект по лору C#)

Переходим по нужному RVA и наблюдаем следующее:
(со скриптом)
getcount-from-list-function.png


(без)
countfunction-from-list-no-script.png


Со скриптом мы прекрасно понимаем, что эта функция действительно является количеством элементов в списке. Т.к. fields это просто вложенная структура, то мы складываем оффсеты.
fields оффсет - 0x10
_size оффсет - 0x8 (0x10 + 0x8 = 0x18 итог)
_items (массив) оффсет - 0x0 (0x10 + 0x0 = 0x10 итог)
image.png

generic-list-fields-struct-from-il2cpp-header.png


Долгожданный il2cpp_gchandle_get_target
На этом долго останавливаться не буду, эта функция не менялась со времён эры динозавров за исключием одного оффсета:
gchandle-get-target-inmycode.png

Данный оффсет берём с иды:
gchandle-get-target-from-ida.png


На этом всё. Жду мнения
Заебись
 
Оффтоп
2026 пиздато начинается. мне нравится. Гайд будет мелкий, я заебался всё это писать. Вторая часть хз когда, возможно через неделю.

Предисловие
Всем привет. Недавно мне улыбнулась удача написать чит на игру Rust. Игра собрана с помощью il2cpp и имеет рандомизацию имён в метадате il2cpp и шифрование некоторых вещей.

Как было раньше
Ранние версии игры (девблоги) не имели шифрования имён и читы там писать намного легче. Я сомневаюсь, что с того времени много что поменялось, поэтому давайте посмотрим как всё было устроено раньше.
Существует класс BaseNetworkable со статичным полем clientEntities:
Посмотреть вложение 327126
Внутри класса BaseNetworkable.EntityRealm содержится список из сущностей:
Посмотреть вложение 327127
И из него уже можно вытащить все networkable объекты (сущности)

Информация из дампа il2cpp
Для начала я сдамплю il2cpp метадату используя инструмент
Пожалуйста, авторизуйтесь для просмотра ссылки.
. Как оказалось игра абсолютно не имеет никакого шифрования метадаты, как делают это другие игры по типу standoff 2 или genshin impact.
В последней версии игры нету никакого статичного поля, которое содержало бы clientEntities
Посмотреть вложение 327128
Однако BaseNetworkable.EntityRealm всё ещё существует в дампе:
Посмотреть вложение 327129
И статически он находится в новом классе
Посмотреть вложение 327130
С ходу бросаются пару мыслей:
- Почему некоторые типы, которые мы видели раньше (BaseNetworkable.EntityRealm, clientEntities) обёрнуты в какие-то классы?
- Какое конкретно поле с BaseNetworkable.EntityRealm выбрать из класса BaseNetworkable.%ca5d42befe66446a3fbc6568b3756c3773e80757?

Поиск и дешифрование BaseNetworkable экземпляра
Для того, чтобы найти чтение поля из BaseNetworkable.%ca5d42befe66446a3fbc6568b3756c3773e80757 (тот самый, что имеет несколько BaseNetworkable.EntityRealm) нужно посмотреть референсы к его TypeInfo

Для тех, кто не в курсе
В il2cpp для обращения к статическому полю необходимо найти указатель на TypeInfo класса, прочитать static_fields и от него обратиться к нужному статическому полю. Все необходимые структуры дампер создаёт в файле il2cpp.h:
Посмотреть вложение 327131

Продолжаем. Адрес с typeinfo лежит в файле script.json:
Посмотреть вложение 327132
Не забываем перевести число в шестнадцатеричную систему счисления.

Снова для тех, кто не в курсе
Дампер имеет встроенные скрипты, для определения названий функций и типов для IDA (и не только). В замечательной папке с дампом лежит файл ida_with_struct_py3.py:
Посмотреть вложение 327133
Загружаем его из контекстного меню:
Посмотреть вложение 327134
Ожидаем примерно минут 10, пока не увидим следующее окно:
Посмотреть вложение 327135
Просто нажимаем "Ок" и получаем красивое отображение в IDA.


Подготавливаем иду к работе
Для начала меняем image base на 0, чтобы переходить сразу по RVA. Сделать это можно в контекстком меню иды -> Edit -> Segments -> Rebase program -> вводим 0 -> Ок. Затем открываем вкладку экспортов и вводим il2cpp_gchandle_get_target. После перехода вас встретит джамп на некий sub:
Посмотреть вложение 327136
Переименновываем саб в j_il2cpp_gchandle_get_target


Теперь я буду иногда показывать по несколько скриншотов: со скриптом и без.
Переходим на адрес с typeinfo и видим следующее:
Посмотреть вложение 327137
Берём любой попавшийся xref и декомпилируем:
В 100% случаев вы увидите похожую картину:
(со скриптом)
Посмотреть вложение 327138
(без)
Посмотреть вложение 327139
Выделил 0xB8, я показывал его ранее - это оффсет на static_fields

Открываем эту функцию
(со скриптом)
Посмотреть вложение 327140

(без)
decrypt-function-no-script.png


Сразу понимаем, что поле, которое нам надо использовать лежит по оффсету 0x38

Нас интересует данный промежуток - это расшифровка:
decryption-routine.png

Ида может делать ошибки в определии типов, поэтому она добавляет свои уебанские макросы: HIDWORD, LODWORD и тд.
Поэтому выполняем следующие действия при попадании в функцию с дешифровкой:
1. Убираем типы с указателем в аргументах. В моём случае второй аргумент это указатель на __int64. Кликаем на него и жмём клавишу 'Y', чтобы убрать указатель (для тупых это звёздочка).
2. Если увидите HIDWORD(переменная), то жмём клавишу 'Y' на переменной и меняем её тип на unsigned int.
В итоге у вас должно получиться:
decryption-routine-fixed-result.png

В свой код переносим локальные переменные (голубой цвет) и необходимую область (красный цвет). В результат функции помещаем результат со скрина (фиолетовый цвет).
Мой код на данном этапе выглядит так:
decryption-routine-inmycode.png

Здесь тоже необходимо применить парочку изменений:
1.Заменяем тип _BYTE на unsigned char, _WORD на unsigned short, _DWORD на unsigned long, _QWORD на unsigned long long
2.Используем свою функцию чтения памяти (вероятно у вас свой 1337 гипервизор или драйвер) в тех местах, где идёт разыменнование чего либо с аргументом. Говоря по дебильному: везде где есть выражение *(тип*)(a1 + что-нибудь или вообще ничего) заменяем на своё чтение памяти
Результат:
decryption-routine-inmycode-fixed-2.png

Называем функцию в стиле decrypt_client_entities_wrapper (мне вообще без разницы как вы её назовёте).
Не обращайте внимание на красный il2cpp_gchandle_get_target - мы к нему ещё вернёмся.

По такому же принципу расшифровываем entity_list. Нам осталось понять как из %fd9c39723c3f962c0308a43e4106cb774701a27c (в прошлом ListDictionary) получить список сущностей и массив из них. ListDictionary это generic класс - то есть обобщённый класс с разными типами в <>. Поэтому у полей все оффсеты равны 0, а все методы имеют rva -1
listdictionary-latest.png

Под методами дампер собрал нам GenericInstMethod - реализации функций для конкретных типов. Например: функция с rva 0x5930B70 применяется для типов с <object, object>, <object, float>, <uint, object>, а функция с rva 0x7F4FA10 применяется только для типа <__Il2CppFullySharedGenericType, __Il2CppFullySharedGenericType>
Напомню, у нас с вами тип <%1c24d316ef52282e54d325685a623c64a0124e28, BaseNetworkable>:
realmclient-cliententities-generic-type.png

В классе ListDictionary мне хотелось бы посмотреть реализацию функции int %c11b6f5f8ca36f1b5661153e32660fc2f41ea536(). По её пустому количеству аргументов и возвращаемому типу мне кажется, что это функция count. Конкретного типа реализации для <%1c24d316ef52282e54d325685a623c64a0124e28, BaseNetworkable> нету, но есть <%1c24d316ef52282e54d325685a623c64a0124e28, object> (ведь BaseNetworkable обычный объект по лору C#)

Переходим по нужному RVA и наблюдаем следующее:
(со скриптом)
getcount-from-list-function.png


(без)
countfunction-from-list-no-script.png


Со скриптом мы прекрасно понимаем, что эта функция действительно является количеством элементов в списке. Т.к. fields это просто вложенная структура, то мы складываем оффсеты.
fields оффсет - 0x10
_size оффсет - 0x8 (0x10 + 0x8 = 0x18 итог)
_items (массив) оффсет - 0x0 (0x10 + 0x0 = 0x10 итог)
image.png

generic-list-fields-struct-from-il2cpp-header.png


Долгожданный il2cpp_gchandle_get_target
На этом долго останавливаться не буду, эта функция не менялась со времён эры динозавров за исключием одного оффсета:
gchandle-get-target-inmycode.png

Данный оффсет берём с иды:
gchandle-get-target-from-ida.png


На этом всё. Жду мнения
👍
 
Слушай, для «мелкого гайда» это прям жирно.

Обожаю такой подход: когда вместо нытья про «рандомные имена в метадате» человек просто открывает IDA, находит оффсет static_fields и реверсит дешифровку.

В 2026-м Rust — это всё еще та еще помойка в плане обфускации, и то, что они начали прятать clientEntities в классы с именами типа %ca5d42..., — классика Facepunch. Пытаются усложнить жизнь пастерам, но, как ты и показал, против Il2cppDumper и прямых рук это не работает.

Пара мыслей по твоему разбору:
  • Про TypeInfo и оффсет 0xB8: это база, но новички часто на этом сыплются. Красава, что сделал акцент на разнице «со скриптом» и «без». Скрипты для Иды — это спасение, иначе в этих sub_XXXXXX можно глаза сломать.
  • Дешифровка: твой перенос кода в C++ выглядит чисто. Главное — напомнить тем, кто будет это копировать, что read&lt;T&gt; должен быть надежным, иначе на первом же разыменовании мусора словят BSOD.
  • Generic-типы: то, что ты полез в ListDictionary через object — самый правильный путь. Многие ищут точное совпадение типов и расстраиваются, когда дампер выдает -1.
 
  • Дешифровка: твой перенос кода в C++ выглядит чисто. Главное — напомнить тем, кто будет это копировать, что read&lt;T&gt; должен быть надежным, иначе на первом же разыменовании мусора словят BSOD.
Поделись дозой, пожалуйста...
Оффтоп
2026 пиздато начинается. мне нравится. Гайд будет мелкий, я заебался всё это писать. Вторая часть хз когда, возможно через неделю.

Предисловие
Всем привет. Недавно мне улыбнулась удача написать чит на игру Rust. Игра собрана с помощью il2cpp и имеет рандомизацию имён в метадате il2cpp и шифрование некоторых вещей.

Как было раньше
Ранние версии игры (девблоги) не имели шифрования имён и читы там писать намного легче. Я сомневаюсь, что с того времени много что поменялось, поэтому давайте посмотрим как всё было устроено раньше.
Существует класс BaseNetworkable со статичным полем clientEntities:
Посмотреть вложение 327126
Внутри класса BaseNetworkable.EntityRealm содержится список из сущностей:
Посмотреть вложение 327127
И из него уже можно вытащить все networkable объекты (сущности)

Информация из дампа il2cpp
Для начала я сдамплю il2cpp метадату используя инструмент
Пожалуйста, авторизуйтесь для просмотра ссылки.
. Как оказалось игра абсолютно не имеет никакого шифрования метадаты, как делают это другие игры по типу standoff 2 или genshin impact.
В последней версии игры нету никакого статичного поля, которое содержало бы clientEntities
Посмотреть вложение 327128
Однако BaseNetworkable.EntityRealm всё ещё существует в дампе:
Посмотреть вложение 327129
И статически он находится в новом классе
Посмотреть вложение 327130
С ходу бросаются пару мыслей:
- Почему некоторые типы, которые мы видели раньше (BaseNetworkable.EntityRealm, clientEntities) обёрнуты в какие-то классы?
- Какое конкретно поле с BaseNetworkable.EntityRealm выбрать из класса BaseNetworkable.%ca5d42befe66446a3fbc6568b3756c3773e80757?

Поиск и дешифрование BaseNetworkable экземпляра
Для того, чтобы найти чтение поля из BaseNetworkable.%ca5d42befe66446a3fbc6568b3756c3773e80757 (тот самый, что имеет несколько BaseNetworkable.EntityRealm) нужно посмотреть референсы к его TypeInfo

Для тех, кто не в курсе
В il2cpp для обращения к статическому полю необходимо найти указатель на TypeInfo класса, прочитать static_fields и от него обратиться к нужному статическому полю. Все необходимые структуры дампер создаёт в файле il2cpp.h:
Посмотреть вложение 327131

Продолжаем. Адрес с typeinfo лежит в файле script.json:
Посмотреть вложение 327132
Не забываем перевести число в шестнадцатеричную систему счисления.

Снова для тех, кто не в курсе
Дампер имеет встроенные скрипты, для определения названий функций и типов для IDA (и не только). В замечательной папке с дампом лежит файл ida_with_struct_py3.py:
Посмотреть вложение 327133
Загружаем его из контекстного меню:
Посмотреть вложение 327134
Ожидаем примерно минут 10, пока не увидим следующее окно:
Посмотреть вложение 327135
Просто нажимаем "Ок" и получаем красивое отображение в IDA.


Подготавливаем иду к работе
Для начала меняем image base на 0, чтобы переходить сразу по RVA. Сделать это можно в контекстком меню иды -> Edit -> Segments -> Rebase program -> вводим 0 -> Ок. Затем открываем вкладку экспортов и вводим il2cpp_gchandle_get_target. После перехода вас встретит джамп на некий sub:
Посмотреть вложение 327136
Переименновываем саб в j_il2cpp_gchandle_get_target


Теперь я буду иногда показывать по несколько скриншотов: со скриптом и без.
Переходим на адрес с typeinfo и видим следующее:
Посмотреть вложение 327137
Берём любой попавшийся xref и декомпилируем:
В 100% случаев вы увидите похожую картину:
(со скриптом)
Посмотреть вложение 327138
(без)
Посмотреть вложение 327139
Выделил 0xB8, я показывал его ранее - это оффсет на static_fields

Открываем эту функцию
(со скриптом)
Посмотреть вложение 327140

(без)
decrypt-function-no-script.png


Сразу понимаем, что поле, которое нам надо использовать лежит по оффсету 0x38

Нас интересует данный промежуток - это расшифровка:
decryption-routine.png

Ида может делать ошибки в определии типов, поэтому она добавляет свои уебанские макросы: HIDWORD, LODWORD и тд.
Поэтому выполняем следующие действия при попадании в функцию с дешифровкой:
1. Убираем типы с указателем в аргументах. В моём случае второй аргумент это указатель на __int64. Кликаем на него и жмём клавишу 'Y', чтобы убрать указатель (для тупых это звёздочка).
2. Если увидите HIDWORD(переменная), то жмём клавишу 'Y' на переменной и меняем её тип на unsigned int.
В итоге у вас должно получиться:
decryption-routine-fixed-result.png

В свой код переносим локальные переменные (голубой цвет) и необходимую область (красный цвет). В результат функции помещаем результат со скрина (фиолетовый цвет).
Мой код на данном этапе выглядит так:
decryption-routine-inmycode.png

Здесь тоже необходимо применить парочку изменений:
1.Заменяем тип _BYTE на unsigned char, _WORD на unsigned short, _DWORD на unsigned long, _QWORD на unsigned long long
2.Используем свою функцию чтения памяти (вероятно у вас свой 1337 гипервизор или драйвер) в тех местах, где идёт разыменнование чего либо с аргументом. Говоря по дебильному: везде где есть выражение *(тип*)(a1 + что-нибудь или вообще ничего) заменяем на своё чтение памяти
Результат:
decryption-routine-inmycode-fixed-2.png

Называем функцию в стиле decrypt_client_entities_wrapper (мне вообще без разницы как вы её назовёте).
Не обращайте внимание на красный il2cpp_gchandle_get_target - мы к нему ещё вернёмся.

По такому же принципу расшифровываем entity_list. Нам осталось понять как из %fd9c39723c3f962c0308a43e4106cb774701a27c (в прошлом ListDictionary) получить список сущностей и массив из них. ListDictionary это generic класс - то есть обобщённый класс с разными типами в <>. Поэтому у полей все оффсеты равны 0, а все методы имеют rva -1
listdictionary-latest.png

Под методами дампер собрал нам GenericInstMethod - реализации функций для конкретных типов. Например: функция с rva 0x5930B70 применяется для типов с <object, object>, <object, float>, <uint, object>, а функция с rva 0x7F4FA10 применяется только для типа <__Il2CppFullySharedGenericType, __Il2CppFullySharedGenericType>
Напомню, у нас с вами тип <%1c24d316ef52282e54d325685a623c64a0124e28, BaseNetworkable>:
realmclient-cliententities-generic-type.png

В классе ListDictionary мне хотелось бы посмотреть реализацию функции int %c11b6f5f8ca36f1b5661153e32660fc2f41ea536(). По её пустому количеству аргументов и возвращаемому типу мне кажется, что это функция count. Конкретного типа реализации для <%1c24d316ef52282e54d325685a623c64a0124e28, BaseNetworkable> нету, но есть <%1c24d316ef52282e54d325685a623c64a0124e28, object> (ведь BaseNetworkable обычный объект по лору C#)

Переходим по нужному RVA и наблюдаем следующее:
(со скриптом)
getcount-from-list-function.png


(без)
countfunction-from-list-no-script.png


Со скриптом мы прекрасно понимаем, что эта функция действительно является количеством элементов в списке. Т.к. fields это просто вложенная структура, то мы складываем оффсеты.
fields оффсет - 0x10
_size оффсет - 0x8 (0x10 + 0x8 = 0x18 итог)
_items (массив) оффсет - 0x0 (0x10 + 0x0 = 0x10 итог)
image.png

generic-list-fields-struct-from-il2cpp-header.png


Долгожданный il2cpp_gchandle_get_target
На этом долго останавливаться не буду, эта функция не менялась со времён эры динозавров за исключием одного оффсета:
gchandle-get-target-inmycode.png

Данный оффсет берём с иды:
gchandle-get-target-from-ida.png


На этом всё. Жду мнения
Увидел слион и влюбился в автора с первого взгляда. Давай в губы :pepeez321:
 
Оффтоп
2026 пиздато начинается. мне нравится. Гайд будет мелкий, я заебался всё это писать. Вторая часть хз когда, возможно через неделю.

Предисловие
Всем привет. Недавно мне улыбнулась удача написать чит на игру Rust. Игра собрана с помощью il2cpp и имеет рандомизацию имён в метадате il2cpp и шифрование некоторых вещей.

Как было раньше
Ранние версии игры (девблоги) не имели шифрования имён и читы там писать намного легче. Я сомневаюсь, что с того времени много что поменялось, поэтому давайте посмотрим как всё было устроено раньше.
Существует класс BaseNetworkable со статичным полем clientEntities:
Посмотреть вложение 327126
Внутри класса BaseNetworkable.EntityRealm содержится список из сущностей:
Посмотреть вложение 327127
И из него уже можно вытащить все networkable объекты (сущности)

Информация из дампа il2cpp
Для начала я сдамплю il2cpp метадату используя инструмент
Пожалуйста, авторизуйтесь для просмотра ссылки.
. Как оказалось игра абсолютно не имеет никакого шифрования метадаты, как делают это другие игры по типу standoff 2 или genshin impact.
В последней версии игры нету никакого статичного поля, которое содержало бы clientEntities
Посмотреть вложение 327128
Однако BaseNetworkable.EntityRealm всё ещё существует в дампе:
Посмотреть вложение 327129
И статически он находится в новом классе
Посмотреть вложение 327130
С ходу бросаются пару мыслей:
- Почему некоторые типы, которые мы видели раньше (BaseNetworkable.EntityRealm, clientEntities) обёрнуты в какие-то классы?
- Какое конкретно поле с BaseNetworkable.EntityRealm выбрать из класса BaseNetworkable.%ca5d42befe66446a3fbc6568b3756c3773e80757?

Поиск и дешифрование BaseNetworkable экземпляра
Для того, чтобы найти чтение поля из BaseNetworkable.%ca5d42befe66446a3fbc6568b3756c3773e80757 (тот самый, что имеет несколько BaseNetworkable.EntityRealm) нужно посмотреть референсы к его TypeInfo

Для тех, кто не в курсе
В il2cpp для обращения к статическому полю необходимо найти указатель на TypeInfo класса, прочитать static_fields и от него обратиться к нужному статическому полю. Все необходимые структуры дампер создаёт в файле il2cpp.h:
Посмотреть вложение 327131

Продолжаем. Адрес с typeinfo лежит в файле script.json:
Посмотреть вложение 327132
Не забываем перевести число в шестнадцатеричную систему счисления.

Снова для тех, кто не в курсе
Дампер имеет встроенные скрипты, для определения названий функций и типов для IDA (и не только). В замечательной папке с дампом лежит файл ida_with_struct_py3.py:
Посмотреть вложение 327133
Загружаем его из контекстного меню:
Посмотреть вложение 327134
Ожидаем примерно минут 10, пока не увидим следующее окно:
Посмотреть вложение 327135
Просто нажимаем "Ок" и получаем красивое отображение в IDA.


Подготавливаем иду к работе
Для начала меняем image base на 0, чтобы переходить сразу по RVA. Сделать это можно в контекстком меню иды -> Edit -> Segments -> Rebase program -> вводим 0 -> Ок. Затем открываем вкладку экспортов и вводим il2cpp_gchandle_get_target. После перехода вас встретит джамп на некий sub:
Посмотреть вложение 327136
Переименновываем саб в j_il2cpp_gchandle_get_target


Теперь я буду иногда показывать по несколько скриншотов: со скриптом и без.
Переходим на адрес с typeinfo и видим следующее:
Посмотреть вложение 327137
Берём любой попавшийся xref и декомпилируем:
В 100% случаев вы увидите похожую картину:
(со скриптом)
Посмотреть вложение 327138
(без)
Посмотреть вложение 327139
Выделил 0xB8, я показывал его ранее - это оффсет на static_fields

Открываем эту функцию
(со скриптом)
Посмотреть вложение 327140

(без)
decrypt-function-no-script.png


Сразу понимаем, что поле, которое нам надо использовать лежит по оффсету 0x38

Нас интересует данный промежуток - это расшифровка:
decryption-routine.png

Ида может делать ошибки в определии типов, поэтому она добавляет свои уебанские макросы: HIDWORD, LODWORD и тд.
Поэтому выполняем следующие действия при попадании в функцию с дешифровкой:
1. Убираем типы с указателем в аргументах. В моём случае второй аргумент это указатель на __int64. Кликаем на него и жмём клавишу 'Y', чтобы убрать указатель (для тупых это звёздочка).
2. Если увидите HIDWORD(переменная), то жмём клавишу 'Y' на переменной и меняем её тип на unsigned int.
В итоге у вас должно получиться:
decryption-routine-fixed-result.png

В свой код переносим локальные переменные (голубой цвет) и необходимую область (красный цвет). В результат функции помещаем результат со скрина (фиолетовый цвет).
Мой код на данном этапе выглядит так:
decryption-routine-inmycode.png

Здесь тоже необходимо применить парочку изменений:
1.Заменяем тип _BYTE на unsigned char, _WORD на unsigned short, _DWORD на unsigned long, _QWORD на unsigned long long
2.Используем свою функцию чтения памяти (вероятно у вас свой 1337 гипервизор или драйвер) в тех местах, где идёт разыменнование чего либо с аргументом. Говоря по дебильному: везде где есть выражение *(тип*)(a1 + что-нибудь или вообще ничего) заменяем на своё чтение памяти
Результат:
decryption-routine-inmycode-fixed-2.png

Называем функцию в стиле decrypt_client_entities_wrapper (мне вообще без разницы как вы её назовёте).
Не обращайте внимание на красный il2cpp_gchandle_get_target - мы к нему ещё вернёмся.

По такому же принципу расшифровываем entity_list. Нам осталось понять как из %fd9c39723c3f962c0308a43e4106cb774701a27c (в прошлом ListDictionary) получить список сущностей и массив из них. ListDictionary это generic класс - то есть обобщённый класс с разными типами в <>. Поэтому у полей все оффсеты равны 0, а все методы имеют rva -1
listdictionary-latest.png

Под методами дампер собрал нам GenericInstMethod - реализации функций для конкретных типов. Например: функция с rva 0x5930B70 применяется для типов с <object, object>, <object, float>, <uint, object>, а функция с rva 0x7F4FA10 применяется только для типа <__Il2CppFullySharedGenericType, __Il2CppFullySharedGenericType>
Напомню, у нас с вами тип <%1c24d316ef52282e54d325685a623c64a0124e28, BaseNetworkable>:
realmclient-cliententities-generic-type.png

В классе ListDictionary мне хотелось бы посмотреть реализацию функции int %c11b6f5f8ca36f1b5661153e32660fc2f41ea536(). По её пустому количеству аргументов и возвращаемому типу мне кажется, что это функция count. Конкретного типа реализации для <%1c24d316ef52282e54d325685a623c64a0124e28, BaseNetworkable> нету, но есть <%1c24d316ef52282e54d325685a623c64a0124e28, object> (ведь BaseNetworkable обычный объект по лору C#)

Переходим по нужному RVA и наблюдаем следующее:
(со скриптом)
getcount-from-list-function.png


(без)
countfunction-from-list-no-script.png


Со скриптом мы прекрасно понимаем, что эта функция действительно является количеством элементов в списке. Т.к. fields это просто вложенная структура, то мы складываем оффсеты.
fields оффсет - 0x10
_size оффсет - 0x8 (0x10 + 0x8 = 0x18 итог)
_items (массив) оффсет - 0x0 (0x10 + 0x0 = 0x10 итог)
image.png

generic-list-fields-struct-from-il2cpp-header.png


Долгожданный il2cpp_gchandle_get_target
На этом долго останавливаться не буду, эта функция не менялась со времён эры динозавров за исключием одного оффсета:
gchandle-get-target-inmycode.png

Данный оффсет берём с иды:
gchandle-get-target-from-ida.png


На этом всё. Жду мнения
норм брат, уважаемо
 
Оффтоп
2026 пиздато начинается. мне нравится. Гайд будет мелкий, я заебался всё это писать. Вторая часть хз когда, возможно через неделю.

Предисловие
Всем привет. Недавно мне улыбнулась удача написать чит на игру Rust. Игра собрана с помощью il2cpp и имеет рандомизацию имён в метадате il2cpp и шифрование некоторых вещей.

Как было раньше
Ранние версии игры (девблоги) не имели шифрования имён и читы там писать намного легче. Я сомневаюсь, что с того времени много что поменялось, поэтому давайте посмотрим как всё было устроено раньше.
Существует класс BaseNetworkable со статичным полем clientEntities:
Посмотреть вложение 327126
Внутри класса BaseNetworkable.EntityRealm содержится список из сущностей:
Посмотреть вложение 327127
И из него уже можно вытащить все networkable объекты (сущности)

Информация из дампа il2cpp
Для начала я сдамплю il2cpp метадату используя инструмент
Пожалуйста, авторизуйтесь для просмотра ссылки.
. Как оказалось игра абсолютно не имеет никакого шифрования метадаты, как делают это другие игры по типу standoff 2 или genshin impact.
В последней версии игры нету никакого статичного поля, которое содержало бы clientEntities
Посмотреть вложение 327128
Однако BaseNetworkable.EntityRealm всё ещё существует в дампе:
Посмотреть вложение 327129
И статически он находится в новом классе
Посмотреть вложение 327130
С ходу бросаются пару мыслей:
- Почему некоторые типы, которые мы видели раньше (BaseNetworkable.EntityRealm, clientEntities) обёрнуты в какие-то классы?
- Какое конкретно поле с BaseNetworkable.EntityRealm выбрать из класса BaseNetworkable.%ca5d42befe66446a3fbc6568b3756c3773e80757?

Поиск и дешифрование BaseNetworkable экземпляра
Для того, чтобы найти чтение поля из BaseNetworkable.%ca5d42befe66446a3fbc6568b3756c3773e80757 (тот самый, что имеет несколько BaseNetworkable.EntityRealm) нужно посмотреть референсы к его TypeInfo

Для тех, кто не в курсе
В il2cpp для обращения к статическому полю необходимо найти указатель на TypeInfo класса, прочитать static_fields и от него обратиться к нужному статическому полю. Все необходимые структуры дампер создаёт в файле il2cpp.h:
Посмотреть вложение 327131

Продолжаем. Адрес с typeinfo лежит в файле script.json:
Посмотреть вложение 327132
Не забываем перевести число в шестнадцатеричную систему счисления.

Снова для тех, кто не в курсе
Дампер имеет встроенные скрипты, для определения названий функций и типов для IDA (и не только). В замечательной папке с дампом лежит файл ida_with_struct_py3.py:
Посмотреть вложение 327133
Загружаем его из контекстного меню:
Посмотреть вложение 327134
Ожидаем примерно минут 10, пока не увидим следующее окно:
Посмотреть вложение 327135
Просто нажимаем "Ок" и получаем красивое отображение в IDA.


Подготавливаем иду к работе
Для начала меняем image base на 0, чтобы переходить сразу по RVA. Сделать это можно в контекстком меню иды -> Edit -> Segments -> Rebase program -> вводим 0 -> Ок. Затем открываем вкладку экспортов и вводим il2cpp_gchandle_get_target. После перехода вас встретит джамп на некий sub:
Посмотреть вложение 327136
Переименновываем саб в j_il2cpp_gchandle_get_target


Теперь я буду иногда показывать по несколько скриншотов: со скриптом и без.
Переходим на адрес с typeinfo и видим следующее:
Посмотреть вложение 327137
Берём любой попавшийся xref и декомпилируем:
В 100% случаев вы увидите похожую картину:
(со скриптом)
Посмотреть вложение 327138
(без)
Посмотреть вложение 327139
Выделил 0xB8, я показывал его ранее - это оффсет на static_fields

Открываем эту функцию
(со скриптом)
Посмотреть вложение 327140

(без)
decrypt-function-no-script.png


Сразу понимаем, что поле, которое нам надо использовать лежит по оффсету 0x38

Нас интересует данный промежуток - это расшифровка:
decryption-routine.png

Ида может делать ошибки в определии типов, поэтому она добавляет свои уебанские макросы: HIDWORD, LODWORD и тд.
Поэтому выполняем следующие действия при попадании в функцию с дешифровкой:
1. Убираем типы с указателем в аргументах. В моём случае второй аргумент это указатель на __int64. Кликаем на него и жмём клавишу 'Y', чтобы убрать указатель (для тупых это звёздочка).
2. Если увидите HIDWORD(переменная), то жмём клавишу 'Y' на переменной и меняем её тип на unsigned int.
В итоге у вас должно получиться:
decryption-routine-fixed-result.png

В свой код переносим локальные переменные (голубой цвет) и необходимую область (красный цвет). В результат функции помещаем результат со скрина (фиолетовый цвет).
Мой код на данном этапе выглядит так:
decryption-routine-inmycode.png

Здесь тоже необходимо применить парочку изменений:
1.Заменяем тип _BYTE на unsigned char, _WORD на unsigned short, _DWORD на unsigned long, _QWORD на unsigned long long
2.Используем свою функцию чтения памяти (вероятно у вас свой 1337 гипервизор или драйвер) в тех местах, где идёт разыменнование чего либо с аргументом. Говоря по дебильному: везде где есть выражение *(тип*)(a1 + что-нибудь или вообще ничего) заменяем на своё чтение памяти
Результат:
decryption-routine-inmycode-fixed-2.png

Называем функцию в стиле decrypt_client_entities_wrapper (мне вообще без разницы как вы её назовёте).
Не обращайте внимание на красный il2cpp_gchandle_get_target - мы к нему ещё вернёмся.

По такому же принципу расшифровываем entity_list. Нам осталось понять как из %fd9c39723c3f962c0308a43e4106cb774701a27c (в прошлом ListDictionary) получить список сущностей и массив из них. ListDictionary это generic класс - то есть обобщённый класс с разными типами в <>. Поэтому у полей все оффсеты равны 0, а все методы имеют rva -1
listdictionary-latest.png

Под методами дампер собрал нам GenericInstMethod - реализации функций для конкретных типов. Например: функция с rva 0x5930B70 применяется для типов с <object, object>, <object, float>, <uint, object>, а функция с rva 0x7F4FA10 применяется только для типа <__Il2CppFullySharedGenericType, __Il2CppFullySharedGenericType>
Напомню, у нас с вами тип <%1c24d316ef52282e54d325685a623c64a0124e28, BaseNetworkable>:
realmclient-cliententities-generic-type.png

В классе ListDictionary мне хотелось бы посмотреть реализацию функции int %c11b6f5f8ca36f1b5661153e32660fc2f41ea536(). По её пустому количеству аргументов и возвращаемому типу мне кажется, что это функция count. Конкретного типа реализации для <%1c24d316ef52282e54d325685a623c64a0124e28, BaseNetworkable> нету, но есть <%1c24d316ef52282e54d325685a623c64a0124e28, object> (ведь BaseNetworkable обычный объект по лору C#)

Переходим по нужному RVA и наблюдаем следующее:
(со скриптом)
getcount-from-list-function.png


(без)
countfunction-from-list-no-script.png


Со скриптом мы прекрасно понимаем, что эта функция действительно является количеством элементов в списке. Т.к. fields это просто вложенная структура, то мы складываем оффсеты.
fields оффсет - 0x10
_size оффсет - 0x8 (0x10 + 0x8 = 0x18 итог)
_items (массив) оффсет - 0x0 (0x10 + 0x0 = 0x10 итог)
image.png

generic-list-fields-struct-from-il2cpp-header.png


Долгожданный il2cpp_gchandle_get_target
На этом долго останавливаться не буду, эта функция не менялась со времён эры динозавров за исключием одного оффсета:
gchandle-get-target-inmycode.png

Данный оффсет берём с иды:
gchandle-get-target-from-ida.png


На этом всё. Жду мнения
а зачем 200 одинаковых тредов по поиску ебучих декриптов на ебучий бейснетворкейбл
 
а зачем 200 одинаковых тредов по поиску ебучих декриптов на ебучий бейснетворкейбл
покажи хоть один гайд объясняющий всё досконально на этом форуме.
а ещё напомню, что это первый гайд, скоро следующий сделаю. я не собираюсь делать статью в стиле: ну вот сюда идите кароче вот это копируйте и нормально, а откуда я всё это достал я не объясню
 
покажи хоть один гайд объясняющий всё досконально на этом форуме.
а ещё напомню, что это первый гайд, скоро следующий сделаю. я не собираюсь делать статью в стиле: ну вот сюда идите кароче вот это копируйте и нормально, а откуда я всё это достал я не объясню
ну может ты и прав про подробности, да в целом раздел раста мертв, постить сюда смысла нету ибо тут одна моча ебаная которая девблоги пастит и нихуя никакого отношения к реверсу и читам отношения не имеет
 
ну может ты и прав про подробности, да в целом раздел раста мертв, постить сюда смысла нету ибо тут одна моча ебаная которая девблоги пастит и нихуя никакого отношения к реверсу и читам отношения не имеет
точно также в майнкрафт разделе. однако это не остановило меня от создания нескольких тем подряд про основу: https://yougame.biz/threads/325113/

я собираюсь сделать тоже самое в расте.
 
точно также в майнкрафт разделе. однако это не остановило меня от создания нескольких тем подряд про основу: https://yougame.biz/threads/325113/

я собираюсь сделать тоже самое в расте.
и будет очень полезно и важно как для юзеров, так и для форума, если сделаешь
спасибо
а этого придурка даже не слушай, он всем под каждый пост хуйню пишет
 
Оффтоп
2026 пиздато начинается. мне нравится. Гайд будет мелкий, я заебался всё это писать. Вторая часть хз когда, возможно через неделю.

Предисловие
Всем привет. Недавно мне улыбнулась удача написать чит на игру Rust. Игра собрана с помощью il2cpp и имеет рандомизацию имён в метадате il2cpp и шифрование некоторых вещей.

Как было раньше
Ранние версии игры (девблоги) не имели шифрования имён и читы там писать намного легче. Я сомневаюсь, что с того времени много что поменялось, поэтому давайте посмотрим как всё было устроено раньше.
Существует класс BaseNetworkable со статичным полем clientEntities:
Посмотреть вложение 327126
Внутри класса BaseNetworkable.EntityRealm содержится список из сущностей:
Посмотреть вложение 327127
И из него уже можно вытащить все networkable объекты (сущности)

Информация из дампа il2cpp
Для начала я сдамплю il2cpp метадату используя инструмент
Пожалуйста, авторизуйтесь для просмотра ссылки.
. Как оказалось игра абсолютно не имеет никакого шифрования метадаты, как делают это другие игры по типу standoff 2 или genshin impact.
В последней версии игры нету никакого статичного поля, которое содержало бы clientEntities
Посмотреть вложение 327128
Однако BaseNetworkable.EntityRealm всё ещё существует в дампе:
Посмотреть вложение 327129
И статически он находится в новом классе
Посмотреть вложение 327130
С ходу бросаются пару мыслей:
- Почему некоторые типы, которые мы видели раньше (BaseNetworkable.EntityRealm, clientEntities) обёрнуты в какие-то классы?
- Какое конкретно поле с BaseNetworkable.EntityRealm выбрать из класса BaseNetworkable.%ca5d42befe66446a3fbc6568b3756c3773e80757?

Поиск и дешифрование BaseNetworkable экземпляра
Для того, чтобы найти чтение поля из BaseNetworkable.%ca5d42befe66446a3fbc6568b3756c3773e80757 (тот самый, что имеет несколько BaseNetworkable.EntityRealm) нужно посмотреть референсы к его TypeInfo

Для тех, кто не в курсе
В il2cpp для обращения к статическому полю необходимо найти указатель на TypeInfo класса, прочитать static_fields и от него обратиться к нужному статическому полю. Все необходимые структуры дампер создаёт в файле il2cpp.h:
Посмотреть вложение 327131

Продолжаем. Адрес с typeinfo лежит в файле script.json:
Посмотреть вложение 327132
Не забываем перевести число в шестнадцатеричную систему счисления.

Снова для тех, кто не в курсе
Дампер имеет встроенные скрипты, для определения названий функций и типов для IDA (и не только). В замечательной папке с дампом лежит файл ida_with_struct_py3.py:
Посмотреть вложение 327133
Загружаем его из контекстного меню:
Посмотреть вложение 327134
Ожидаем примерно минут 10, пока не увидим следующее окно:
Посмотреть вложение 327135
Просто нажимаем "Ок" и получаем красивое отображение в IDA.


Подготавливаем иду к работе
Для начала меняем image base на 0, чтобы переходить сразу по RVA. Сделать это можно в контекстком меню иды -> Edit -> Segments -> Rebase program -> вводим 0 -> Ок. Затем открываем вкладку экспортов и вводим il2cpp_gchandle_get_target. После перехода вас встретит джамп на некий sub:
Посмотреть вложение 327136
Переименновываем саб в j_il2cpp_gchandle_get_target


Теперь я буду иногда показывать по несколько скриншотов: со скриптом и без.
Переходим на адрес с typeinfo и видим следующее:
Посмотреть вложение 327137
Берём любой попавшийся xref и декомпилируем:
В 100% случаев вы увидите похожую картину:
(со скриптом)
Посмотреть вложение 327138
(без)
Посмотреть вложение 327139
Выделил 0xB8, я показывал его ранее - это оффсет на static_fields

Открываем эту функцию
(со скриптом)
Посмотреть вложение 327140

(без)
decrypt-function-no-script.png


Сразу понимаем, что поле, которое нам надо использовать лежит по оффсету 0x38

Нас интересует данный промежуток - это расшифровка:
decryption-routine.png

Ида может делать ошибки в определии типов, поэтому она добавляет свои уебанские макросы: HIDWORD, LODWORD и тд.
Поэтому выполняем следующие действия при попадании в функцию с дешифровкой:
1. Убираем типы с указателем в аргументах. В моём случае второй аргумент это указатель на __int64. Кликаем на него и жмём клавишу 'Y', чтобы убрать указатель (для тупых это звёздочка).
2. Если увидите HIDWORD(переменная), то жмём клавишу 'Y' на переменной и меняем её тип на unsigned int.
В итоге у вас должно получиться:
decryption-routine-fixed-result.png

В свой код переносим локальные переменные (голубой цвет) и необходимую область (красный цвет). В результат функции помещаем результат со скрина (фиолетовый цвет).
Мой код на данном этапе выглядит так:
decryption-routine-inmycode.png

Здесь тоже необходимо применить парочку изменений:
1.Заменяем тип _BYTE на unsigned char, _WORD на unsigned short, _DWORD на unsigned long, _QWORD на unsigned long long
2.Используем свою функцию чтения памяти (вероятно у вас свой 1337 гипервизор или драйвер) в тех местах, где идёт разыменнование чего либо с аргументом. Говоря по дебильному: везде где есть выражение *(тип*)(a1 + что-нибудь или вообще ничего) заменяем на своё чтение памяти
Результат:
decryption-routine-inmycode-fixed-2.png

Называем функцию в стиле decrypt_client_entities_wrapper (мне вообще без разницы как вы её назовёте).
Не обращайте внимание на красный il2cpp_gchandle_get_target - мы к нему ещё вернёмся.

По такому же принципу расшифровываем entity_list. Нам осталось понять как из %fd9c39723c3f962c0308a43e4106cb774701a27c (в прошлом ListDictionary) получить список сущностей и массив из них. ListDictionary это generic класс - то есть обобщённый класс с разными типами в <>. Поэтому у полей все оффсеты равны 0, а все методы имеют rva -1
listdictionary-latest.png

Под методами дампер собрал нам GenericInstMethod - реализации функций для конкретных типов. Например: функция с rva 0x5930B70 применяется для типов с <object, object>, <object, float>, <uint, object>, а функция с rva 0x7F4FA10 применяется только для типа <__Il2CppFullySharedGenericType, __Il2CppFullySharedGenericType>
Напомню, у нас с вами тип <%1c24d316ef52282e54d325685a623c64a0124e28, BaseNetworkable>:
realmclient-cliententities-generic-type.png

В классе ListDictionary мне хотелось бы посмотреть реализацию функции int %c11b6f5f8ca36f1b5661153e32660fc2f41ea536(). По её пустому количеству аргументов и возвращаемому типу мне кажется, что это функция count. Конкретного типа реализации для <%1c24d316ef52282e54d325685a623c64a0124e28, BaseNetworkable> нету, но есть <%1c24d316ef52282e54d325685a623c64a0124e28, object> (ведь BaseNetworkable обычный объект по лору C#)

Переходим по нужному RVA и наблюдаем следующее:
(со скриптом)
getcount-from-list-function.png


(без)
countfunction-from-list-no-script.png


Со скриптом мы прекрасно понимаем, что эта функция действительно является количеством элементов в списке. Т.к. fields это просто вложенная структура, то мы складываем оффсеты.
fields оффсет - 0x10
_size оффсет - 0x8 (0x10 + 0x8 = 0x18 итог)
_items (массив) оффсет - 0x0 (0x10 + 0x0 = 0x10 итог)
image.png

generic-list-fields-struct-from-il2cpp-header.png


Долгожданный il2cpp_gchandle_get_target
На этом долго останавливаться не буду, эта функция не менялась со времён эры динозавров за исключием одного оффсета:
gchandle-get-target-inmycode.png

Данный оффсет берём с иды:
gchandle-get-target-from-ida.png


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