TP 2021 Android Final  
修复so 和预赛一样,决赛的 .so 文件的 .text,.rodata 段被加密。由于 dd 指令 dump 内存太慢,决赛我选择 gameguardian(GameGuardian APK Download ) Dump内存,dump的内存地址从 /proc/xxxx/maps 读取。
1 2 3 4 5 6 7 8 9 10 11 12 u0_a285       15423    777  1363396  110672  ep_poll             0  S com.personal.flappybird1 |walleye:/ # cat /proc/15423 /maps | grep 2 cppc02fb000 -c06 db000  r--s 00000000  103 :1 d 2917159                            /data/media/0 /Android/data/com.personal.flappybird/files/il2 cpp/Metadata/global-metadata.datc16cb000 -c1 dd1000  r-xp 00000000  103 :1 d 2310173                            /data/app/com.personal.flappybird-Ro_Mv8 KtyMyikogWcojKtw==/lib/arm/libil2 cpp.soc1dd2000 -c1 e03000  r--p 00706000  103 :1 d 2310173                            /data/app/com.personal.flappybird-Ro_Mv8 KtyMyikogWcojKtw==/lib/arm/libil2 cpp.soc1e03000 -c1 e22000  rw-p 00737000  103 :1 d 2310173                            /data/app/com.personal.flappybird-Ro_Mv8 KtyMyikogWcojKtw==/lib/arm/libil2 cpp.soc1e53000 -c1 e54000  rw-p 00755000  103 :1 d 2310173                            /data/app/com.personal.flappybird-Ro_Mv8 KtyMyikogWcojKtw==/lib/arm/libil2 cpp.soc1e54000 -c1 e5 a000  r-xp 00755000  103 :1 d 2310173                            /data/app/com.personal.flappybird-Ro_Mv8 KtyMyikogWcojKtw==/lib/arm/libil2 cpp.soc1e5a000 -c1 e5 c000  rw-p 0075 a000  103 :1 d 2310173                            /data/app/com.personal.flappybird-Ro_Mv8 KtyMyikogWcojKtw==/lib/arm/libil2 cpp.sod3dda000 -d3 e2 d000  r--s 00000000  103 :1 d 2917157                            /data/media/0 /Android/data/com.personal.flappybird/files/il2 cpp/Resources/mscorlib.dll-resources.dat
 
1 2 3 4 5 6 7 walleye :/ # cat /proc/15423 /maps | grep unityc335a000 -c3 e14000  r-xp 00000000  103 :1 d 2310168                            /data/app/com.personal.flappybird-Ro_Mv8 KtyMyikogWcojKtw==/lib/arm/libunity.soc3e15000 -c3 e2 f000  r--p 00 aba000  103 :1 d 2310168                            /data/app/com.personal.flappybird-Ro_Mv8 KtyMyikogWcojKtw==/lib/arm/libunity.soc3e2f000 -c3 e41000  rw-p 00 ad4000  103 :1 d 2310168                            /data/app/com.personal.flappybird-Ro_Mv8 KtyMyikogWcojKtw==/lib/arm/libunity.soc3ef5000 -c3 ef7000  rw-p 00 ae5000  103 :1 d 2310168                            /data/app/com.personal.flappybird-Ro_Mv8 KtyMyikogWcojKtw==/lib/arm/libunity.soc3ef7000 -c3 efc000  r-xp 00 ae6000  103 :1 d 2310168                            /data/app/com.personal.flappybird-Ro_Mv8 KtyMyikogWcojKtw==/lib/arm/libunity.soc3efc000 -c3 efe000  rw-p 00 aea000  103 :1 d 2310168                            /data/app/com.personal.flappybird-Ro_Mv8 KtyMyikogWcojKtw==/lib/arm/libunity.so
 
