- Статус
- Оффлайн
- Регистрация
- 13 Фев 2026
- Сообщения
- 538
- Реакции
- 14
Здорово, реверсеры. Залетел тут технический вопрос по мобильному Unity и препарированию libil2cpp.so на ARMv7. Ситуация классическая: есть функция, возвращающая массив байт (byte[]), и нужно подменить этот возврат, но только в одном конкретном месте (func2), не трогая оригинал во всем остальном коде.
Суть идеи была в том, чтобы просто впихнуть свои байты в .data или .rodata и подменить вызов BL func1 на загрузку адреса через LDR R0, [R1].
Почему это не сработает в лоб?
В Unity (через призму IL2CPP) массивы C# — это не просто «голый» кусок памяти с данными, как в C++. Это полноценные объекты. Если ты просто подставишь указатель на свой DCB массив в R0, игра моментально отлетит в краш.
Когда func2 получит твой «голый» указатель, она попытается прочитать Length или обратиться к элементу. Проц полезет по смещению искать метаданные объекта, увидит там твои мусорные байты вместо валидных указателей и выдаст Access Violation.
Как это разрулить?
Варианта два. Первый — патчить не возврат функции, а саму логику в func2, чтобы она брала данные из твоего адреса напрямую, минуя методы массива. Второй (более цивильный) — использовать хуки. Через какой-нибудь Dobby или KittyMemory можно вызвать оригинальную il2cpp_array_new, которая создаст честный массив в куче, заполнить его своими данными и вернуть в R0.
Если же нужно именно статикой в либе — придется либо искать в памяти уже готовый массив нужной длины, либо имитировать всю структуру объекта в .data, что на мобилках может быть нестабильно из-за динамических адресов классов.
Кто пробовал собирать фейковые Il2Cpp-объекты прямо в секции данных, насколько это вообще жизнеспособно без инициализации метаданных в рантайме?
Суть идеи была в том, чтобы просто впихнуть свои байты в .data или .rodata и подменить вызов BL func1 на загрузку адреса через LDR R0, [R1].
Почему это не сработает в лоб?
В Unity (через призму IL2CPP) массивы C# — это не просто «голый» кусок памяти с данными, как в C++. Это полноценные объекты. Если ты просто подставишь указатель на свой DCB массив в R0, игра моментально отлетит в краш.
- Структура Il2CppArray всегда начинается с заголовка (Il2CppObject), где лежат метаданные (ссылки на класс и т.д.).
- Далее идет поле max_length (иногда еще Il2CppArrayBounds), которое хранит размер массива.
- Только после этих полей в памяти начинаются реальные байты данных.
Когда func2 получит твой «голый» указатель, она попытается прочитать Length или обратиться к элементу. Проц полезет по смещению искать метаданные объекта, увидит там твои мусорные байты вместо валидных указателей и выдаст Access Violation.
В рантайме это выглядит примерно так:
Чтобы твой патч сработал, R0 должен указывать на начало такой структуры, а не на начало данных.
Код:
typedef struct {
Il2CppObject obj;
Il2CppArrayBounds *bounds;
uint32_t max_length;
uint8_t vector[VARIABLE_SIZE];
} Il2CppArray;
Как это разрулить?
Варианта два. Первый — патчить не возврат функции, а саму логику в func2, чтобы она брала данные из твоего адреса напрямую, минуя методы массива. Второй (более цивильный) — использовать хуки. Через какой-нибудь Dobby или KittyMemory можно вызвать оригинальную il2cpp_array_new, которая создаст честный массив в куче, заполнить его своими данными и вернуть в R0.
Если же нужно именно статикой в либе — придется либо искать в памяти уже готовый массив нужной длины, либо имитировать всю структуру объекта в .data, что на мобилках может быть нестабильно из-за динамических адресов классов.
Кто пробовал собирать фейковые Il2Cpp-объекты прямо в секции данных, насколько это вообще жизнеспособно без инициализации метаданных в рантайме?