首页
社区
课程
招聘
[原创] FindMyDex Crackme 解题记录
发表于: 2020-1-17 19:39 7729

[原创] FindMyDex Crackme 解题记录

2020-1-17 19:39
7729
0x00 前言
FindMyDex.APK 是一道Android CTF题,获取Flag的过程比较有学习价值,固记录一次Crack过程。

0x01  FindMyDex app 运行界面
     
运行界面没有任何组件显示,只是变换界面颜色。

0x02 Java层逻辑分析

apk中没有dex,找到dex应该就可以找到flag

android.app.NativeActivity Android的ndk开发,dex应该是藏在了so中。

Android NativeActivity 原理简单介绍一下,后面的分析需要用到;
1、 NativeActivity是Android进行纯NDK开发使用的,和普通的Activity没有区别;
2、 NativeActivity通过native_app_glu 启动c++线程, 然后创建线程在应用程序的.so动态链接库中寻找函数 __ANativeActivity_onCreate(ANativeActivity, void* size_t),调用 android_main(android_app* pApplication)函数;
3、C++的入口函数就是android_main

0x03 Native层分析

ANativeActivity_onCreate
int __fastcall ANativeActivity_onCreate(_DWORD *a1, int a2, size_t a3)
{
  _DWORD *v3; // r8
  int v4; // r10
  size_t v5; // r9
  int v6; // r0
  __int64 v7; // r2
  char *v8; // r5
  void *v9; // r0
  int *v10; // r0
  char *v11; // r0
  pthread_attr_t attr; // [sp+4h] [bp-30h]

  v3 = a1;
  v4 = a2;
  v5 = a3;
  v6 = *a1;
  *(_DWORD *)(v6 + 20) = sub_2FBC;
  *(_DWORD *)v6 = sub_3016;
  *(_DWORD *)(v6 + 4) = sub_301E;
  *(_DWORD *)(v6 + 8) = sub_3026;
  *(_DWORD *)(v6 + 12) = sub_307A;
  HIDWORD(v7) = sub_30AA;
  *(_DWORD *)(v6 + 16) = sub_3082;
  LODWORD(v7) = sub_309A;
  *(_DWORD *)(v6 + 56) = sub_308A;
  *(_DWORD *)(v6 + 60) = sub_3092;
  *(_QWORD *)(v6 + 24) = v7;
  *(_DWORD *)(v6 + 40) = sub_30B0;
  *(_DWORD *)(v6 + 44) = sub_30B8;
  *(_DWORD *)(v6 + 48) = sub_30BE;
  v8 = (char *)malloc(0x94u);
  _aeabi_memclr4(v8, 148);
  *((_DWORD *)v8 + 3) = v3;
  pthread_mutex_init((pthread_mutex_t *)(v8 + 64), 0);
  pthread_cond_init((pthread_cond_t *)(v8 + 68), 0);
  if ( v4 )
  {
    v9 = malloc(v5);
    *((_DWORD *)v8 + 5) = v9;
    *((_DWORD *)v8 + 6) = v5;
    _aeabi_memcpy(v9, v4, v5);
  }
  if ( pipe(&attr.__align + 6) )
  {
    v10 = (int *)_errno();
    v11 = strerror(*v10);
    _android_log_print(6, "threaded_app", "could not create pipe: %s", v11);
    v8 = 0;
  }
  else
  {
    *((_QWORD *)v8 + 9) = *((_QWORD *)&attr.__align + 3);
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, 1);
    pthread_create((pthread_t *)v8 + 20, &attr, (void *(*)(void *))sub_3218, v8);
    pthread_mutex_lock((pthread_mutex_t *)(v8 + 64));
    while ( !*((_DWORD *)v8 + 27) )
      pthread_cond_wait((pthread_cond_t *)(v8 + 68), (pthread_mutex_t *)(v8 + 64));
    pthread_mutex_unlock((pthread_mutex_t *)(v8 + 64));
  }
  v3[7] = v8;
  return _stack_chk_guard - *(&attr.__align + 8);
}
其中 
pthread_create((pthread_t *)v8 + 20, &attr, (void *(*)(void *))sub_3218, v8);