dump后,使用 010 editor 将 dump下的数据覆盖 两个 .so 的 .text 和 .rodata。
观察资源文件,发现 global-metadata.dat 被加密。分析il2cpp.so,发现加载global-metadata.dat的函数被hook到sec2021.so。
上图是还原后的加载函数。
为了获取 global-metadata.dat 文件,我们可以选择从全局变量 s_GlobalMetadata 找到内存中的 global-metadata.datdump下来。
记下 s_GlobalMetadata 在il2cpp.so的偏移,通过 /proc/xxxx/maps 获取il2cpp.so的基址,加上偏移后,用GG修改器dump下来。
dump后,发现dat文件与正常的dat文件差距较大。分析 il2cpp::vm::MetadataCache::Initialize函数,发现 dat 文件的字符串被加密了,写个CPP解密一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 char  g_pDecoutput[20000000 ];extern  unsigned  char  hexData[];int  __fastcall GetStringFromIndex (int  idx)  {     char  *pOutBase=g_pDecoutput;      int  pRet;      int  *v3;      char  *data=(char *)hexData;      printf ("%d:" ,idx);     if  ( !*(char  *)(g_pDecoutput + idx) )     {         if  ( 222636  > idx )              {             do              {                 if  ( !data[idx] )                     break ;                 pOutBase[idx] = idx & 0x7F  ^ (data[idx] - 0x7F ) ^ 0x5C ;                 printf ("%c" ,pOutBase[idx]);                 ++idx;             }             while  ( idx < 222636  );                            }     }     puts ("" );     return  idx++; }
 
解密后,可以看到字符串,下面是末尾的一部分字符串。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 222120 :medalSparkle 222133 :flappy 222140 :ready 222146 :newBool 222154 :gameScore 222164 :<GameOver>c__Iterator0 222187 :<i>__1 222194 : $locvar0 222203 : $locvar1 222212 :<StartGame>c__Iterator1 222236 :MainMenu 222245 :ChangeScene 222257 :RateGame 222266 :<StartGame>c__Iterator0 222290 :ObstacleBehaviour 222308 :moveSpeed 222318 :ObstacleSpawner 222334 :OnTriggerEnter2D 222351 :obstaclePrefabs 222367 :tempTime 222376 :PlayerController 222393 :OnCollisionEnter2D 222412 :KillPlayer 222423 :thrust 222430 :minTiltSmooth 222444 :maxTiltSmooth 222458 :hoverDistance 222472 :hoverSpeed 222483 :tiltSmooth 222494 :playerRigid 222506 :downRotation 222519 :upRotation 222530 :Scrolling 222540 :scrollSpeed 222552 :isMainMenu 222563 :quadRenderer 222576 :SoundManager 222589 :tempName 222598 :PlayTheAudio 222611 :swoosh 222618 :flap 222623 :playAudio 
 
尝试使用il2cppdumper解析,但报错了。
1 2 3 4 5 6 7 8 9 Il2CppDumper.exe D:\C TF\M ATCH\T P2021\F inal\A ndroid\F lappyBird\l ib\a rmeabi-v7a\l ibil2cpp.so D:\C TF\M ATCH\T P2021\F inal\A ndroid\F lappyBird\a ssets\b in\D ata\M anaged\M etadata\g lobal-metadata.dat .\ Initializing metadata... System.ArgumentException: An item with the same key has already been added. Key: 119    at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)    at System.Linq.Enumerable.ToDictionary[TSource,TKey](TSource[] source, Func`2 keySelector, IEqualityComparer`1 comparer)    at Il2CppDumper.Metadata..ctor(Stream stream) in C:\p rojects\i l2cppdumper\I l2CppDumper\I l2Cpp\M etadata.cs:line 78    at Il2CppDumper.Program.Init(String il2cppPath, String metadataPath, Metadata& metadata, Il2Cpp& il2Cpp) in C:\p rojects\i l2cppdumper\I l2CppDumper\P rogram.cs:line 126    at Il2CppDumper.Program.Main(String[] args) in C:\p rojects\i l2cppdumper\I l2CppDumper\P rogram.cs:line 100 Press any key to exit...
 
