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

Гайд Декриптуем метадату / часть 1 - Классы

Начинающий
Начинающий
Статус
Оффлайн
Регистрация
31 Окт 2021
Сообщения
141
Реакции
3
Гайд как расшифровать любой файл игры на il2cpp / mono

Наши инструменты - HxD, Ghidra, il2cppinspector, python.

Все делаем по классике - вытягиваем валидную либку с метаданными, прогоняем через дампер и получаем обычное сообщение что метадата неверная (не нужно верить сообщению так как это обычный шифр на ней)

Открываем HxD и вставляем свою метадату, там мы должны увидеть не нули а AF B1 и так далее, если вы такое видите значит идем дальше - если нули то ищем другую метадату.

Открываем гидру и ищем указатель update(или что то может быть похожее)


По указателю мы должны попасть в модульную структуру il2cpp, а именно загрузку всех классов до их определения
1768418692669.png

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

Дальше отправляемся в go - to
1768418792083.png

и мы должны будет с псевдокода update взять валидный fun для глубокого определения их взаимодействия, в моем же случае это FUN_00f6acac
1768418931227.png

видим такую картину, если вы видите что то похожее то вы на верном пути, это поисковой алгоритм самой либки которая отвечает за хеширование специальных обьектов, функция uVar3 = FUN_00f6adc0(param_2,param_3) отвечает что функция 00f6adc0 отвечает за указатель данного применения хеширования
данный код весь выполняется как хеш-таблица, которая вычисляет индекс uVar9-7 на основе полученных данных.


из всего вышеперечисленного мы можем уверенно сказать что 8 битный long в структуре il2cpp класса является указателем на следующий элемент в списке.
следующая функция FUN_00f66f90
1768419448923.png

и еще
1768419533768.png

и еще
1768419555546.png


puvar32 является указателем il2cpptypedefinition которая так же создается в метадате игры, а вот 0x5c является размером записи
сохраняем данную информацию в голове

далее -
из всего кода можно понять что базовый размер структуры составляет 0x130 байт что подтверждает что данный псевдокод указывает нам на валидную структуру в метадате

так же если вы заметили то puvar32 и 38 часто встречается в псевдокоде из-за чего можно подтвердить что наша метадата перед дампом проверяет все классы

из чего можно сделать следующую структуру нашего анализа
long* image; 0x00: *plVar24 = lVar39;
uint typeDefinitionIndex; 0x04: Из FUN_00f65494: `*(uint *)(plVar1 + 4)`.
uint flags_or_padding_0x08; 0x08: В FUN_00f6acac - здесь был names_hash.

.


