Реверс-инжиниринг Il2cpp: расшифровка глобальных метаданных
Реверс-инжиниринг Il2cpp: расшифровка глобальных метаданных

Предисловие

 В Интернете много информации об Il2cpp,суммируя,Il2cpp — это новый метод упаковки, используемый Unity для замены исходной виртуальной машины Mono.,Сначала он генерирует IL (промежуточный язык).,Затем преобразуйте его в файл CPP.,Повысьте эксплуатационную эффективность и одновременно повысьте безопасность. Исходный метод упаковки, основанный на Mono, чрезвычайно легко отменить.,Новые игры, представленные сейчас на рынке, в основном упаковываются с использованием Il2cpp.,Конечно, существует также множество руководств по обратному инжинирингу для Il2cpp.,Но они все одинаковые,Научит пользоваться, написано домашним начальникомIl2cppDumperидтиdumpВот и все,Технического содержания нет. фактически,Потому что этот инструмент настолько известен,Многие производители игр приняли контрмеры,В результате, даже если вы будете следовать руководству,В большинстве случаев это не удастся. Поэтому я планирую изучить технологии атаки и защиты, связанные с Il2cpp.,Итак, я нашел один в ИнтернетеIl2cppизCTFВопросы для практики。Источник вопроса:n1ctf-2018

baby unity3d

 Требования к вопросам очень ясны,Просто введите правильный флаг. Теперь, когда мы знаем, что это программа Unity, использующая Il2cpp.,Тогда просто прямоидтинайди этоизlibil2cpp.soа такжеglobal-metadata.datдокумент,Затем попробуйте проанализировать с помощью Il2cppDumper.,Конечно, анализ не удастся. Причина сбоя парсинга должна быть в этих двух файлах.,По крайней мере один файл зашифрован,В результате нормальный анализ выполнить невозможно. Этот вопрос относительно простой,Только зашифрованноеglobal-metadata.datдокумент,может бытьglobal-metadata.datПеретащите010EditorПроверять,нормальныйизglobal-metadata.datначалоиз Четыре байта должны бытьAF 1B B1 FA,И этот вопросизglobal-metadata.datОчевидно, оно было зашифровано.,Так что для завершения разбора его нужно только расшифровать,Последующая проблема с флагом легко решается.

  Хочу расшифроватьglobal-metadata.datУ нас есть две идеи,Один из них — сбросить результат расшифровки,Другой — анализировать алгоритмы шифрования. Для первой идеи,Вот скрипт фриды

Язык кода:javascript
копировать
function frida_Memory(pattern)
{
Java.perform(function ()
{
    console.log("Идентификатор заголовка:" + pattern);
    var addrArray = Process.enumerateRanges("r--");
    for (var i = 0; i < addrArray.length; i++)
    {
        var addr = addrArray[i];
        Memory.scan(addr.base, addr.size, pattern,
        {
            onMatch: function (address, size)
            {
                console.log('Найти ' + pattern + " Адрес:" + address.toString());
                console.log(hexdump(address,
                    {
                        offset: 0,
                        length: 64,
                        header: true,
                        ansi: true
                    }
                    ));
                //0x108, 0x10C Если не работает, замените на 0x100, 0x104
                var DefinitionsOffset = parseInt(address, 16) + 0x108;
                var DefinitionsOffset_size = Memory.readInt(ptr(DefinitionsOffset));

                var DefinitionsCount = parseInt(address, 16) + 0x10C;
                var DefinitionsCount_size = Memory.readInt(ptr(DefinitionsCount));

                //Получаем размер глобальных метаданных на основе двух смещений
                var global_metadata_size = DefinitionsOffset_size + DefinitionsCount_size
                    console.log("Размер:", global_metadata_size);
                var file = new File("/data/data/" + get_self_process_name() + "/global-metadata.dat", "wb");
                file.write(Memory.readByteArray(address, global_metadata_size));
                file.flush();
                file.close();
                console.log('Экспорт завершен...');
            },
            onComplete: function ()
            {
                //console.log("Поиск завершен")
            }
        }
        );
    }
}
);
}
setImmediate(frida_Memory("AF 1B B1 FA")); //функции заголовка global-metadata.dat