模拟sec2021.so sec2021 的功能与初赛大致相似,都导出了2个数组:
分析p_array中的函数
第0个函数大概与保护相关,在fread,fwrite等函数都有调用。
h_open_meatdata 函数实现加载 metadata。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void  __fastcall dec_Metadata (int  a1, int  a2, int  a3, int  a4, int  a5, int  a6, int  a7, int  a8, __int64 a9, __int64 a10, int  a11)  {   int  v11;    int  v12;    char  v13;    int  v14;    int  v15;    int  sem;    int  v17;    a8 = 0xEECF7326 ;                                 ((void   (__fastcall *)(int  *, int , int , int ))((char  *)off_4C704 + v14))(&a8, v11, -1 , v12);   a9 = 0LL ;   a10 = 0LL ;   LOBYTE (a11) = v13;   v15 = ((int   (*)(void ))((char  *)off_4C708 + v14))();   sem = ((int   (__fastcall *)(int , __int64 *))((char  *)0x340d8  + v14))(v15, &a9);   a8 = 0xEECF7326 ;   ((void   (__fastcall *)(int  *, int , int , int ))((char  *)0x003202c  + v14))(&a8, v11, g_metadata_size, sem);   a8 = 0xEECF7326 ;   ((void   (__fastcall *)(int  *, int ))((char  *)off_4C714 + v14))(&a8, 3539 );   v17 = ((int   (__fastcall *)(int  *))((char  *)off_4C718 + v14))(&dword_4EDA1);   hooked_il2cpp_func[0 ] = (int   (*)(void ))(v17 + 0x553034 );   hooked_il2cpp_func[1 ] = (int   (*)(void ))(v17 + 0x58DDE4 );   hooked_il2cpp_func[2 ] = (int   (*)(void ))(v17 + 0x55311C );   __asm { POPEQ           {R4-R11,PC}; Pop registers } }
 
和初赛一样,我选择模拟 sec2021.so。将模拟的 sec2021.so、解密后的 unity.so,il2cpp.so、解密后的 global-metadata.dat覆盖原文件打包后,发现程序crash。
1 2 3 4 5 6 7 8 09 -12  10 :19 :23 .802  10761  15384  E CRASH   :      #00   pc 0058 dde4   /data/app/com.personal.flappybird-JK0 jL3 qOSXAgeknqh2 UNyA==/lib/arm/libil2 cpp.so09 -12  10 :19 :23 .802  10761  15384  E CRASH   :      #01   pc 002 a6480   /data/app/com.personal.flappybird-JK0 jL3 qOSXAgeknqh2 UNyA==/lib/arm/libunity.so09 -12  10 :19 :23 .802  10761  15384  E CRASH   :      #02   pc 002 a63 a4   /data/app/com.personal.flappybird-JK0 jL3 qOSXAgeknqh2 UNyA==/lib/arm/libunity.so09 -12  10 :19 :23 .802  10761  15384  E CRASH   :      #03   pc 002 a16 b4   /data/app/com.personal.flappybird-JK0 jL3 qOSXAgeknqh2 UNyA==/lib/arm/libunity.so09 -12  10 :19 :23 .802  10761  15384  E CRASH   :      #04   pc 004 cd144   /data/app/com.personal.flappybird-JK0 jL3 qOSXAgeknqh2 UNyA==/lib/arm/libunity.so09 -12  10 :19 :23 .802  10761  15384  E CRASH   :      #05   pc 004 cce40   /data/app/com.personal.flappybird-JK0 jL3 qOSXAgeknqh2 UNyA==/lib/arm/libunity.so0 
 
