HNHuangJingYU 发表于 2021-9-29 21:44

《攻防世界》MOBILE--easy-dex

本帖最后由 HNHuangJingYU 于 2021-9-29 21:48 编辑

1. 嗯,拿到easy-dex.apk拖入jeb分析好家伙直接没有dex文件,嗯明白了,小型的脱壳题目,果断进入libnative.so,逆向前不知道什么是NativeActivity通过这里进行了简单了解下:https://medium.com/androiddevelopers/getting-started-with-c-and-android-native-activities-2213b402ffff,了解到了两个重要函数ANativeActivity_onCreate、android_main大概就是页面的入口函数,通过ida分析主要的核心代码在android_main里面
int __fastcall android_main(_DWORD *application)
{

application_1 = application;
destLen = 0x100000;
dest = malloc(0x100000u);
v2 = dword_43A18;
v3 = malloc(dword_43A18);
_aeabi_memcpy(v3, &loc_7004, v2);
filename = -1651995194;

LOBYTE(filename) = 47;                        ////将47赋值给filename中检索低位字节
do
    *(&filename + v4++) ^= 0xE9u;               // //给filename填充数据
while ( v4 != 53 );                           ////长度为52
v5 = 1;
LOBYTE(name) = 47;                            // //将47赋值给name中检索低位字节。
do
    *(&name + v5++) ^= 0xE9u;
while ( v5 != 47 );                           // //长度47
j_app_dummy();
_aeabi_memclr8(&application_2, 52);
*application_1 = &application_2;            ////取application_2地址赋值给application_1
application_1 = sub_29B8;
application_1 = sub_2B90;                  // func2
application_2 = application_1;
v27 = ASensorManager_getInstance();         ////application_2 取得 application_1 地址
v28 = ASensorManager_getDefaultSensor();      // //管理传感器实例对象
v6 = 0;
v29 = ASensorManager_createEventQueue(v27, application_1, 3, 0, 0);
v7 = application_1;
if ( v7 )                                     // application_1是否存在
{
    v8 = *(v7 + 4);
    v9 = *(v7 + 8);
    v31 = *v7;
    v32 = v8;
    v33 = v9;
}
_android_log_print(4, "FindMyDex", "Can you shake your phone 100 times in 10 seconds?");
v10 = 0;
do
{
    while ( 1 )
    {
      v12 = 0;
      if ( !v30 )
      v12 = -1;
      v13 = ALooper_pollAll(v12, 0, &v25, &v24);// 执行所有挂起的回调
      if ( v13 >= 0 )
      break;
      if ( v30 )
      {
      v11 = v31 + 0.01;
      if ( (v31 + 0.01) > 1.0 )
          v11 = 0.0;
      v31 = v11;
      sub_2C14(&application_2);
      }
    }
    if ( v24 )
      (*(v24 + 8))(application_1);
    if ( v13 == 3 && v28 )
    {
      while ( 1 )
      {
      do
      {
          if ( ASensorEventQueue_getEvents(v29, &v35, 1) < 1 )
            goto LABEL_51;
      }
      while ( v36 != 1 );
      if ( v10 & 1 )
      {
          if ( v37 >= -15.0 )
          {
LABEL_30:
            v14 = v10;
            goto LABEL_31;
          }
          if ( v10 == 1 )
            v6 = time(0);
          v14 = v10 + 1;
      }
      else
      {
          if ( v37 <= 15.0 )
            goto LABEL_30;
          v14 = v10 + 1;
          if ( v10 >= 0 )
            _android_log_print(4, "FindMyDex", "Oh yeah~ You Got it~ %d times to go~", 99 - v10);
      }
LABEL_31:
      v10 = v14;
      if ( (v14 - 1) <= 0x58 )                // 解密文件
      {
          v10 = v14;
          v15 = v14 / 10;
          if ( v14 % 10 == 9 )
          {
            v16 = dword_43A18;
            v17 = dword_43A18 / 10;
            v18 = (v15 + 1) * (dword_43A18 / 10);
            if ( dword_43A18 / 10 * v15 < v18 )
            {
            v19 = &v3;
            do
            {
                --v17;
                *v19++ ^= v14;
            }
            while ( v17 );
            }
            if ( v14 == 89 )
            {
            while ( v18 < v16 )
                v3 ^= 0x59u;
            }
            v10 = v14 + 1;
          }
      }
      if ( v14 == 100 )
      {
          if ( (time(0) - v6) > 9 )
          {
            _android_log_print(4, "FindMyDex", "OH~ You are too slow. Please try again");
            _aeabi_memcpy(v3, &loc_7004, dword_43A18);
            v10 = 0;
          }
          else
          {
            v20 = v6;
            if ( uncompress(dest, &destLen, v3, dword_43A18) )// 解压缩文件
            _android_log_print(5, "FindMyDex", "Dangerous operation detected.");
            v21 = open(&filename, 577, 511);
            if ( !v21 )
            _android_log_print(5, "FindMyDex", "Something wrong with the permission.");
            write(v21, dest, destLen);          // 写入数据
            close(v21);
            free(dest);
            free(v3);
            if ( access(&name, 0) && mkdir(&name, 0x1FFu) )
            _android_log_print(5, "FindMyDex", "Something wrong with the permission..");
            sub_2368(application_1);
            remove(&filename);                  // 删除文件
            _android_log_print(4, "FindMyDex", "Congratulations!! You made it!");
            sub_2250(application_1);
            v10 = 2147483648;
            v6 = v20;
          }
      }
      }
    }
LABEL_51:
    ;
}
while ( !application_1 );
sub_2BDA(&application_2);
return _stack_chk_guard - v65;
}2. 主要分为创建文件对传感器进行实例化和监听、对数据进行dex解密、解压缩、写数据、删除文件。
3. 分析解密文件处可知都是对dword_43A18数据段进行操作,所以先将这个段dump下来试试看,查看ida view得知dword_43A18位于.data里面,如图:

4. 通过0x43A18 - 0x3CA10 = 0x7008 定位到0x7008处,那么数据段就是从0x7004开始的那么就得到了dump需要的条件,那么运行脚本:
static main()
{
auto i,fp;
fp = fopen("d:\\dump","wb");
auto start = 0x7004;
auto size = 0x3CA10;
for(i=start;i<start+size;i++)
{
fputc(Byte(i),fp);
}
fp.close();
}5. 在D盘找到demp文件,那么现在再进行解密。解密操作核心部分:

6. Java复现脚本(到了这里实在是卡住了第一次接触这种题,搜了下大佬思路,引用了大神的脚本,并对脚本进行了难理解的注释,算是做一丢丢贡献把,我也是第一次接触CTF,就当作一次学习的旅程吧)这里加了一个jar文件、注意下如图:

@Test
public void demo() {
    try {
      FileInputStream fis = new FileInputStream("D://dump");
      int length = 0x3ca10;   //文件大小
      byte[] b = new byte;
      int tempchar;
      int index = 0;
      while ((tempchar = fis.read()) != -1) {   //读取每个字节的数据存入 byte[] b
            b = (byte) tempchar;
            index++;
      }
      int v15, v16, v17, v18, v19;
      for (int v14 = 0; v14 < 90; v14++) {      //这里是对应着C++中的 LABEL_31 那块部分解密逻辑
            v15 = v14 / 10;                        //因为LABEL_31处于一个while ( 1 )的大逻辑死循环里面
            if (v14 % 10 == 9) {                     //当v37满足-15<=v37<=15的时候 则v14 = v10 +1(V10的初始值为0)
                v16 = length;                        //所以就可以得出这个for循环
                v17 = length / 10;
                v18 = (v15 + 1) * (length / 10);
                if (length / 10 * v15 < v18) {
                  v19 = v17 * v15;      //这里进行了变换 原句:v19 = &v3;
                  do {                  //因为v3是一个占内存控件大小为0x3ca10的空数据地址 这里v19则是地址
                        --v17;
                        b ^= v14;    //原句 : *v19++ ^= v14;可理解为: *v3取这块内存地址下的数据
                  }
                  while (v17 > 0);
                }
                if (v14 == 89) {
                  while (v18 < v16) {         //到达这里解密算法就结束了
                        b ^= 89;
                  }
                }
            }
      }

      fis.close();

      FileOutputStream fos = new FileOutputStream("D://class.dex");   //进行文件写出
      for (byte ch : unjzlib(b)) {
            fos.write(ch);
      }
      fos.flush();
      fos.close();

    } catch (IOException e) {
      e.printStackTrace();
    }

}



public static byte[] unjzlib(byte[] object) {
    byte[] data = null;
    try {
      ByteArrayInputStream in = new ByteArrayInputStream(object);
      ZInputStream zIn = new ZInputStream(in); // qwb{TH3y_lo<e_EACh_OTH3r_FOrEUER}
      byte[] buf = new byte;
      int num;
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      while ((num = zIn.read(buf, 0, buf.length)) != -1) {
            baos.write(buf, 0, num);
      }
      data = baos.toByteArray();
      baos.flush();
      baos.close();
      zIn.close();
      in.close();

    } catch (IOException e) {
      e.printStackTrace();
    }
    return data;
}7. 运行后就得到了完整的dex文件了路径在你写出文件的目录下,用jeb打开dex文件(jadx不行)一看b类好家伙这种算法逆向直接就给了我一大重击拳,求助大神的帖子后发现是一个第三方算法。先将MainActivity中的m转换为Base64因为twofish算法解密后是Base64。8.转换为Base64结果:iE3y2hEF1izgbVUfGKWQrUCtgFQFop7iEkbmRwWdwsZ1HdQGcPxRVAkWzV/eDC9N,去到网页在线twofish解密得到flag:qwb{TH3y_Io<e_EACh_OTh3r_FOrEUER}
页: [1]
查看完整版本: 《攻防世界》MOBILE--easy-dex