java_main代码如下
int __fastcall android_main(_DWORD *a1)
{
  _DWORD *v1; // r9
  void *v2; // r4
  Bytef *v3; // r10
  signed int v4; // r2
  signed int v5; // r1
  int v6; // r0
  int v7; // r3
  int v8; // r6
  int v9; // r8
  float v10; // s0
  signed int v11; // r0
  int v12; // r4
  int v13; // r4
  int v14; // r3
  void *v15; // r0
  int v16; // r2
  int v17; // r1
  int v18; // r3
  Bytef *v19; // r3
  int v20; // r8
  Bytef *dest; // [sp+18h] [bp-158h]
  time_t v23; // [sp+1Ch] [bp-154h]
  int v24; // [sp+20h] [bp-150h]
  char v25; // [sp+24h] [bp-14Ch]
  _DWORD *v26; // [sp+28h] [bp-148h]
  int v27; // [sp+2Ch] [bp-144h]
  int v28; // [sp+30h] [bp-140h]
  int v29; // [sp+34h] [bp-13Ch]
  int v30; // [sp+38h] [bp-138h]
  float v31; // [sp+50h] [bp-120h]
  int v32; // [sp+54h] [bp-11Ch]
  int v33; // [sp+58h] [bp-118h]
  uLongf destLen; // [sp+5Ch] [bp-114h]
  char v35; // [sp+60h] [bp-110h]
  int v36; // [sp+68h] [bp-108h]
  float v37; // [sp+80h] [bp-F0h]
  int name; // [sp+C8h] [bp-A8h]
  int v39; // [sp+CCh] [bp-A4h]
  int v40; // [sp+D0h] [bp-A0h]
  int v41; // [sp+D4h] [bp-9Ch]
  int v42; // [sp+D8h] [bp-98h]
  int v43; // [sp+DCh] [bp-94h]
  int v44; // [sp+E0h] [bp-90h]
  int v45; // [sp+E4h] [bp-8Ch]
  int v46; // [sp+E8h] [bp-88h]
  int v47; // [sp+ECh] [bp-84h]
  int v48; // [sp+F0h] [bp-80h]
  __int16 v49; // [sp+F4h] [bp-7Ch]
  char v50; // [sp+F6h] [bp-7Ah]
  int filename; // [sp+F8h] [bp-78h]
  int v52; // [sp+FCh] [bp-74h]
  int v53; // [sp+100h] [bp-70h]
  int v54; // [sp+104h] [bp-6Ch]
  int v55; // [sp+108h] [bp-68h]
  int v56; // [sp+10Ch] [bp-64h]
  int v57; // [sp+110h] [bp-60h]
  int v58; // [sp+114h] [bp-5Ch]
  int v59; // [sp+118h] [bp-58h]
  int v60; // [sp+11Ch] [bp-54h]
  int v61; // [sp+120h] [bp-50h]
  int v62; // [sp+124h] [bp-4Ch]
  int v63; // [sp+128h] [bp-48h]
  char v64; // [sp+12Ch] [bp-44h]
  int v65; // [sp+134h] [bp-3Ch]

  v1 = a1;
  destLen = 0x100000;
  dest = (Bytef *)malloc(0x100000u);
  v2 = off_48BF0;
  v3 = (Bytef *)malloc((size_t)off_48BF0);
  _aeabi_memcpy(v3, &unk_7004, v2);
  filename = -1651995194;
  v52 = -2003974520;
  v53 = -1966700387;
  v54 = -2000190330;
  v55 = -2071422265;
  v56 = -947092071;
  v57 = -1920499569;
  v58 = -1936879484;
  v59 = -2138061167;
  v60 = -962950011;
  v61 = -1702328950;
  v62 = -946172774;
  v63 = -376337267;
  v64 = 0;
  name = -1651995194;
  v39 = -2003974520;
  v40 = -1966700387;
  v41 = -2000190330;
  v42 = -2071422265;
  v43 = -947092071;
  v44 = -1920499569;
  v45 = -1936879484;
  v46 = -2138061167;
  v47 = -962950011;
  v48 = -1853059706;
  v50 = 0;
  v4 = 1;
  v49 = -5690;
  LOBYTE(filename) = 47;
  do
    *((_BYTE *)&filename + v4++) ^= 0xE9u;
  while ( v4 != 53 );
  v5 = 1;
  LOBYTE(name) = 47;
  do
    *((_BYTE *)&name + v5++) ^= 0xE9u;
  while ( v5 != 47 );
  j_app_dummy();
  _aeabi_memclr4(&v27, 48);
  *v1 = &v26;
  v1[1] = sub_2974;
  v1[2] = sub_2B4C;
  v26 = v1;
  v27 = ASensorManager_getInstance();
  v28 = ASensorManager_getDefaultSensor();
  v23 = 0;
  v29 = ASensorManager_createEventQueue(v27, v1[7], 3, 0, 0);
  v6 = v1[5];
  if ( v6 )
  {
    v7 = *(_DWORD *)(v6 + 4);
    v8 = *(_DWORD *)(v6 + 8);
    v31 = *(float *)v6;
    v32 = v7;
    v33 = v8;
  }
  ((void (__fastcall *)(signed int, const char *, const char *))_android_log_print)(
    4,
    "FindMyDex",
    "Can you shake your phone 100 times in 10 seconds?");
  v9 = 0;
  do
  {
    while ( 1 )
    {
      v11 = 0;
      if ( !v30 )
        v11 = -1;
      v12 = ALooper_pollAll(v11, 0, &v25, &v24);
      if ( v12 >= 0 )
        break;
      if ( v30 )
      {
        v10 = v31 + 0.01;
        if ( (float)(v31 + 0.01) > 1.0 )
          v10 = 0.0;
        v31 = v10;
        sub_2BD0(&v26);
      }
    }
    if ( v24 )
      (*(void (__fastcall **)(_DWORD *))(v24 + 8))(v1);
    if ( v12 == 3 && v28 )
    {
      while ( 1 )
      {
        do
        {
          if ( ASensorEventQueue_getEvents(v29, &v35, 1) < 1 )
            goto LABEL_51;
        }
        while ( v36 != 1 );
        if ( v9 & 1 )
        {
          if ( v37 >= -15.0 )
          {
LABEL_30:
            v13 = v9;
            goto LABEL_31;
          }
          if ( v9 == 1 )
            v23 = time(0);
          v13 = v9 + 1;
        }
        else
        {
          if ( v37 <= 15.0 )
            goto LABEL_30;
          v13 = v9 + 1;
          if ( v9 >= 0 )
            _android_log_print(4, "FindMyDex", "Oh yeah~ You Got it~ %d times to go~");
        }
LABEL_31:
        v9 = v13;
        if ( (unsigned int)(v13 - 1) <= 0x58 )
        {
          v9 = v13;
          v14 = v13 / 10;
          if ( v13 % 10 == 9 )
          {
            v15 = off_48BF0;
            v16 = (signed int)off_48BF0 / 10;
            v17 = (v14 + 1) * ((signed int)off_48BF0 / 10);
            v18 = v14 * ((signed int)off_48BF0 / 10);
            if ( v18 < v17 )
            {
              v19 = &v3[v18];
              do
              {
                --v16;
                *v19++ ^= v13;
              }
              while ( v16 );
            }
            if ( v13 == 89 )
            {
              while ( v17 < (signed int)v15 )
                v3[v17++] ^= 0x59u;
            }
            v9 = v13 + 1;
          }
        }
        if ( v13 == 100 )
        {
          if ( (signed int)(time(0) - v23) > 9 )
          {
            _android_log_print(4, "FindMyDex", "OH~ You are too slow. Please try again");
            _aeabi_memcpy(v3, &unk_7004, off_48BF0);
            v9 = 0;
          }
          else
          {
            if ( uncompress(dest, &destLen, v3, (uLong)off_48BF0) )
              _android_log_print(5, "FindMyDex", "Dangerous operation detected.");
            v20 = open((const char *)&filename, 577, 511);
            if ( !v20 )
              _android_log_print(5, "FindMyDex", "Something wrong with the permission.");
            write(v20, dest, destLen);
            close(v20);
            free(dest);
            free(v3);
            if ( access((const char *)&name, 0) && mkdir((const char *)&name, 0x1FFu) )
              _android_log_print(5, "FindMyDex", "Something wrong with the permission..");
            sub_22F8(v1);
            remove((const char *)&filename);
            _android_log_print(4, "FindMyDex", "Congratulations!! You made it!");
            sub_21E0(v1);
            v9 = 2147483648;
          }
        }
      }
    }
LABEL_51:
    ;
  }
  while ( !v1[15] );
  sub_2B96(&v26);
  return _stack_chk_guard - v65;
}
代码整体逆向下来,思路如下
1、读取字节流,通过解密后进行解压缩获得dex;
2、手机在1s中摇100次,会在/data/data/com.a.sample.findmydex/files 目录下生成 class.dex和odex,然后remove掉;