分析crash部分,发现是 il2cpp_method_get_name 发生了异常。
这个导出函数显得很奇怪,相当于是 R0=*(0+12)。结合 p_array 中有 hook_dlsym,怀疑是sec2021.so 中的 dlsym 将这个函数解析到真正的地址,而 il2cpp.so 中的原导出地址是假的。
分析 hook_dlsym,发现很多 BX 指令,干扰了IDA的分析。将BXPatch成NOP,勉强分析一下。
第一个函数是根据index获取字符串,有很多BX BLX之类的混淆指令,patch成Nop后,勉强能看,但循环、条件判断之类的都无法看出了,还好通过查找SBox的交叉引用,看到其他函数的未经混淆的解密函数,写CPP解密一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 int  __fastcall GetString_31A60 (int  *a1, int  ID)  {   int  v2;    unsigned  __int8 size;    char  *pBuf;    int  v5;    int  *v6;    _DWORD *v7;    char  cur_xor;    _DWORD v10[3 ];    char  buf4[4 ];    int  ID_1;    int  a1a;    v2 = *a1;   ID_1 = ID;   Set0 (buf4);   sub_30D78 ((int )v10, (int )&dword_4DFC4, (int )&ID_1);   sub_30DA0 ((int )buf4, v10);   Set0to0_0 ((int )v10, (int )&dword_4DFC4);   sub_30DD4 (buf4, v10);   cur_xor = SBox[ID_1];   size = SBox[ID_1 + 1 ] ^ cur_xor;   pBuf = (char  *)malloc (size + 1 );   v10[2 ] = pBuf;   a1a = v2;   clear_mem ((int )&a1a, (int )pBuf, -1 );   a1a = v2;   copy ((int )&a1a, (int )pBuf, -1 , &SBox[ID_1 + 2 ], size);   a1a = v2;   do_xor ((int )&a1a, (int )pBuf, size, cur_xor);   a1a = v2;   v5 = getCheckSum (&a1a, pBuf);   v6 = (_DWORD *)&byte_4;   v7 = (_DWORD *)(unsigned  __int8)SBox[size + 2  + ID_1];   if  ( (_DWORD *)v5 == v7 )     v6 = &dword_14;   sub_31054 (0 , v7, v6);   sub_31038 (&dword_4DFC4, 0 );   return  v10[2 ]; }
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 int  getxor (int  id)  {     int  checksum=0xff ;          int  _0=sbox[id];     int  _1 = sbox[id+1 ];     int  cur_size=_0 ^ _1;     unsigned  char  key[9 ];     key[0 ]=_0;     for  (int  i = 1 ; i < 9 ; ++i) {         key[i]= ((unsigned  __int8)(key[i-1 ] + 17 ) >> 5 ) | (8  * (key[i-1 ] + 17 ));              }     puts ("" );     for  (int  i = 0 ; i < cur_size; ++i) {         printf ("%c" ,key[i%9 ]^sbox[id+2 +i]);     }     puts ("" );     return  0 ; }int  main ()   {     getxor (264 );     getxor (297 );     getxor (327 );     getxor (363 );     getxor (390 ); }
 
第二个是strcmp,再结合汇编分析,
不难想到,hook_dlsym 将这几个函数解析到真正的地址。
1 2 3 4 5 il2cpp_method_get_name il2cpp_class_get_name il2cpp_class_get_methods  (0 x553034 )il2cpp_image_get_class  (0 x58 DDE4 )il2cpp_class_get_method_from_name  (0 x55311 C)
 
其中,前两个函数是在sec2021.so中实现,且只有2个指令。
没有必要模拟,直接在 il2cpp.so中的对应位置 patch 成正确的指令即可。
分析后三条指令:
1 2 3 4 5 6 7 8 9 10 11 12 int  h_il2cpp_class_get_methods ()  {   return  hooked_il2cpp_func[0 ](); }int  h_il2cpp_image_get_class ()  {   return  hooked_il2cpp_func[1 ](); }int  h_il2cpp_class_get_method_from_name ()  {   return  hooked_il2cpp_func[2 ](); }
 
找 hooked_il2cpp_func 的引用,发现在加载 metadata 函数被赋值。
不难猜到,v17 和后面的数字分别是il2cpp的基址和真正函数的偏移。
将后三个函数的导出地址的第一条指令,patch成 B 0xXXXXXX,即可让这三个函数跳转到真正函数地址。
实现无敌破解  global-metadata.dat 的格式与常规的差很多,无法使用工具解析。
在GitHub上搜关键字 gameScoreText flappy,可以找到这个游戏的源代码。
 
找到判断障碍物的逻辑
下载后,编译成apk,使用 il2cppdumper 分析,可以定位到关键逻辑函数,方便对比分析。
其中,UnityEngine_Component__CompareTag函数存在特征字符串”UnityEngine.GameObject::CompareTag(System.String)”
在题目的so中找到特征字符串、查找引用,定位到题目对应函数。
为了实现无敌,将 BNE patch 成 B 即可。