Вероятно, процесс заключается в использовании магии для определения начального адреса файла в памяти.,Затем вычислите размер файла, анализируя заголовок файла.,продолжать последнимdump。Сценарийиз Применимые условия:global-metadata.datсуществовать解密后必须要有нормальныйиз Магия - этоAF 1B B1 FAВ противном случае позиционирование,При этом информация заголовка файла должна быть корректной, иначе размер файла невозможно будет вычислить. Этот скрипт имеет определенное ссылочное значение,Однако это не работает для этой проблемы,Начальный адрес не был найден после выполнения скрипта.,Кажется, даже после расшифровки,В памяти тоже нетAF 1B B1 FAсуществовать。Итак, этот генерализdumpЭтот метод, вероятно, больше не будет работать,можно найти толькоglobal-metadata.datизфункция загрузки,Подождите, пока расшифровка завершится, прежде чем выгружать файл.,Поэтому нам нужноglobal-metadata.datиз Загрузить процесс для анализа。

global-metadata.dat加载流程

  这篇文章IL2CPP Tutorial: Finding loaders for obfuscated global-metadata.dat filesверноglobal-metadata.datПроцесс загрузки очень подробный.изпредставлять,Определенно стоит прочитать. Позвольте мне кратко изложить это здесь,существоватьlibil2cpp.soЕсть один внутриil2cpp_initфункция是функция загрузки调用链中из Нет.одинфункция,Вся цепочка вызовов такая

Язык кода:javascript
копировать
il2cpp_init
  -> il2cpp::vm::Runtime::Init
    -> il2cpp::vm::MetadataCache::Initialize
      -> il2cpp::vm::MetadataLoader::LoadMetadataFile

  мы можемсуществоватьlibil2cpp.soИскать внутриil2cpp_initИли во всей цепочке вызововиз Ключевые слова для таргетингаодинфункция,Самый простойизпутем поискаglobal-metadata.datчтобы найти непосредственноMetadataCache::Initialize,Но этот вопрос не работает,Потому что спрашивающий намеренно поставилglobal-metadata.datЭта строка зашифрована,Так что его невозможно найти。Итак, мы ищемil2cpp_initПриходитьверно Согласно исходному коду, перейдите вниз кMetadataCache::Initialize

Что делать, если ни одна из вышеперечисленных функций не найдена?,Собственно, в статье выше об этом тоже упоминалось,существоватьlibunity.soБудетверноil2cpp_initВыполните анализ символов,возьмиизадрес。Просто обратитесь к статье выше для получения подробной информации.。Другой пример в этой статье:il2cpp_initбыло сделаноROT-5иметь дело с,Имя функции становитсяnq2huu_nsny,Затемя Обнаружитьсуществоватья自己找из НекоторыйcaseИскать здесьnq2huu_nsnyтакже можно найти,Итак, этоnq2huu_nsnyТакже стоит попробовать。

il2cpp_init

Язык кода:javascript
копировать
int __fastcall il2cpp_init(int a1)
{
  setlocale(6, "");
  return sub_4C4770(a1, "v2.0.50727");
}

sub_4C4770