const char* name; 0x10: plVar24[2] = (long)puVar29
const char* namespaze; 0x18: plVar24[3] = (long)puVar29; (

long* parent; 0x58: plVar24[0xb] = (long)plVar23;
long* declaringType; 0x50: plVar24[10] = (long)plVar23;
long* element_class; 0x48: plVar24[9] = (long)plVar24;
long* castClass; 0x40: plVar24[8] = (long)plVar24;
// Метаданные //
void* typeMetadataHandle; 0x68: plVar24[0xd] = (long)puVar32;
uint field_count; 0x110: *(uint *)(plVar24 + 0x22) = puVar32[8]; количество полей
uint method_count; 0x114: *(uint *)((long)plVar24 + 0x114) = puVar32[0x16];

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

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

конец 1 части
 
переименновать переменные - ❌
записывать в блокнотик что такое puVar24 - ✅

зная уже эти базовые параметры можно предположить что загрузка классов в любом случае происходит в этой либке
какой нахуй либке. бля а чё ты просто ассемблерный код вставкой на форуме не сделал? не объяснено абсолютно ничего. ищем указатель на update, какой блять апдейт, откуда ты его взял. в какой блять либке происходит загрузка классов, она либо в GameAssembly.dll либо в libunity.so либо в il2cpp.so. скорее всего ты говоришь об assembly или image типах в il2cpp. ну так может ты расскажешь о них?

из чего можно сделать следующую структуру нашего анализа
long* image; 0x00: *plVar24 = lVar39;
uint typeDefinitionIndex; 0x04: Из FUN_00f65494: `*(uint *)(plVar1 + 4)`.
uint flags_or_padding_0x08; 0x08: В FUN_00f6acac - здесь был names_hash.

.


const char* name; 0x10: plVar24[2] = (long)puVar29
const char* namespaze; 0x18: plVar24[3] = (long)puVar29; (

long* parent; 0x58: plVar24[0xb] = (long)plVar23;
long* declaringType; 0x50: plVar24[10] = (long)plVar23;
long* element_class; 0x48: plVar24[9] = (long)plVar24;
long* castClass; 0x40: plVar24[8] = (long)plVar24;
// Метаданные //
void* typeMetadataHandle; 0x68: plVar24[0xd] = (long)puVar32;
uint field_count; 0x110: *(uint *)(plVar24 + 0x22) = puVar32[8]; количество полей
uint method_count; 0x114: *(uint *)((long)plVar24 + 0x114) = puVar32[0x16];
какого анализа
1768425415942.png

что это нахуй

давай для тебя такой же гайд сделаю. сегодня гайд на дамп schema system в кс2
1768425455917.png

вот я нашёл указатель update, как мы видим тут есть оффсеты нужные для дампа. ТЫ ЧЁ НАКУРИЛСЯ ИЛИ ЧТО

uint flags_or_padding_0x08; 0x08: В FUN_00f6acac - здесь был names_hash.
откуда ты это взял. поле с названием flags_or_padding_0x08, которое в твоей функции обозначено за names_hash. так может его так и назовёшь, не?

вспомните что мы разбираем с вами только функцию update
да какую нахуй функцию update
1768425588098.png

ты ей сам название придумал? если что, после скачивания юнити и il2cpp аддона (или как правильно сказать, build type'а) у тебя сурс уже на компе, зачем ты что-то гадаешь.

мне ещё особенно нравится твой выбор использовать декомпилятор гидры. нет бы скачать IDA 9.2 или IDA 9.3 Beta где есть поддержка aarch64 декомпилтора (ну судя по NEON_uaddlv это aarch64) или Binary ninja где ахуенный декомпилтор

но не, лучше посмотреть на это:
1768425848757.png
 
переименновать переменные - ❌
записывать в блокнотик что такое puVar24 - ✅
В данном гайде была затронута тема ручного декрипта либки для метадаты
Новичкам в данной теме не очень будет удобно добавлять комментарии так как информации действительно много и ссылаться на комментарии чтобы потом в будущем тупо в них запутаться это не очень круто, в моем же случае если написать в блокнот чтобы в дальнейшем по хрефам уже анализировать это намного удобнее, так как ты это делаешь не вручную
какой нахуй либке. бля а чё ты просто ассемблерный код вставкой на форуме не сделал? не объяснено абсолютно ничего. ищем указатель на update, какой блять апдейт, откуда ты его взял. в какой блять либке происходит загрузка классов, она либо в GameAssembly.dll либо в libunity.so либо в il2cpp.so. скорее всего ты говоришь об assembly или image типах в il2cpp. ну так может ты расскажешь о них?
update это функция в strings, буквально самая первая идет при загрузке либки
вопрос про какую либку идет речь
1768426541005.png
, в разных играх разные либки, хочу тебя удивить но не все игры используют gameassembly либо же libil2cpp, на моем примере разбиралась либка liblogic.

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


какого анализа
Посмотреть вложение 324875
что это нахуй

давай для тебя такой же гайд сделаю. сегодня гайд на дамп schema system в кс2
Посмотреть вложение 324876
вот я нашёл указатель update, как мы видим тут есть оффсеты нужные для дампа. ТЫ ЧЁ НАКУРИЛСЯ ИЛИ ЧТО
первое это анализ уже загрузочного блока апдейт, который отвечает за загрузку и накладывания хеша на классы, и речи не шло чтобы найти какие то оффсеты, если ты впервые видишь такую картину то это не моя проблема.


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


да какую нахуй функцию update
Посмотреть вложение 324877
ты ей сам название придумал? если что, после скачивания юнити и il2cpp аддона (или как правильно сказать, build type'а) у тебя сурс уже на компе, зачем ты что-то гадаешь.

мне ещё особенно нравится твой выбор использовать декомпилятор гидры. нет бы скачать IDA 9.2 или IDA 9.3 Beta где есть поддержка aarch64 декомпилтора (ну судя по NEON_uaddlv это aarch64) или Binary ninja где ахуенный декомпилтор

но не, лучше посмотреть на это:
Посмотреть вложение 324878
я назвал так функцию потому что в коде она указана с таким флагом, опять же чтобы на будущее не запутаться я пишу цельные строчки и другим рекомендую
функция апдейт - в первом моем ответе
по поводу выбора дизассемблера, ЭТО ЛИЧНО МОЙ ВЫБОР, так как в гидре мне намного удобнее смотреть псевдокод и удобнее с ним работать, ида начиная с версии 7.0 обновляла зачастую поддержку пайтона, да и сам интерфейс меня не так сильно забавляет как в гидре

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


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

update это функция в strings
update это функция в строках. у тебя в строках функции? хм, не знал, у меня там текст обычно, буду знать что во вкладке strings лежат функции

В данном гайде была затронута тема ручного декрипта либки для метадаты
бро, ты реально накурен. чем то явно тяжелым. господи, как же тяжело. есть функция, которая парсит файл global-metadata.dat и парсит некоторые поля с неё:
C++:
Expand Collapse Copy
bool il2cpp::vm::GlobalMetadata::Initialize(int32_t* imagesCount, int32_t* assembliesCount)
{
    s_GlobalMetadata = vm::MetadataLoader::LoadMetadataFile("global-metadata.dat");
    if (!s_GlobalMetadata)
        return false;

    s_GlobalMetadataHeader = (const Il2CppGlobalMetadataHeader*)s_GlobalMetadata;
    IL2CPP_ASSERT(s_GlobalMetadataHeader->sanity == 0xFAB11BAF);
    IL2CPP_ASSERT(s_GlobalMetadataHeader->version == 27);

    s_MetadataImagesCount = *imagesCount = s_GlobalMetadataHeader->imagesCount / sizeof(Il2CppImageDefinition);
    *assembliesCount = s_GlobalMetadataHeader->assembliesCount / sizeof(Il2CppAssemblyDefinition);

    // Pre-allocate these arrays so we don't need to lock when reading later.
    // These arrays hold the runtime metadata representation for metadata explicitly
    // referenced during conversion. There is a corresponding table of same size
    // in the converted metadata, giving a description of runtime metadata to construct.
    s_MetadataImagesTable = (Il2CppImageGlobalMetadata*)IL2CPP_CALLOC(s_MetadataImagesCount, sizeof(Il2CppImageGlobalMetadata));
    s_TypeInfoTable = (Il2CppClass**)IL2CPP_CALLOC(s_Il2CppMetadataRegistration->typesCount, sizeof(Il2CppClass*));
    s_TypeInfoDefinitionTable = (Il2CppClass**)IL2CPP_CALLOC(s_GlobalMetadataHeader->typeDefinitionsCount / sizeof(Il2CppTypeDefinition), sizeof(Il2CppClass*));
    s_MethodInfoDefinitionTable = (const MethodInfo**)IL2CPP_CALLOC(s_GlobalMetadataHeader->methodsCount / sizeof(Il2CppMethodDefinition), sizeof(MethodInfo*));
    s_GenericMethodTable = (const Il2CppGenericMethod**)IL2CPP_CALLOC(s_Il2CppMetadataRegistration->methodSpecsCount, sizeof(Il2CppGenericMethod*));

    ProcessIl2CppTypeDefinitions(InitializeTypeHandle, InitializeGenericParameterHandle);

    return true;
}
предполагаю в голове ты уже доебался до того, что «у меня нету global-metadata.dat строчки/строчки»

не знаю что у тебя за 1337 игра с ультра шифрованием, но подход везде один. в стендоффе функция vm::MetadataLoader::LoadMetadataFile возвращала указатель на буфер в секции, куда она зашита. из-за этого первый аргумент не использовался и окно со строками могло эту строчку просто не отобразить

в какой-то другой игре (человек зачем то удалил переписку, какая то пародия на раст) после считывания файла в буфер происходила его расшифровка, можно было просто поставить бряк на ret и сдампить метадату


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


я назвал так функцию потому что в коде она указана с таким флагом
твои же слова «я назвал так ФУНКЦИЮ»
uint flags_or_padding_0x08; 0x08

по моему это явно не функция. функция по синтаксису выглядит так «возвращаемый_тип название(тип_аргумента название_аргумента, …) { тело функции }•
а у тебя явно не функция


в коде она указана с таким флагом
флаг у функции? ахахахах скажи честно, ты может просто поугарать тему создал или что?

Дальше отправляемся в go - to
а зачем это обьяснять? то есть как ты нашел эту функцию обьяснять не надо, а как обьяснить переход по адресу так ты первый


ида начиная с версии 7.0 обновляла зачастую поддержку пайтона
угу, а я тебе случаем не написал отличие версий в прошлом ответе???

ну и. ну вместо libunity или libil2cpp у тебя liblogic и что мне делать с этим? как я по твоему тупому псевдомусору пойму что это именно загрузка классов, когда ты ничего не пояснил


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


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