加密的dex以字节流的方式写在.data中,起始位置是 0x7004 长度是 0x41BE8
用ida py脚本进行dump
import idaapi
start_address=0x7004
data_length=0x41BE8
data=idaapi.get_bytes(start_address, data_length)
fp = open('dump', 'wb')
fp.write(data)
fp.close()

解密算法

用Java实现一下解密dex
        try {
            FileInputStream fis =  new FileInputStream("xx://dump");
            int length = 0x41BE8;
            byte[] b = new byte[length];
            int tempchar;
            int index = 0;
            while((tempchar  = fis.read()) != -1){
                b[index] = (byte) tempchar;
                index++;
            }
            int v15,v16,v17,v18,v19;
            for(int v14=0;v14<90;v14++){
                v15 = v14 / 10;
                if(v14 % 10 == 9){
                    v16 = length;
                    v17 = length / 10;
                    v18 = (v15 + 1) * (length / 10);
                    if(length / 10 * v15 < v18){
                        v19 = v17 * v15;
                        do{
                            --v17;
                            b[v19++] ^= v14;
                        }
                        while (v17>0);
                    }
                    if(v14 == 89){
                        while (v18 < v16){
                            b[v18++] ^= 89;
                        }
                    }
                }
            }

            fis.close();

            FileOutputStream fos = new FileOutputStream("xx://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);
            byte[] buf = new byte[1024];
            int num = -1;
            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;
    }
获得的dex

拿到dex就可以开始搞一下flag了

由此可见是 明文+key = 密文 

由此可以看到 应该是使用twofish算法
对m进行一下转义
new String(Base64.getEncoder().encode(m)

然后找个工具验证一下

到此获得成获得flag

0x04 总结
这道题主要是考察了Anddroid NativeActivity的理解,记录一下过程,大佬勿喷。
附件上传了apk和解密后的dex,有兴趣可以尝试一下crack。

最后祝大家小年快乐!



[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 2
支持
分享
最新回复 (6)
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
刚接触这方面的知识 问下大佬可以给dvmdexfileopen下断点吗
最后于 2020-1-30 10:39 被iris111编辑 ,原因:
2020-1-30 10:39
0
雪    币: 10944
活跃值: (7329)
能力值: ( LV12,RANK:219 )
在线值:
发帖
回帖
粉丝
3
iris111 刚接触这方面的知识&nbsp;问下大佬可以给dvmdexfileopen下断点吗
dvmDexFileOpenPartial? 可以吧
2020-1-30 13:32
0
雪    币: 403
活跃值: (798)
能力值: ( LV4,RANK:58 )
在线值:
发帖
回帖
粉丝
4
大佬太厉害了,这么详细怎么没人看呢
2020-2-10 11:35
0
雪    币: 10944
活跃值: (7329)
能力值: ( LV12,RANK:219 )
在线值:
发帖
回帖
粉丝
5
xjklewh [em_63]大佬太厉害了,这么详细怎么没人看呢[em_4]
弟弟而已 
2020-2-10 11:53
0
雪    币: 409
活跃值: (257)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
请问解密dex的Java代码中ZipInputStream是什么类
2020-8-19 16:53
0
雪    币: 10944
活跃值: (7329)
能力值: ( LV12,RANK:219 )
在线值:
发帖
回帖
粉丝
7
sud0 请问解密dex的Java代码中ZipInputStream是什么类
jzlib 
2020-8-19 17:47
0
游客
登录 | 注册 方可回帖
返回
//