Язык кода:javascript
копировать
int __fastcall sub_4C4770(int a1)
{
.......

  v1 = nullsub_3();
  v2 = nullsub_1(v1);
  v3 = sub_514E34(v2);
  dword_695A80 = (int)"2.0";
  v4 = sub_4F8468(v3);
  v5 = sub_5171B8(v4);
  v6 = sub_4B5564(v5);
  v7 = sub_501A60(v6);
  v8 = sub_4FA8B8(v7);
  v9 = sub_4E0D84(v8);
  sub_4D566C(v9);
  memset(&dword_695AB0, 0, 0x13Cu);
  v10 = sub_5017E4("mscorlib.dll");
  dword_695AB0 = il2cpp_assembly_get_image_0(v10);
  dword_695AB4 = ((int (*)(void))il2cpp_class_from_name_0)();
  dword_695ABC = il2cpp_class_from_name_0(dword_695AB0, "System", "Void");
  dword_695AC0 = il2cpp_class_from_name_0(dword_695AB0, "System", "Boolean");
  dword_695AB8 = il2cpp_class_from_name_0(dword_695AB0, "System", "Byte");
  dword_695AC4 = il2cpp_class_from_name_0(dword_695AB0, "System", &unk_5BA5E1);
  dword_695AC8 = il2cpp_class_from_name_0(dword_695AB0, "System", "Int16");
  dword_695ACC = il2cpp_class_from_name_0(dword_695AB0, "System", &unk_5BA5E7);
  dword_695AD0 = il2cpp_class_from_name_0(dword_695AB0, "System", "Int32");

......

Нажмите, чтобы увидеть по одному,Обнаружитьsub_4B5564На самом деле этоMetadataCache::Initialize

Язык кода:javascript
копировать
void sub_4B5564()
{
  void *v0; // r4
  int v1; // r4
  unsigned int v2; // r7
  int v3; // r0
  int v4; // lr
  int v5; // r2
  int v6; // r4
  int v7; // r3
  _DWORD *v8; // r1
  int v9; // r6
  int v10; // r0
  unsigned int v11; // r3
  int v12; // r7
  int v13; // r1
  unsigned int v14; // r1
  unsigned int v15; // r9
  int v16; // r6
  unsigned __int16 v17; // r0
  unsigned __int16 *v18; // r6
  int v19; // t1
  _DWORD *v20; // r7
  unsigned __int16 v21; // r4
  int v22; // r2
  int v23; // r1
  int v24; // r0
  int v25; // r6
  int v26; // r7
  int v27; // r1
  int v28; // [sp+8h] [bp-48h]
  unsigned int v29; // [sp+Ch] [bp-44h]
  int v30; // [sp+10h] [bp-40h]
  int v31; // [sp+14h] [bp-3Ch]
  int v32[2]; // [sp+18h] [bp-38h] BYREF
  int v33; // [sp+20h] [bp-30h] BYREF
  int v34; // [sp+24h] [bp-2Ch]
  double v35; // [sp+28h] [bp-28h] BYREF
  int v36; // [sp+30h] [bp-20h]

  v0 = (void *)sub_4B5518("CLKFIL\rMETIDITI\nDIT", 19);
  dword_6959CC = sub_513060();
  free(v0);
  dword_6959D0 = dword_6959CC;
  v28 = dword_6959CC + *(_DWORD *)(dword_6959CC + 184);
  if ( *(_DWORD *)(dword_6959CC + 188) >= 0x44u )
  {
    v1 = dword_6959CC + *(_DWORD *)(dword_6959CC + 184);
    v2 = 0;
    do
    {
      sub_5019F8(v1);
      v1 += 68;
      ++v2;
    }
    while ( v2 < *(_DWORD *)(dword_6959D0 + 188) / 0x44u );
  }
  dword_6959D4 = sub_5169D4(*(_DWORD *)(dword_6959C4 + 24), 4);
  dword_6959D8 = sub_5169D4(*(_DWORD *)(dword_6959D0 + 164) / 0x68u, 4);
  dword_6959DC = sub_5169D4(*(_DWORD *)(dword_6959D0 + 52) / 0x38u, 4);
  dword_6959E0 = sub_5169D4(*(_DWORD *)(dword_6959C4 + 32), 4);
  dword_6959E4 = *(_DWORD *)(dword_6959D0 + 180) / 0x18u;
  v3 = sub_5169D4(dword_6959E4, 28);
  dword_6959E8 = v3;
  if ( dword_6959E4 >= 1 )
  {
    v4 = dword_6959CC;
    v5 = 0;
    v6 = dword_6959D0;
    v7 = 12;
    v8 = (_DWORD *)(*(_DWORD *)(dword_6959D0 + 176) + dword_6959CC + 12);
    while ( 1 )
    {
      v9 = v3 + v7;
      ++v5;
      *(_DWORD *)(v9 - 12) = v4 + *(_DWORD *)(v6 + 24) + *(v8 - 3);
      *(_DWORD *)(v9 - 8) = *(v8 - 2);
      *(_DWORD *)(v9 - 4) = *(v8 - 1);
      *(_DWORD *)(v3 + v7) = *v8;
      *(_DWORD *)(v9 + 4) = v8[1];
      *(_DWORD *)(v9 + 12) = v8[2];
      if ( v5 >= dword_6959E4 )
        break;
      v7 += 28;
      v8 += 6;
      v6 = dword_6959D0;
      v4 = dword_6959CC;
      v3 = dword_6959E8;
    }
  }
  sub_4B5A28();
  v35 = 0.0;
  v36 = 0;
  v10 = dword_6959D0;
  if ( *(_DWORD *)(dword_6959D0 + 188) >= 0x44u )
  {
    v11 = 0;
    v31 = dword_6959CC + *(_DWORD *)(dword_6959D0 + 160);
    do
    {
      v12 = 0;
      v13 = *(_DWORD *)(v28 + 68 * v11);
      if ( v13 != -1 )
        v12 = dword_6959E8 + 28 * v13;
      v30 = v12;
      v14 = *(_DWORD *)(v12 + 12);
      if ( v14 )
      {
        v15 = 0;
        v29 = v11;
        do
        {
          v16 = v31 + 104 * (*(_DWORD *)(v12 + 8) + v15);
          v19 = *(unsigned __int16 *)(v16 + 80);
          v18 = (unsigned __int16 *)(v16 + 80);
          v17 = v19;
          if ( v19 )
          {
            v20 = (_DWORD *)(v31 + 104 * (*(_DWORD *)(v12 + 8) + v15) + 52);
            v21 = 0;
            do
            {
              v22 = *(_DWORD *)(dword_6959D0 + 48);
              v34 = *v20 + v21;
              v23 = *(_DWORD *)(dword_6959CC + v22 + 56 * v34 + 24);
              if ( v23 == -1 )
              {
                v33 = 0;
              }
              else
              {
                v33 = *(_DWORD *)(*(_DWORD *)(dword_6959C0 + 4) + 4 * v23);
                if ( v33 )
                {
                  sub_4B5CFC(&v35, &v33);
                  v17 = *v18;
                }
              }
              ++v21;
            }
            while ( v21 < (unsigned int)v17 );
            v12 = v30;
            v14 = *(_DWORD *)(v30 + 12);
          }
          ++v15;
        }
        while ( v15 < v14 );
        v11 = v29;
        v10 = dword_6959D0;
      }
      ++v11;
    }
    while ( v11 < *(_DWORD *)(v10 + 188) / 0x44u );
  }
  v24 = dword_6959C4;
  if ( *(int *)(dword_6959C4 + 16) >= 1 )
  {
    v25 = 0;
    v26 = 0;
    do
    {
      v27 = *(_DWORD *)(v24 + 20);
      v32[1] = *(_DWORD *)(*(_DWORD *)(v24 + 36) + 12 * *(_DWORD *)(v27 + v25));
      v32[0] = *(_DWORD *)(*(_DWORD *)(dword_6959C0 + 20) + 4 * *(_DWORD *)(v27 + v25 + 4));
      sub_4B5CFC(&v35, v32);
      v24 = dword_6959C4;
      v25 += 12;
      ++v26;
    }
    while ( v26 < *(_DWORD *)(dword_6959C4 + 16) );
  }
  sub_4C70FC(&v35);
  if ( LODWORD(v35) )
    operator delete((void *)LODWORD(v35));
}

Среди них этотsub_4B5518("CLKFIL\rMETIDITI\nDIT", 19);Просто расшифруйте строку вglobal-metadata.datиз Расположение。

Язык кода:javascript
копировать
_BYTE *__fastcall sub_4B5518(char *a1, int a2)
{
  _BYTE *result; // r0
  int v5; // r1
  _BYTE *v6; // r2
  char v7; // t1

  result = malloc(a2 + 1);
  if ( a2 >= 1 )
  {
    v5 = a2;
    v6 = result;
    do
    {
      v7 = *a1++;
      --v5;
      *v6++ = (v7 - 2) ^ 0x26;
    }
    while ( v5 );
  }
  result[a2] = 0;
  return result;
}

Затемsub_513060тогда это на самом делеMetadataLoader::LoadMetadataFile

Язык кода:javascript
копировать
int __fastcall sub_513060(const char *a1)
{
  void *v2; // r0
  int v3; // r4
  int v4; // r6
  size_t v5; // r5
  int v6; // r8
  unsigned int *v7; // r2
  int v8; // r1
  void *v9; // r0
  void *v10; // r0
  unsigned int *v12; // r2
  int v13; // r1
  unsigned int *v14; // r2
  int v15; // r1
  int v16; // [sp+Ch] [bp-4Ch] BYREF
  int v17[2]; // [sp+10h] [bp-48h] BYREF
  int v18; // [sp+18h] [bp-40h] BYREF
  int v19[2]; // [sp+1Ch] [bp-3Ch] BYREF
  int v20; // [sp+24h] [bp-34h] BYREF
  int v21; // [sp+28h] [bp-30h] BYREF
  int v22[2]; // [sp+2Ch] [bp-2Ch] BYREF
  int v23[2]; // [sp+34h] [bp-24h] BYREF

  sub_4C5B40(&v20);
  v19[0] = (int)"Metadata";
  v19[1] = 8;
  v22[0] = v20;
  v22[1] = *(_DWORD *)(v20 - 12);
  sub_4C7F74(&v21, v22, v19);
  v2 = (void *)(v20 - 12);
  if ( (_UNKNOWN *)(v20 - 12) != &unk_6A25F4 )
  {
    v7 = (unsigned int *)(v20 - 4);
    __dmb(0xBu);
    do
      v8 = __ldrex(v7);
    while ( __strex(v8 - 1, v7) );
    __dmb(0xBu);
    if ( v8 <= 0 )
      j_operator delete(v2);
  }
  v17[0] = (int)a1;
  v17[1] = strlen(a1);
  v23[0] = v21;
  v23[1] = *(_DWORD *)(v21 - 12);
  sub_4C7F74(&v18, v23, v17);
  v3 = 0;
  v16 = 0;
  v4 = sub_4CDA80(&v18, 3, 1, 1, 0, &v16);
  if ( !v16 )
  {
    v5 = sub_4CDE4C(v4, &v16);
    if ( !v16 )
    {
      v6 = sub_5163A8(v4, 0, 0);
      sub_4CDCF4(v4, &v16);
      if ( v16 )
      {
        v3 = 0;
        sub_516540(v6, 0);
      }
      else
      {
        v3 = sub_512FDC(v6, v5);
      }
    }
  }
  v9 = (void *)(v18 - 12);
  if ( (_UNKNOWN *)(v18 - 12) != &unk_6A25F4 )
  {
    v12 = (unsigned int *)(v18 - 4);
    __dmb(0xBu);
    do
      v13 = __ldrex(v12);
    while ( __strex(v13 - 1, v12) );
    __dmb(0xBu);
    if ( v13 <= 0 )
      j_operator delete(v9);
  }
  v10 = (void *)(v21 - 12);
  if ( (_UNKNOWN *)(v21 - 12) != &unk_6A25F4 )
  {
    v14 = (unsigned int *)(v21 - 4);
    __dmb(0xBu);
    do
      v15 = __ldrex(v14);
    while ( __strex(v15 - 1, v14) );
    __dmb(0xBu);
    if ( v15 <= 0 )
      j_operator delete(v10);
  }
  return v3;
}

верночем исходный код Обнаружитьэтотsub_512FDCЭто функция расшифровки

Язык кода:javascript
копировать
char *__fastcall sub_512FDC(int a1, size_t size)
{
  char *result; // r0
  size_t v5; // r2

  result = (char *)malloc(size);
  if ( size )
  {
    v5 = 0;
    do
    {
      *(_DWORD *)&result[v5 & 0xFFFFFFFC] = *(_DWORD *)(a1 + (v5 & 0xFFFFFFFC)) ^ dword_5DCF6C[(v5 + v5 / 0x84) % 0x84];
      v5 += 4;
    }
    while ( v5 < size );
  }
  return result;
}

Напишите скрипт расшифровки

Язык кода:javascript
копировать
import struct
f = open('global-metadata.dat', 'rb')
a = ""
a = f.read()
key = [0xF83DA249, 0x15D12772, 0x40C50697, 0x984E2B6B, 0x14EC5FF8, 0xB2E24927,
       0x3B8F77AE, 0x472474CD, 0x5B0CE524, 0xA17E1A31, 0x6C60852C, 0xD86AD267, 0x832612B7, 0x1CA03645, 0x5515ABC8,
       0xC5FEFF52, 0xFFFFAC00, 0x0FE95CB6, 0x79CF43DD, 0xAA48A3FB, 0xE1D71788, 0x97663D3A, 0xF5CFFEA7, 0xEE617632,
       0x4B11A7EE, 0x040EF0B5, 0x0606FC00, 0xC1530FAE, 0x7A827441, 0xFCE91D44, 0x8C4CC1B1, 0x7294C28D, 0x8D976162,
       0x8315435A, 0x3917A408, 0xAF7F1327, 0xD4BFAED7, 0x80D0ABFC, 0x63923DC3, 0xB0E6B35A, 0xB815088F, 0x9BACF123,
       0xE32411C3, 0xA026100B, 0xBCF2FF58, 0x641C5CFC, 0xC4A2D7DC, 0x99E05DCA, 0x9DC699F7, 0xB76A8621, 0x8E40E03C,
       0x28F3C2D4, 0x40F91223, 0x67A952E0, 0x505F3621, 0xBAF13D33, 0xA75B61CC, 0xAB6AEF54, 0xC4DFB60D, 0xD29D873A,
       0x57A77146, 0x393F86B8, 0x2A734A54, 0x31A56AF6, 0x0C5D9160, 0xAF83A19A, 0x7FC9B41F, 0xD079EF47, 0xE3295281,
       0x5602E3E5, 0xAB915E69, 0x225A1992, 0xA387F6B2, 0x7E981613, 0xFC6CF59A, 0xD34A7378, 0xB608B7D6, 0xA9EB93D9,
       0x26DDB218, 0x65F33F5F, 0xF9314442, 0x5D5C0599, 0xEA72E774, 0x1605A502, 0xEC6CBC9F, 0x7F8A1BD1, 0x4DD8CF07,
       0x2E6D79E0, 0x6990418F, 0xCF77BAD9, 0xD4FE0147, 0xFEF4A3E8, 0x85C45BDE, 0xB58F8E67, 0xA63EB8D7, 0xC69BD19B,
       0xDA442DCA, 0x3C0C1743, 0xE6F39D49, 0x33568804, 0x85EB6320, 0xDA223445, 0x36C4A941, 0xA9185589, 0x71B22D67,
       0xF59A2647, 0x3C8B583E, 0xD7717DED, 0xDF05699C, 0x4378367D, 0x1C459339, 0x85133B7F, 0x49800CE2, 0x3666CA0D,
       0xAF7AB504, 0x4FF5B8F1, 0xC23772E3, 0x3544F31E, 0x0F673A57, 0xF40600E1, 0x7E967417, 0x15A26203, 0x5F2E34CE,
       0x70C7921A, 0xD1C190DF, 0x5BB5DA6B, 0x60979C75, 0x4EA758A4, 0x078FE359, 0x1664639C, 0xAE14E73B, 0x2070FF03]
with open('decrypt', 'wb') as fp:
    n = 0
    while n < len(a):
        num = struct.unpack("<I", a[n:n + 4])[0]
        num = num ^ key[(n + n // 0x84) % 0x84]
        d = struct.pack('I', num)
        fp.write(d)
        n = n + 4

После завершения расшифровки я обнаружил, что Il2cppDumper по-прежнему нельзя использовать.,Поместите расшифрованный файл в редактор 010 и обнаружите, что магическое число неверно.,Изменить наAF 1B B1 FAВот и все,Оказывается, он убрал этап проверки магического числа.,Таким образом, вы можете изменить магическое число,Это предотвращает сброс с помощью универсального скрипта frida, упомянутого ранее.

Решение

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

Язык кода:javascript
копировать
public Void .ctor() { }
// RVA: 0x518834 VA: 0xc575a834
private Void Start() { }
// RVA: 0x518838 VA: 0xc575a838
private Void Update() { }
// RVA: 0x51883c VA: 0xc575a83c
public Void Click() { }
// RVA: 0x518a24 VA: 0xc575aa24
private Boolean CheckFlag(String input) { }
// RVA: 0x518b54 VA: 0xc575ab54
public static String AESEncrypt(String text, String password, String iv) { }
// RVA: 0x518ee4 VA: 0xc575aee4
public static String AESDecrypt(String text, Byte[] password, Byte[] iv) { }
// RVA: 0x5191f0 VA: 0xc575b1f0
private static Void .cctor() { }

CheckFlagиз Смещение0x518a24,Пучокlibil2cpp.soпомещатьIDAвнутри Затемв соответствии сGПрыгнул мимоидти,Просмотр функций

Язык кода:javascript
копировать
int __fastcall sub_518A24(int a1, int a2)
{
  int v3; // r0
  int v4; // r4

  if ( !byte_69C825 )
  {
    sub_4B82BC(1279);
    byte_69C825 = 1;
  }
  v3 = dword_698140;
  if ( (*(_BYTE *)(dword_698140 + 178) & 1) != 0 && !*(_DWORD *)(dword_698140 + 96) )
  {
    il2cpp_runtime_class_init_0();
    v3 = dword_698140;
  }
  v4 = sub_518B54(
         *(_DWORD *)(v3 + 80),
         a2,
         *(_DWORD *)(*(_DWORD *)(v3 + 80) + 4000),
         *(_DWORD *)(*(_DWORD *)(v3 + 80) + 2364));
  if ( (*(_BYTE *)(dword_696FB8 + 178) & 1) != 0 && !*(_DWORD *)(dword_696FB8 + 96) )
    il2cpp_runtime_class_init_0();
  return sub_7D644(0, v4, dword_69B7F0, 0);
}

вот этоsub_518B54функция На самом деле этоAESDecrypt,Вы можете использовать сценарий IDA Il2cppDumper для восстановления имени функции.,Я тут просто ленюсь и не восстанавливаю.,Общая логика кода заключается в шифровании входных данных и последующем сравнении их с флагом.,Поэтому мы просто распечатываем ключ и флаг AES и расшифровываем их.

Другие методы

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

  • Zygisk-Il2CppDumper:одинMagiskплагин,Вы можете динамически выгружать имена функций и смещения функций.,Изначально вам необходимо самостоятельно настроить среду разработки Android.,сейчассуществовать Вместо этого автор используетgithub Действие: просто создайте его и введите имя пакета, чтобы скомпилировать и использовать его, что очень удобно.
  • frida-il2cpp-bridge:одинfridaБиблиотека,Очень мощный,Имеет не только функцию динамического дампа имен функций и смещений функций.,Также есть трассировка, перехват и другие функции.,Определенно стоит попробовать.

Reference

unity3d Принципиальный анализ il2cpp и обратный анализ IL2CPP Tutorial: Finding loaders for obfuscated global-metadata.dat files Анализ IL2CPP Unity Baby unity3D Исходный код Il2cpp

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose