2017年5月11日星期四

ANDROID 逆向实例(八)- 乐固加固脱壳(2017.01)

虽然乐固做了一些反调试的东西,不过感觉比 Ali 更好分析一点,很多情况并不是反调试越厉害加固就越好。感觉 Ali 在加固方面做的比乐固有意思,Ali 利用 SP 来储存返回地址和参数所以整个流程很混乱,基本看上去就是在各种 JUMP,而乐固仍然是常规规的函数调用和返回方式,流程清晰很多。

另外想说一下,之前把对 Ali 壳的分析发到看雪了,可能因为我只把大概的流程放上去了所以有评论觉得这是一个很没技术的秒脱壳子,至于究竟这个壳子怎么样,好不好脱,完全没有自己尝试过。个人感觉 Ali 的壳子对于初学者来说有很深的学习意义,有不少有意思的细节,建议分析。

* 为了阅读这篇文章时更好理解文中所说内容,下面大多形如 [sub_] [unk_xxx] [loc_xxx] 的函数都经过了重命名以帮助理解。

下面言归正传,下图是反编译 apk 后的 smali 差异,另外还新增了一个 lib 文件夹。
lib 文件夹的格式如下所示。

bugly 相关的可以不用管了,是腾讯的一个类似上报 bug 之类的玩意和加固没有关系。smali 层面只做了一些简单的混淆,不做过多分析了(其实是时间太久忘了 ....),直接进入 Native 层。

直接动态 IDA 挂上,然后在 Linker 和 libdvm.so 对应调用 .init / .init_array / JNI_Onload 的偏移地址下好断点,这样下断点的好处是可以无视 .so 的 ELF 头被修改的情况直达想要的位置,也上个图简单示意一下。

触发断点之后说明运行的到了 .init / .init_array / JNI_Onload 的出发点,然后 F7 即可进入对应 .so ELF 内执行的代码位置。

.init Segment

根据上面说的来到 .init 在 .so 里的位置,偏移量是 [+ 14D4],IDA 静态 + F5 之后得到伪代码,下面是完善函数名之后的伪代码。

int func_init()
{
  unsigned int v0; // ST44_4@11
  unsigned int v1; // ST40_4@11
  unsigned __int64 v2; // kr00_8@13
  _BYTE v3; // ST27_1@15
  _DWORD j; // [sp+14h] [bp-44h]@13
  int v6; // [sp+18h] [bp-40h]@13
  _DWORD v7; // [sp+1Ch] [bp-3Ch]@13
  char v8; // [sp+2Eh] [bp-2Ah]@13
  char v9; // [sp+2Fh] [bp-29h]@13
  char v10; // [sp+30h] [bp-28h]@13
  char v11; // [sp+33h] [bp-25h]@13
  unsigned int v12; // [sp+34h] [bp-24h]@4
  int v13; // [sp+38h] [bp-20h]@4
  unsigned int v14; // [sp+48h] [bp-10h]@4
  unsigned int i; // [sp+4Ch] [bp-Ch]@1

  for ( i = (unsigned int)func_init & 0xFFFFF000; *(_DWORD *)i != 1179403647; i -= 4096 )
    ;
  v14 = 0;
  v13 = i + *(_DWORD *)(i + 28);
  v12 = 0;
  while ( v12 < *(_WORD *)(i + 44) )
  {
    if ( *(_DWORD *)v13 != 1 || *(_DWORD *)(v13 + 24) != 5 )
    {
      if ( *(_DWORD *)v13 == 1 && *(_DWORD *)(v13 + 24) == 6 )
      {
        v0 = *(_DWORD *)(v13 + 8) & 0xFFFFF000;
        v1 = (*(_DWORD *)(v13 + 8) + *(_DWORD *)(v13 + 16) + 4095) & 0xFFFFF000;
        break;
      }
    }
    else
    {
      v14 = (*(_DWORD *)(v13 + 8) + *(_DWORD *)(v13 + 16) + 4095) & 0xFFFFF000;
    }
    ++v12;
    v13 += 32;
  }
  v11 = 43;
  v10 = -103;
  v9 = 32;
  v8 = 21;
  v2 = (unsigned __int64)word_A010[0] << 16;
  v7 = LOWORD(word_A010[0]);
  v6 = LOWORD(word_A010[0]) - (word_A010[0] >> 16);
  mmprotect(
    (void *)(i + HIDWORD(v2)),
    (void *)((v6 + 4095) & 0xFFFFF000),
    &bad_offset_80003911[2],
    (void *)(i + HIDWORD(v2)));
  for ( j = HIDWORD(v2); j <= v7; ++j )
  {
    v3 = *(_BYTE *)(i + j);
    *(_BYTE *)(i + j) ^= (unsigned __int8)((j ^ (v10 - v9)) + v8) ^ v11;
    *(_BYTE *)(i + j) += v10 ^ v9 & v8;
    v11 += v3 & (v10 + v9 - v8) & j;
    v10 += v3 ^ (v11 + j);
    v9 ^= (unsigned __int8)(v3 - v11) ^ (unsigned __int8)j;
    v8 -= v3 + v11 - j;
  }
  mmprotect(
    (void *)(i + HIDWORD(v2)),
    (void *)((v6 + 4095) & 0xFFFFF000),
    (char *)&off_4 + 1,
    (void *)(i + HIDWORD(v2)));
  cacheflush_syscall(i + HIDWORD(v2), v6);
  word_A010[0] = i;
  dword_A380 = i;
  unk_A384 = v14;
  return sub_8630();
}
略过很多字节变换,略过重命名后的 mmprotect() 和 cacheflush_syscall(),整段其实就执行了一个函数 [sub_8630],跟进之后发现伪代码如下。

int sub_8630()
{
  int result; // r0@1
  int v1; // [sp+Ch] [bp-4h]@1

  v1 = 0;
  dword_A378 = 0;
  result = pthread_create(&v1, 0, anti_init, dword_A380);
  unk_A37C = 1;
  return result;
}
可以看出是 pthread_create 新开了一个线程执行 anti_init 函数,进入 F5 查看 anti_init 函数伪代码。


int __fastcall anti_init(int a1)
{
  int v2; // r0@3
  int v3; // r0@3
  int v4; // r1@3
  int v5; // r2@7
  int v6; // r0@10
  int *v7; // r3@14
  int *v8; // r1@15
  int *v9; // r3@15
  int v10; // r2@15
  unsigned int v11; // r0@15
  int *v12; // r3@16
  int v13; // [sp+0h] [bp-480h]@3
  int v14; // [sp+4h] [bp-47Ch]@16
  int v15; // [sp+8h] [bp-478h]@16
  int v16; // [sp+Ch] [bp-474h]@15
  int v17; // [sp+10h] [bp-470h]@14
  int v18; // [sp+14h] [bp-46Ch]@14
  int v19; // [sp+18h] [bp-468h]@12
  int v20; // [sp+1Ch] [bp-464h]@12
  int v21; // [sp+20h] [bp-460h]@9
  int v22; // [sp+24h] [bp-45Ch]@7
  int v23; // [sp+28h] [bp-458h]@7
  int v24; // [sp+2Ch] [bp-454h]@3
  int v25; // [sp+30h] [bp-450h]@3
  int fd_1; // [sp+34h] [bp-44Ch]@3
  int v27; // [sp+38h] [bp-448h]@3
  int v28; // [sp+3Ch] [bp-444h]@1
  void *v29; // [sp+40h] [bp-440h]@1
  char *v30; // [sp+44h] [bp-43Ch]@1
  char *v31; // [sp+48h] [bp-438h]@1
  char v32; // [sp+4Ch] [bp-434h]@1
  char v33; // [sp+4Dh] [bp-433h]@1
  char v34; // [sp+4Eh] [bp-432h]@1
  char v35; // [sp+4Fh] [bp-431h]@1
  char v36; // [sp+50h] [bp-430h]@1
  char v37; // [sp+51h] [bp-42Fh]@1
  char v38; // [sp+52h] [bp-42Eh]@1
  char v39; // [sp+53h] [bp-42Dh]@1
  char v40; // [sp+54h] [bp-42Ch]@1
  char v41; // [sp+55h] [bp-42Bh]@1
  char v42; // [sp+56h] [bp-42Ah]@1
  char v43; // [sp+57h] [bp-429h]@1
  char v44; // [sp+58h] [bp-428h]@1
  char v45; // [sp+59h] [bp-427h]@1
  char v46; // [sp+5Ah] [bp-426h]@1
  char v47; // [sp+5Bh] [bp-425h]@1
  int fd; // [sp+5Ch] [bp-424h]@3
  char v49; // [sp+60h] [bp-420h]@7
  int v50; // [sp+460h] [bp-20h]@4
  int v51; // [sp+464h] [bp-1Ch]@1
  int v52; // [sp+468h] [bp-18h]@1
  int v53; // [sp+46Ch] [bp-14h]@1

  v53 = a1;
  ++dword_A378;
  v31 = (char *)&GLOBAL_OFFSET_TABLE_;
  v30 = &v32;
  v29 = &GLOBAL_OFFSET_TABLE_;
  unk_A388 = malloc(1024);
  v28 = sub_6DEC();
  unk_A38C = sub_6EE0(0, v53, unk_A384);
  v52 = getppid();
  v38 = unk_A2B7 ^ 0x96;
  v37 = unk_A2B6 ^ 0xF7;
  v46 = unk_A2BF ^ 0xE1;
  v39 = unk_A2B8 ^ 0x9E;
  v34 = unk_A2B3 ^ 0x85;
  v41 = unk_A2BA ^ 0x98;
  v47 = unk_A2C0;
  v32 = unk_A2B1 ^ 0xE9;
  v35 = unk_A2B4 ^ 0x95;
  v44 = unk_A2BD ^ 0xCA;
  v42 = unk_A2BB ^ 0xEE;
  v36 = unk_A2B5 ^ 0xEA;
  v33 = unk_A2B2 ^ 0x97;
  v45 = unk_A2BE ^ 0xD3;
  v43 = unk_A2BC ^ 0xA5;
  v40 = unk_A2B9 ^ 0xFB;
  v51 = opendir(&v32);
  if ( v51 )
  {
    v27 = 0xFFF;
    fd = inotify_init();
    fd_1 = fd;
    v2 = fcntl(fd, 3, 0);
    v3 = fcntl(fd_1, 4, v2 | 0x800);
    v4 = fd;
    *((_BYTE *)&v13 - 4) = byte_A2CD ^ 0x8D;
    *((_BYTE *)&v13 - 16) = unk_A2C1 ^ 0x91;
    *((_BYTE *)&v13 - 7) = byte_A2CA ^ 0xC5;
    *((_BYTE *)&v13 - 5) = byte_A2CC ^ 0x80;
    *((_BYTE *)&v13 - 13) = byte_A2C4 ^ 0xD3;
    *((_BYTE *)&v13 - 11) = byte_A2C6 ^ 0xE1;
    *((_BYTE *)&v13 - 9) = byte_A2C8 ^ 0xCA;
    *((_BYTE *)&v13 - 8) = byte_A2C9 ^ 0xE1;
    *((_BYTE *)&v13 - 2) = byte_A2CF;
    *((_BYTE *)&v13 - 3) = byte_A2CE ^ 0xC8;
    *((_BYTE *)&v13 - 15) = byte_A2C2 ^ 0x87;
    *((_BYTE *)&v13 - 14) = byte_A2C3 ^ 0x96;
    *((_BYTE *)&v13 - 6) = byte_A2CB ^ 0xDA;
    *((_BYTE *)&v13 - 12) = byte_A2C5 ^ 0xD7;
    *((_BYTE *)&v13 - 10) = byte_A2C7 ^ 0xE2;
    v25 = v3;
    v24 = inotify_add_watch(v4, (int)(&v13 - 4), v27);// "/proc/self/mem"
    while ( 1 )
    {
      v50 = readdir(v51);
      if ( !v50 )
        break;
      if ( *(_BYTE *)(v50 + 18) & 4 && 46 != *(_BYTE *)(v50 + 19) )
      {
        v5 = v50 + 19;
        *((_BYTE *)&v13 - 11) = byte_A2DD ^ 0x8D;
        *((_BYTE *)&v13 - 22) = byte_A2D2 ^ 0x9E;
        *((_BYTE *)&v13 - 12) = byte_A2DC ^ 0xAD;
        *((_BYTE *)&v13 - 5) = byte_A2E3 ^ 0x86;
        *((_BYTE *)&v13 - 14) = byte_A2DA ^ 0xE1;
        *((_BYTE *)&v13 - 8) = byte_A2E0 ^ 0xB1;
        *((_BYTE *)&v13 - 10) = byte_A2DE ^ 0xCF;
        *((_BYTE *)&v13 - 15) = byte_A2D9 ^ 0xB1;
        *((_BYTE *)&v13 - 24) = unk_A2D0 ^ 0xA7;
        *((_BYTE *)&v13 - 13) = byte_A2DB ^ 0xE0;
        *((_BYTE *)&v13 - 7) = byte_A2E1 ^ 0xA2;
        *((_BYTE *)&v13 - 3) = byte_A2E5 ^ 0xAB;
        *((_BYTE *)&v13 - 17) = byte_A2D7 ^ 0x8B;
        *((_BYTE *)&v13 - 2) = byte_A2E6;
        *((_BYTE *)&v13 - 23) = byte_A2D1 ^ 0xBD;
        *((_BYTE *)&v13 - 19) = byte_A2D5 ^ 0xCC;
        *((_BYTE *)&v13 - 9) = byte_A2DF ^ 0xA7;
        *((_BYTE *)&v13 - 6) = byte_A2E2 ^ 0xE6;
        *((_BYTE *)&v13 - 16) = byte_A2D8 ^ 0xA0;
        *((_BYTE *)&v13 - 18) = byte_A2D6 ^ 0x83;
        *((_BYTE *)&v13 - 21) = byte_A2D3 ^ 0x98;
        *((_BYTE *)&v13 - 20) = byte_A2D4 ^ 0xD2;
        *((_BYTE *)&v13 - 4) = byte_A2E4 ^ 0xC2;
        v23 = sprintf(&v49, &v13 - 6, v5);
        v22 = inotify_add_watch(fd, (int)&v49, 0xFFF);// "/proc/self/task/{pid}/mem"
      }
    }
    v21 = closedir(v51);
    while ( 1 )
    {
      *((_BYTE *)&v13 - 12) = byte_A2FC ^ 0x8D;
      *((_BYTE *)&v13 - 7) = byte_A301;
      *((_BYTE *)&v13 - 10) = byte_A2FE ^ 0xD9;
      *((_BYTE *)&v13 - 14) = byte_A2FA ^ 0xD2;
      *((_BYTE *)&v13 - 17) = byte_A2F7 ^ 0xD5;
      *((_BYTE *)&v13 - 19) = byte_A2F5 ^ 0x81;
      *((_BYTE *)&v13 - 22) = byte_A2F2 ^ 0xB8;
      *((_BYTE *)&v13 - 13) = byte_A2FB ^ 0xC7;
      *((_BYTE *)&v13 - 15) = byte_A2F9 ^ 0xB2;
      *((_BYTE *)&v13 - 18) = byte_A2F6 ^ 0x81;
      *((_BYTE *)&v13 - 24) = unk_A2F0 ^ 0x9C;
      *((_BYTE *)&v13 - 9) = byte_A2FF ^ 0x95;
      *((_BYTE *)&v13 - 20) = byte_A2F4 ^ 0x82;
      *((_BYTE *)&v13 - 23) = byte_A2F1 ^ 0x81;
      *((_BYTE *)&v13 - 16) = byte_A2F8 ^ 0x88;
      *((_BYTE *)&v13 - 8) = byte_A300 ^ 0xC;
      *((_BYTE *)&v13 - 11) = byte_A2FD ^ 0xC8;
      *((_BYTE *)&v13 - 21) = byte_A2F3 ^ 0x8B;
      v6 = chk_status_trace((int)(&v13 - 6));
      if ( v6 != v52 )
      {
        *((_BYTE *)&v13 - 16) = byte_A2F8 ^ 0x88;
        *((_BYTE *)&v13 - 11) = byte_A2FD ^ 0xC8;
        *((_BYTE *)&v13 - 24) = unk_A2F0 ^ 0x9C;
        *((_BYTE *)&v13 - 9) = byte_A2FF ^ 0x95;
        *((_BYTE *)&v13 - 8) = byte_A300 ^ 0xC;
        *((_BYTE *)&v13 - 10) = byte_A2FE ^ 0xD9;
        *((_BYTE *)&v13 - 20) = byte_A2F4 ^ 0x82;
        *((_BYTE *)&v13 - 18) = byte_A2F6 ^ 0x81;
        *((_BYTE *)&v13 - 7) = byte_A301;
        *((_BYTE *)&v13 - 23) = byte_A2F1 ^ 0x81;
        *((_BYTE *)&v13 - 19) = byte_A2F5 ^ 0x81;
        *((_BYTE *)&v13 - 14) = byte_A2FA ^ 0xD2;
        *((_BYTE *)&v13 - 21) = byte_A2F3 ^ 0x8B;
        *((_BYTE *)&v13 - 12) = byte_A2FC ^ 0x8D;
        *((_BYTE *)&v13 - 13) = byte_A2FB ^ 0xC7;
        *((_BYTE *)&v13 - 22) = byte_A2F2 ^ 0xB8;
        *((_BYTE *)&v13 - 15) = byte_A2F9 ^ 0xB2;
        *((_BYTE *)&v13 - 17) = byte_A2F7 ^ 0xD5;
        if ( chk_status_trace((int)(&v13 - 6)) )
        {
          *((_BYTE *)&v13 - 6) = byte_A304 ^ 0xAF;
          *((_BYTE *)&v13 - 8) = unk_A302 ^ 0x8B;
          *((_BYTE *)&v13 - 5) = byte_A305 ^ 0xF3;
          *((_BYTE *)&v13 - 7) = byte_A303 ^ 0xCB;
          *((_BYTE *)&v13 - 4) = byte_A306;
          *((_BYTE *)&v13 - 13) = byte_A30A ^ 0xA7;
          *((_BYTE *)&v13 - 5) = byte_A312 ^ 0xDF;
          *((_BYTE *)&v13 - 9) = byte_A30E ^ 0xB5;
          *((_BYTE *)&v13 - 14) = byte_A309 ^ 0xAC;
          *((_BYTE *)&v13 - 4) = byte_A313 ^ 0x80;
          *((_BYTE *)&v13 - 8) = byte_A30F ^ 0xFD;
          *((_BYTE *)&v13 - 12) = byte_A30B ^ 0xC4;
          *((_BYTE *)&v13 - 7) = byte_A310 ^ 0xE7;
          *((_BYTE *)&v13 - 15) = byte_A308 ^ 0xC5;
          *((_BYTE *)&v13 - 16) = unk_A307 ^ 0xA9;
          *((_BYTE *)&v13 - 3) = byte_A314;
          *((_BYTE *)&v13 - 6) = byte_A311 ^ 0x9E;
          *((_BYTE *)&v13 - 10) = byte_A30D ^ 0x93;
          *((_BYTE *)&v13 - 11) = byte_A30C ^ 0xE3;
          v20 = log(6, (int)(&v13 - 2), (int)(&v13 - 4), (int)(&v13 - 4));// log("anti", "tracer antied")
          v19 = j_raise(0x11);
        }
      }
      if ( read(fd, &v49, dword_400) > 0 )
      {
        *((_BYTE *)&v13 - 5) = byte_A305 ^ 0xF3;
        *((_BYTE *)&v13 - 4) = byte_A306;
        *((_BYTE *)&v13 - 6) = byte_A304 ^ 0xAF;
        *((_BYTE *)&v13 - 8) = unk_A302 ^ 0x8B;
        *((_BYTE *)&v13 - 7) = byte_A303 ^ 0xCB;
        v7 = &v13 - 4;
        *(_BYTE *)v7 = unk_A315 ^ 0xB2;
        *((_BYTE *)&v13 - 14) = unk_A317 ^ 0xA6;
        *((_BYTE *)&v13 - 11) = unk_A31A ^ 0xBF;
        *((_BYTE *)&v13 - 13) = unk_A318 ^ 0xBA;
        *((_BYTE *)&v13 - 15) = unk_A316 ^ 0xAF;
        *((_BYTE *)&v13 - 10) = unk_A31B ^ 0x92;
        *((_BYTE *)&v13 - 7) = unk_A31E ^ 0xE7;
        *((_BYTE *)&v13 - 9) = unk_A31C ^ 0x9E;
        *((_BYTE *)&v13 - 8) = unk_A31D ^ 0xB3;
        *((_BYTE *)&v13 - 12) = unk_A319 ^ 0xDD;
        *((_BYTE *)v7 + 10) = unk_A31F;
        v18 = log(6, (int)(&v13 - 2), (int)(&v13 - 4), (int)(&v13 - 4));// log("anti", "mem antied")
        v17 = j_raise(17);
      }
      v8 = (int *)&v31[(_DWORD)&dword_438[2]];
      v9 = (int *)&v31[(_DWORD)&dword_438[4]];
      ++*((_DWORD *)v31 + 269);
      v10 = *v8;
      v16 = *v9;
      v11 = sub_6EE0(0, v53, v10);
      if ( v16 != v11 )
      {
        *((_BYTE *)&v13 - 5) = byte_A305 ^ 0xF3;
        *((_BYTE *)&v13 - 4) = byte_A306;
        *((_BYTE *)&v13 - 7) = byte_A303 ^ 0xCB;
        *((_BYTE *)&v13 - 8) = unk_A302 ^ 0x8B;
        *((_BYTE *)&v13 - 6) = byte_A304 ^ 0xAF;
        v12 = &v13 - 2;
        *(_BYTE *)v12 = unk_A320 ^ 0xCD;
        *((_BYTE *)&v13 - 6) = unk_A322 ^ 0x87;
        *((_BYTE *)&v13 - 7) = unk_A321 ^ 0xDF;
        *((_BYTE *)&v13 - 3) = unk_A325 ^ 0xF5;
        *((_BYTE *)&v13 - 5) = unk_A323 ^ 0xFC;
        *((_BYTE *)&v13 - 2) = unk_A326;
        *((_BYTE *)v12 + 4) = unk_A324 ^ 0x1A;
        v15 = log(6, (int)(&v13 - 2), (int)(&v13 - 2), (int)(&v13 - 2));// log("anti", "antied")
        v14 = j_raise(17);
      }
      v13 = sleep(1);
    }
  }
  return 0;
}
一个 while(1) 的循环里面根据不同的条件执行了 3 个 raise() 函数,不用管具体是根据什么样的 3 个条件执行的 raise() 操作,直接把 [BL  j_raise] 对应 arm 十六进制码修改成 [Mov R1, R1] 就可以达到 Bypass 反调试的目的,这个地方殊途同归的 Bypass 方法很多。

JNI_Onload

还是同样的方法来到 JNI_Onload 在 .so 里的位置,偏移量是 [+ 65A8],IDA 静态查看,F5 之后得到对应的伪代码,动态 + 静态结合调试可以还原出大部分函数的真实目的。

void *__fastcall JNI_Onload(JavaVM *J_vm_1, JNIEnv *J_env)
{
  char v2; // r0@5
  int v3; // r0@9
  int v4; // lr@9
  int v5; // r3@9
  char *v6; // r12@9
  int v8; // [sp+0h] [bp-450h]@8
  int (__fastcall *J_dlsym_1)(_DWORD, _DWORD); // [sp+4h] [bp-44Ch]@10
  int v10; // [sp+8h] [bp-448h]@9
  int v11; // [sp+Ch] [bp-444h]@9
  int v12; // [sp+10h] [bp-440h]@9
  int *v13; // [sp+14h] [bp-43Ch]@9
  int a1a; // [sp+18h] [bp-438h]@9
  int v15; // [sp+1Ch] [bp-434h]@8
  int v16; // [sp+20h] [bp-430h]@4
  char *v17; // [sp+24h] [bp-42Ch]@1
  int v18; // [sp+28h] [bp-428h]@1
  int (__fastcall *J_dlsym)(_DWORD, _DWORD); // [sp+2Ch] [bp-424h]@9
  int v20; // [sp+30h] [bp-420h]@9
  int v21; // [sp+34h] [bp-41Ch]@2
  JNIEnv *J_env_1; // [sp+434h] [bp-1Ch]@1
  JavaVM *J_vm; // [sp+438h] [bp-18h]@1
  void *v24; // [sp+43Ch] [bp-14h]@10

  J_vm = J_vm_1;
  J_env_1 = J_env;
  v18 = 0;
  v17 = (char *)&GLOBAL_OFFSET_TABLE_;
  while ( sub_69D4((int)&v21) )
    ;
  if ( !*(_DWORD *)&v17[(_DWORD)dword_438] )
    v16 = j_raise(9);
  while ( 1 )
  {
    v2 = 0;
    if ( *((_DWORD *)v17 + 269) )
      v2 = 1;
    if ( !(((unsigned __int8)v2 ^ 1) & 1) )
      break;
    *((_BYTE *)&v8 - 7) = byte_A025 ^ 0xB5;
    *((_BYTE *)&v8 - 8) = unk_A024 ^ 0x81;
    *((_BYTE *)&v8 - 4) = byte_A028 ^ 0xEC;
    *((_BYTE *)&v8 - 6) = byte_A026 ^ 0xD4;
    *((_BYTE *)&v8 - 5) = byte_A027 ^ 0xF5;
    *((_BYTE *)&v8 - 3) = byte_A029;
    *((_BYTE *)&v8 - 13) = byte_A207 ^ 0xBA;
    *((_BYTE *)&v8 - 6) = byte_A20E;
    *((_BYTE *)&v8 - 14) = byte_A206 ^ 0x88;
    *((_BYTE *)&v8 - 8) = byte_A20C ^ 0xD3;
    *((_BYTE *)&v8 - 9) = byte_A20B ^ 0xC9;
    *((_BYTE *)&v8 - 12) = byte_A208 ^ 0xF7;
    *((_BYTE *)&v8 - 11) = byte_A209 ^ 0xED;
    *((_BYTE *)&v8 - 16) = unk_A204 ^ 0xD1;
    *((_BYTE *)&v8 - 10) = byte_A20A ^ 0xEB;
    *((_BYTE *)&v8 - 15) = byte_A205 ^ 0xCE;
    *((_BYTE *)&v8 - 7) = byte_A20D ^ 0x91;
    v15 = log(6, (int)(&v8 - 2), (int)(&v8 - 4), (int)(&v8 - 4));
  }
  a1a = 6;
  v13 = &v21;
  v12 = (int)&GLOBAL_OFFSET_TABLE_;
  v11 = J_unzip((int)&v21, dword_A020);
  v3 = linker_method_dlopen((int)v13, 0);
  v20 = v3;
  v4 = v12 + 715;
  *((_BYTE *)&v8 - 6) = *(_BYTE *)(v12 + 725);
  *((_BYTE *)&v8 - 14) = *(_BYTE *)(v4 + 2) ^ 0x8B;
  *((_BYTE *)&v8 - 7) = *(_BYTE *)(v4 + 9) ^ 0xB0;
  *((_BYTE *)&v8 - 8) = *(_BYTE *)(v4 + 8) ^ 0x92;
  *((_BYTE *)&v8 - 9) = *(_BYTE *)(v4 + 7) ^ 0xCD;
  *((_BYTE *)&v8 - 11) = *(_BYTE *)(v4 + 5) ^ 0x84;
  *((_BYTE *)&v8 - 16) = *(_BYTE *)v4 ^ 0xC3;
  *((_BYTE *)&v8 - 12) = *(_BYTE *)(v4 + 4) ^ 0xA4;
  *((_BYTE *)&v8 - 13) = *(_BYTE *)(v4 + 3) ^ 0xB1;
  *((_BYTE *)&v8 - 10) = *(_BYTE *)(v4 + 6) ^ 0x9E;
  *((_BYTE *)&v8 - 15) = *(_BYTE *)(v4 + 1) ^ 0xDB;
  J_dlsym = (int (__fastcall *)(_DWORD, _DWORD))linker_method_dlsym(v3, &v8 - 4);
  v5 = v12;
  v6 = &byte_E0[v12];
  *((_BYTE *)&v8 - 7) = byte_E0[v12 + 1] ^ 0xB5;
  *((_BYTE *)&v8 - 4) = v6[4] ^ 0xEC;
  *((_BYTE *)&v8 - 3) = v6[5];
  *((_BYTE *)&v8 - 8) = *v6 ^ 0x81;
  *((_BYTE *)&v8 - 5) = v6[3] ^ 0xF5;
  *((_BYTE *)&v8 - 6) = v6[2] ^ 0xD4;
  *((_BYTE *)&v8 - 13) = *(_BYTE *)(v5 + 729) ^ 0xAA;
  *((_BYTE *)&v8 - 12) = *(_BYTE *)(v5 + 730) ^ 0xBE;
  *((_BYTE *)&v8 - 16) = *(_BYTE *)(v5 + 726) ^ 0x84;
  *((_BYTE *)&v8 - 15) = *(_BYTE *)(v5 + 727) ^ 0xC9;
  *((_BYTE *)&v8 - 6) = *(_BYTE *)(v5 + 736);
  *((_BYTE *)&v8 - 7) = *(_BYTE *)(v5 + 735) ^ 0xC7;
  *((_BYTE *)&v8 - 14) = *(_BYTE *)(v5 + 728) ^ 0xA9;
  *((_BYTE *)&v8 - 11) = *(_BYTE *)(v5 + 731) ^ 0x95;
  *((_BYTE *)&v8 - 9) = *(_BYTE *)(v5 + 733) ^ 0x9D;
  *((_BYTE *)&v8 - 10) = *(_BYTE *)(v5 + 732) ^ 0xDC;
  *((_BYTE *)&v8 - 8) = *(_BYTE *)(v5 + 734) ^ 0xC2;
  v10 = log(a1a, (int)(&v8 - 2), (int)(&v8 - 4), v5);// log("txtag", "load done!")
  if ( J_dlsym )
  {
    J_dlsym_1 = J_dlsym;
    v24 = (void *)J_dlsym(J_vm, J_env_1);
  }
  else
  {
    v24 = &unk_10004;
  }
  return v24;
}

这里需要重点关注的就是 dlopen 和 dlsym 这两个函数了,其他都是一些字符串操作的内容,对脱壳没有什么太大的帮助。其实 dlopen 都不需要关注了,直接在最后 J_dlsym 这里下断,F7 。

跳到 .so 外的一段内存,很明显是程序 mmap 进来的内容,至于怎么 mmap mmprotect 之类的不需要做太多关注,看看这里指向代码的功能。

debug150:7860E800 ; signed int __fastcall sub_7860E800(int a1, int)
debug150:7860E800 sub_7860E800
debug150:7860E800
debug150:7860E800 var_14= -0x14
debug150:7860E800
debug150:7860E800 PUSH            {R0,R1,R4-R6,LR}
debug150:7860E802 ADD             R5, SP, #0x18+var_14
debug150:7860E804 MOVS            R4, #0
debug150:7860E806 MOVS            R1, R5
debug150:7860E808 LDR             R2, =0x10006
debug150:7860E80A MOVS            R6, R0
debug150:7860E80C STR             R4, [SP,#0x18+var_14]
debug150:7860E80E BL              J_getEnv
debug150:7860E812 CMP             R0, R4
debug150:7860E814 BEQ             loc_7860E844
debug150:7860E816 MOVS            R0, R6
debug150:7860E818 MOVS            R1, R5
debug150:7860E81A LDR             R2, =0x10004
debug150:7860E81C BL              J_getEnv
debug150:7860E820 CMP             R0, R4
debug150:7860E822 BEQ             loc_7860E848
debug150:7860E824 MOVS            R0, R6
debug150:7860E826 MOVS            R1, R5
debug150:7860E828 LDR             R2, =0x10002
debug150:7860E82A BL              J_getEnv
debug150:7860E82E CMP             R0, R4
debug150:7860E830 BEQ             loc_7860E84C
debug150:7860E832 MOVS            R0, R6
debug150:7860E834 MOVS            R1, R5
debug150:7860E836 LDR             R2, =0x10001
debug150:7860E838 BL              J_getEnv
debug150:7860E83C CMP             R0, R4
debug150:7860E83E BNE             loc_7860E85E
debug150:7860E840 LDR             R4, =0x10001
debug150:7860E842 B               loc_7860E84E
debug150:7860E844 ; ---------------------------------------------------------------------------
debug150:7860E844
debug150:7860E844 loc_7860E844                            ; CODE XREF: sub_7860E800+14 j
debug150:7860E844 LDR             R4, =0x10006
debug150:7860E846 B               loc_7860E84E
debug150:7860E848 ; ---------------------------------------------------------------------------
debug150:7860E848
debug150:7860E848 loc_7860E848                            ; CODE XREF: sub_7860E800+22 j
debug150:7860E848 LDR             R4, =0x10004
debug150:7860E84A B               loc_7860E84E
debug150:7860E84C ; ---------------------------------------------------------------------------
debug150:7860E84C
debug150:7860E84C loc_7860E84C                            ; CODE XREF: sub_7860E800+30 j
debug150:7860E84C LDR             R4, =0x10002
debug150:7860E84E
debug150:7860E84E loc_7860E84E                            ; CODE XREF: sub_7860E800+42 j
debug150:7860E84E                                         ; sub_7860E800+46 j ...
debug150:7860E84E LDR             R3, [SP,#0x18+var_14]
debug150:7860E850 CMP             R3, #0
debug150:7860E852 BEQ             loc_7860E85E
debug150:7860E854 BL              sub_7860EFB8
debug150:7860E858 LDR             R0, [SP,#0x18+var_14]
debug150:7860E85A BL              sub_7860E7C4
debug150:7860E85E
debug150:7860E85E loc_7860E85E                            ; CODE XREF: sub_7860E800+3E j
debug150:7860E85E                                         ; sub_7860E800+52 j
debug150:7860E85E MOVS            R0, R4
debug150:7860E860 POP             {R1,R2,R4-R6,PC}
debug150:7860E860 ; End of function sub_7860E800

还是一堆的赋值操作略过不表,看到执行了 [sub_7860EFB8] [sub_7860E7C4] 两个函数,分别看下这两个函数的作用。

signed int sub_7860EFB8()
{
  signed int result; // r0@1

  dword_78626068 = (int)"java/lang/String";
  dword_7862606C = (int)"getBytes";
  dword_78626070 = (int)"()[B";
  dword_78626074 = (int)"android/os/Build$VERSION";
  dword_78626078 = (int)"SDK_INT";
  dword_7862607C = (int)"I";
  .......
}

[sub_7860EFB8] 全是赋值操作,跳过。下面是 F5 之后的 [sub_7860E7C4],主要执行了 [sub_7860E780] 然后打了个 registerNatives 相关的 log,相信作安卓逆向的同学看到 registerNative 这个都会比较激动吧,跟进 [sub_7860E780]。

int __fastcall sub_7860E7C4(int a1)
{
  int v1; // r4@1

  v1 = sub_7860E780(a1, (int)"com/tencent/StubShell/TxAppEntry", (int)&dword_78626004, 5);
  if ( v1 )
    v1 = 1;
  else
    log(3, (int)"SecShell", (int)"registerNatives Fail");
  return v1;
}
F5 后的伪代码如下,亮眼的 0x35C 刚好是 registerNatives 相对于 JNINativeInterface 的偏移量,在这下断。

int __fastcall sub_7860E780(int a1, int a2, int a3, int a4)
{
  int v4; // r5@1
  int v5; // r4@1
  int v6; // r6@1
  int J_env; // r1@1
  int v8; // r0@2

  v4 = a4;
  v5 = a1;
  v6 = a3;
  J_env = (*(int (**)(void))(*(_DWORD *)a1 + 0x18))();
  if ( J_env )
  {
    v8 = (*(int (__fastcall **)(int, int, int, int))(*(_DWORD *)v5 + 0x35C))(v5, J_env, v6, v4);// registerNatives
    J_env = 1;
    if ( v8 < 0 )
    {
      ((void (__fastcall *)(signed int, const char *, const char *))log)(3, "SecShell", "register nativers error");
      J_env = 0;
    }
  }
  return J_env;
}
触发断点之后查看 R2 寄存器的值。

可以看到这里的 RegisterNative 是用来注册 native load(Landroid/content/Context;)V ,函数指针则是 [0x78615839],至于为什么是这样可以参考我 *这一篇文章

接着跳入 [0x78615839],这里我直接贴 F5 + 重命名函数之后的伪代码了,如图。
可以看到先检查了 * sdk 版本,* dalvik Or art,* 是否存在 zjdroid,然后在分别进行了两个操作,因为这里我用的是 dalvik,所以跟进 [sub_78614CD0]。

_DWORD *__fastcall sub_78614CD0(int a1, int a2)
{
  int v2; // r6@1
  int v3; // r0@2
  int v4; // r4@2
  int v5; // r0@2
  int v6; // r0@2
  int v7; // r0@2
  int v8; // r7@2
  int v9; // ST00_4@3
  int v10; // ST00_4@3
  int v11; // r0@5
  int v12; // ST00_4@5
  const char *v13; // r1@6
  const char *v14; // r2@6
  int v15; // r0@16
  int v16; // r5@16
  _DWORD *v17; // r3@16
  int v18; // ST00_4@17
  int v19; // ST00_4@18
  const char *v20; // r1@20
  const char *v21; // r2@20
  int v22; // r4@21
  int v23; // r1@23
  const char *v24; // r1@24
  const char *v25; // r2@24
  int v26; // r4@33
  int v27; // ST14_4@37
  int v28; // r0@37
  int v29; // r1@38
  int v30; // r1@41
  int v31; // r5@44
  int v32; // ST00_4@44
  int v33; // r0@45
  int v34; // ST00_4@46
  int v35; // r5@46
  int v36; // r4@50
  int v37; // r4@54
  int v38; // r2@58
  int v39; // r3@59
  int v40; // r3@61
  int v41; // r3@62
  int v42; // r3@63
  int v43; // r3@64
  int v44; // r5@64
  int v45; // r2@65
  int v46; // r5@77
  int v47; // r2@77
  int v48; // r0@79
  int v49; // r0@80
  int v50; // r4@82
  int v51; // r0@84
  int v52; // r5@84
  int m; // r4@84
  int v54; // r0@86
  int v55; // r2@87
  const char *v56; // r3@87
  int v57; // ST00_4@87
  int v58; // r1@87
  int v59; // r0@87
  int v60; // r0@76
  int i; // r4@76
  int v62; // r0@90
  int v63; // r5@92
  int v64; // r0@92
  int j; // r4@92
  int v66; // r0@94
  int v67; // r5@95
  int v68; // r4@95
  int v69; // r0@95
  int k; // r4@95
  int v71; // r0@97
  int v72; // r5@98
  int v73; // r4@98
  int v74; // r7@98
  int v75; // r0@98
  int v76; // r0@98
  int l; // r4@98
  int v78; // r0@100
  int v79; // r4@101
  int v80; // r0@101
  int v81; // ST00_4@102
  int v82; // ST00_4@103
  _DWORD *result; // r0@105
  int v84; // [sp+10h] [bp-120h]@3
  int v85; // [sp+10h] [bp-120h]@76
  int v86; // [sp+14h] [bp-11Ch]@5
  signed int v87; // [sp+14h] [bp-11Ch]@37
  int v88; // [sp+14h] [bp-11Ch]@95
  int v89; // [sp+18h] [bp-118h]@3
  int v90; // [sp+18h] [bp-118h]@13
  int v91; // [sp+1Ch] [bp-114h]@1
  signed int v92; // [sp+20h] [bp-110h]@33
  int v93; // [sp+20h] [bp-110h]@51
  int v94; // [sp+24h] [bp-10Ch]@3
  int v95; // [sp+24h] [bp-10Ch]@95
  int v96; // [sp+28h] [bp-108h]@2
  signed int v97; // [sp+28h] [bp-108h]@51
  int v98; // [sp+2Ch] [bp-104h]@3
  int v99; // [sp+30h] [bp-100h]@13
  int v100; // [sp+34h] [bp-FCh]@13
  int v101; // [sp+38h] [bp-F8h]@2
  int v102; // [sp+3Ch] [bp-F4h]@13
  int v103; // [sp+40h] [bp-F0h]@37
  int v104; // [sp+44h] [bp-ECh]@3
  int v105; // [sp+4Ch] [bp-E4h]@92
  int v106; // [sp+54h] [bp-DCh]@2
  _DWORD *v107; // [sp+5Ch] [bp-D4h]@1
  int v108; // [sp+60h] [bp-D0h]@34
  char v109; // [sp+64h] [bp-CCh]@37
  char v110; // [sp+74h] [bp-BCh]@50
  int v111; // [sp+88h] [bp-A8h]@50
  char v112; // [sp+8Ch] [bp-A4h]@50
  int v113; // [sp+A0h] [bp-90h]@50
  char v114; // [sp+A4h] [bp-8Ch]@34
  int v115; // [sp+B8h] [bp-78h]@34
  signed int v116; // [sp+C4h] [bp-6Ch]@37
  int v117; // [sp+114h] [bp-1Ch]@1

  v2 = a1;
  v117 = *(_DWORD *)dword_78625DA4;
  v107 = (_DWORD *)dword_78625DA4;
  v91 = J_GC_object_method(
          a1,
          a2,
          (int)"android/content/Context",
          (int)"getClassLoader",
          (int)"()Ljava/lang/ClassLoader;");
  if ( !v91 )
    goto LABEL_105;
  v3 = getEnv(v2, "com/tencent/StubShell/TxAppEntry");
  v4 = v3;
  v5 = ((int (__fastcall *)(int, int, const char *, const char *))unk_78610864)(
         v2,
         v3,
         "mSrcPath",
         "Ljava/lang/String;");
  v101 = ((int (__fastcall *)(int, int, int))unk_78610872)(v2, v4, v5);
  v96 = J_copy_array_bytes(v2, v101);
  v6 = ((int (__fastcall *)(int, int, const char *, const char *))unk_78610864)(v2, v4, "mPKName", "Ljava/lang/String;");
  v7 = ((int (__fastcall *)(int, int, int))unk_78610872)(v2, v4, v6);
  v106 = J_copy_array_bytes(v2, v7);
  v8 = unk_78625DA8;
  if ( J_sdk_int > 10 )
  {
    v9 = *(_DWORD *)(unk_78625DA8 + 248);
    v104 = ((int (__fastcall *)(int, int, _DWORD, _DWORD))unk_7860EE40)(
             v2,
             v91,
             *(_DWORD *)(unk_78625DA8 + 240),
             *(_DWORD *)(unk_78625DA8 + 244));
    v10 = *(_DWORD *)(v8 + 220);
    v98 = ((int (__fastcall *)(int, int, _DWORD, _DWORD))unk_7860EE40)(
            v2,
            v104,
            *(_DWORD *)(v8 + 212),
            *(_DWORD *)(v8 + 216));
    v94 = J_getArrayLength(v2, v98);
    v89 = 0;
    v84 = 0;
    while ( 1 )
    {
      if ( v89 >= v94 )
      {
        v90 = 0;
        v102 = 0;
        v100 = 0;
        v99 = 0;
        goto LABEL_33;
      }
      v11 = ((int (__fastcall *)(int, int, int))unk_7860E8C8)(v2, v98, v89);
      v12 = *(_DWORD *)(v8 + 232);
      v86 = ((int (__fastcall *)(_DWORD, _DWORD, _DWORD, _DWORD))unk_7860EE40)(
              v2,
              v11,
              *(_DWORD *)(v8 + 224),
              *(_DWORD *)(v8 + 228));
      if ( !v86 )
        break;
      v84 = ((int (__fastcall *)(int, int, _DWORD, _DWORD))unk_7860F770)(
              v2,
              v86,
              *(_DWORD *)(v8 + 236),
              *(_DWORD *)(v8 + 424));
      if ( !v84 )
      {
        v84 = ((int (__fastcall *)(int, int, _DWORD, _DWORD))unk_7860F950)(
                v2,
                v86,
                *(_DWORD *)(v8 + 236),
                *(_DWORD *)(v8 + 424));
        if ( !v84 )
        {
          v13 = "SecShell";
          v14 = "SetDexClassLoaderCookie GetIntField fail";
          goto LABEL_10;
        }
      }
      if ( *(_DWORD *)v84 && !((int (__cdecl *)(_DWORD))unk_7861EC2C)(*(_DWORD *)v84) )
      {
        v90 = 0;
        v102 = 0;
        v100 = 0;
        v99 = 0;
        goto LABEL_33;
      }
LABEL_14:
      ++v89;
    }
    v13 = "SecShell";
    v14 = "SetDexClassLoaderCookie GetObjectField fail:pDexPathListElementsClassName";
LABEL_10:
    log(3, (int)v13, (int)v14);
    goto LABEL_14;
  }
  v15 = getEnv(v2, *(_DWORD *)(unk_78625DA8 + 456));
  v16 = ((int (__fastcall *)(int, int, int))unk_7861080E)(v2, v91, v15);
  v17 = (_DWORD *)(v8 + 204);
  if ( v16 )
  {
    v18 = *(_DWORD *)(v8 + 208);
    v99 = ((int (__fastcall *)(_DWORD, _DWORD, _DWORD, _DWORD))unk_7860EE40)(v2, v91, *(_DWORD *)(v8 + 456), *v17);
    v100 = ((int (__fastcall *)(int, int, _DWORD, const char *))unk_7860EE40)(v2, v91, *(_DWORD *)(v8 + 456), "mPaths");
    v102 = ((int (__fastcall *)(_DWORD, _DWORD, _DWORD, const char *))unk_7860EE40)(
             v2,
             v91,
             *(_DWORD *)(v8 + 456),
             "mFiles");
    v90 = ((int (__fastcall *)(_DWORD, _DWORD, _DWORD, const char *))unk_7860EE40)(
            v2,
            v91,
            *(_DWORD *)(v8 + 456),
            "mZips");
  }
  else
  {
    v19 = *(_DWORD *)(v8 + 208);
    v99 = ((int (__fastcall *)(_DWORD, _DWORD, _DWORD, _DWORD))unk_7860EE40)(v2, v91, *(_DWORD *)(v8 + 200), *v17);
    v102 = ((int (__fastcall *)(_DWORD, _DWORD, _DWORD, const char *))unk_7860EE40)(
             v2,
             v91,
             *(_DWORD *)(v8 + 200),
             "mFiles");
    v100 = 0;
    v90 = ((int (__fastcall *)(_DWORD, _DWORD, _DWORD, const char *))unk_7860EE40)(
            v2,
            v91,
            *(_DWORD *)(v8 + 200),
            "mZips");
  }
  if ( !v99 )
  {
    v20 = "SecShell";
    v21 = "SetDexClassLoaderCookie GetObjectField fail:pmDexs";
    goto LABEL_48;
  }
  v22 = 0;
  v94 = J_getArrayLength(v2, v99);
  v84 = 0;
  while ( 1 )
  {
    if ( v22 >= v94 )
    {
      v98 = 0;
      v104 = 0;
      goto LABEL_33;
    }
    v23 = ((int (__fastcall *)(int, int, int))unk_7860E8C8)(v2, v99, v22);
    if ( !v23 )
    {
      v24 = "SecShell";
      v25 = "SetDexClassLoaderCookie GetObjectArrayElement fail";
LABEL_27:
      log(3, (int)v24, (int)v25);
      goto LABEL_30;
    }
    v84 = ((int (__fastcall *)(int, int, _DWORD, _DWORD))unk_7860F770)(
            v2,
            v23,
            *(_DWORD *)(v8 + 236),
            *(_DWORD *)(v8 + 424));
    if ( !v84 )
    {
      v24 = "SecShell";
      v25 = "SetDexClassLoaderCookie GetIntField fail";
      goto LABEL_27;
    }
    if ( *(_DWORD *)v84 && !((int (__cdecl *)(_DWORD))unk_7861EC2C)(*(_DWORD *)v84) )
      break;
LABEL_30:
    ++v22;
  }
  v98 = 0;
  v104 = 0;
LABEL_33:
  v26 = sub_786123A4(v106, (int)"classes.dex", 0);
  v92 = 0;
  if ( !v26 )
  {
    ((void (__fastcall *)(char *, int, int *))unk_786124E8)(&v114, v96, &v108);
    log(3, (int)"SecShell", (int)"strSrcPath:%s");
    ((void (__fastcall *)(char *))unk_78612A54)(&v114);
    log(3, (int)"SecShell", (int)"strSrcPath:%s");
    v26 = sub_786123A4(v115, (int)"classes.dex", 0);
    ((void (__fastcall *)(char *))unk_786124A4)(&v114);
    if ( v26 )
    {
      v92 = 0;
    }
    else
    {
      v26 = ((int (__fastcall *)(int))unk_786117A4)(v84) - 40;
      v92 = 1;
    }
  }
  v27 = ((int (__fastcall *)(int))unk_786108CA)(v26 + 40);
  log(3, (int)"SecShell", (int)"orgDexOffset:%d");
  j_j_memset_0((int)&v114, 0, 112);
  memcpy((int)&v114, v26 + v27 + 40);
  j_j_memset_0((int)&v109, 0, 16);
  ((void (__fastcall *)(char *, char *, signed int, signed int))unk_78617E4C)(&v109, &v114, 112, 32);
  v103 = v26 + v27 + 40;
  v87 = v116;
  v28 = log(3, (int)"SecShell", (int)"fileSize:%d");
  if ( v92 )
  {
    v29 = v87;
    if ( v87 & 0xFFF )
      v29 = (v87 / 4096 + 1) << 12;
    v28 = ((int (__fastcall *)(int, int, signed int))unk_7861EC7C)(v26, v29, 3);
    if ( v28 )
    {
      v30 = v87;
      if ( v87 & 0xFFF )
        v30 = (v87 / 4096 + 1) << 12;
      v28 = ((int (__fastcall *)(int, int, signed int))unk_7861EC7C)(v26, v30, 5);
    }
  }
  v31 = ((int (__fastcall *)(int))unk_78610CD4)(v28);
  v32 = *(_DWORD *)errno();
  log(3, (int)"SecShell", (int)"mRes:%d error:%d");
  if ( v31 != -1
    || (v33 = log(3, (int)"SecShell", (int)"wrong code1"), ((int (__fastcall *)(int))unk_78610D24)(v33) != -1) )
  {
LABEL_50:
    ((void (__fastcall *)(char *, int, signed int, signed int))unk_78617E4C)(&v109, v103, 112, 32);
    ((void (__fastcall *)(char *, const char *, int *))unk_786124E8)(&v110, "/data/data/", &v108);
    ((void (__fastcall *)(char *, int))loc_7861261A)(&v110, v106);
    ((void (__fastcall *)(char *, char *, const char *))unk_786127D4)(&v112, &v110, "/mix.so");
    ((void (__fastcall *)(char *, const char *))loc_7861261A)(&v110, "/mix.dex");
    v36 = sub_7860ECA4(v2, v111);
    sub_7860ECA4(v2, v113);
    if ( ((int (__fastcall *)(int, int, int))unk_786112A0)(v2, v101, v111)
      && (v93 = J_GC_static_method(
                  v2,
                  (int)"dalvik/system/DexFile",
                  (int)"loadDex",
                  (int)"(Ljava/lang/String;Ljava/lang/String;I)Ldalvik/system/DexFile;")) != 0 )
    {
      v101 = v36;
      v97 = 0;
    }
    else
    {
      log(3, (int)"SecShell", (int)"load mix.dex failed");
      ((void (__fastcall *)(char *, int))unk_78612738)(&v110, v96);
      v93 = J_GC_static_method(
              v2,
              (int)"dalvik/system/DexFile",
              (int)"loadDex",
              (int)"(Ljava/lang/String;Ljava/lang/String;I)Ldalvik/system/DexFile;");
      log(3, (int)"SecShell", (int)"load org.dex end");
      v97 = 1;
    }
    v37 = ((int (__fastcall *)(_DWORD, _DWORD, _DWORD, _DWORD))unk_7860F770)(
            v2,
            v93,
            *(_DWORD *)(v8 + 236),
            *(_DWORD *)(v8 + 424));
    if ( !v37 )
    {
      v37 = ((int (__fastcall *)(int, int, _DWORD, _DWORD))unk_7860F950)(
              v2,
              v93,
              *(_DWORD *)(v8 + 236),
              *(_DWORD *)(v8 + 424));
      if ( !v37 )
        log(3, (int)"SecShell", (int)"testCookie is null");
    }
    if ( v97 )
    {
      v38 = J_sdk_int;
    }
    else
    {
      v38 = J_sdk_int;
      if ( J_sdk_int > 10 )
      {
        v39 = *(_DWORD *)(*(_DWORD *)(v37 + 8) + 4);
        goto LABEL_64;
      }
    }
    v40 = *(_DWORD *)(v37 + 12);
    if ( v38 == 8 )
      v41 = *(_DWORD *)(v40 + 36);
    else
      v42 = *(_DWORD *)(v40 + 40);
LABEL_64:
    v108 = 0;
    ((void (__fastcall *)(int, int, signed int, int *))unk_78611690)(v2, v103, v87, &v108);
    v43 = v108;
    v44 = *(_DWORD *)(v108 + 4);
    if ( v97 )
    {
      *(_DWORD *)(v37 + 8) = v108;
      *(_BYTE *)(v37 + 4) = 1;
      if ( J_sdk_int == 10 )
        *(_DWORD *)(v84 + 16) = v103;
    }
    else
    {
      v45 = J_sdk_int;
      if ( J_sdk_int > 10 )
      {
        if ( J_sdk_int <= 18 && chk_yunos() )
          ((void (__fastcall *)(_DWORD, int, int))unk_78615CF0)(*(_DWORD *)(*(_DWORD *)(v37 + 8) + 4), v44, J_sdk_int);
        else
          ((void (__fastcall *)(_DWORD, int, int))unk_78615D04)(*(_DWORD *)(*(_DWORD *)(v37 + 8) + 4), v44, J_sdk_int);
LABEL_75:
        if ( J_sdk_int <= 10 )
        {
          v60 = getEnv(v2, "dalvik/system/DexFile");
          v85 = ((int (__fastcall *)(int, int, int, _DWORD))unk_78610880)(v2, v94 + 1, v60, 0);
          ((void (__fastcall *)(int, int, _DWORD, int))unk_7860E8D6)(v2, v85, 0, v93);
          for ( i = 0; i < v94; ((void (__fastcall *)(int, int, int, int))unk_7860E8D6)(v2, v85, i, v62) )
            v62 = ((int (__fastcall *)(int, int, int))unk_7860E8C8)(v2, v99, i++);
          if ( v100 )
          {
            v63 = J_getArrayLength(v2, v100);
            v64 = getEnv(v2, "java/lang/String");
            v105 = ((int (__fastcall *)(int, int, int, _DWORD))unk_78610880)(v2, v63 + 1, v64, 0);
            ((void (__fastcall *)(int, int, _DWORD, int))unk_7860E8D6)(v2, v105, 0, v101);
            for ( j = 0; j < v63; ((void (__fastcall *)(_DWORD, _DWORD, int, _DWORD))unk_7860E8D6)(v2, v105, j, v66) )
              v66 = ((int (__fastcall *)(_DWORD, _DWORD, int))unk_7860E8C8)(v2, v100, j++);
          }
          v67 = J_getArrayLength(v2, v102);
          v68 = getEnv(v2, "java/io/File");
          v88 = ((int (__fastcall *)(_DWORD, int, _DWORD, _DWORD))unk_78610880)(v2, v67 + 1, v68, 0);
          v69 = J_getObjectClass(v2, v68, "", "(Ljava/lang/String;)V");
          v95 = J_NewObjectV(v2, v68, v69, v101);
          ((void (__fastcall *)(_DWORD, _DWORD, _DWORD, _DWORD))unk_7860E8D6)(v2, v88, 0, v95);
          for ( k = 0; k < v67; ((void (__fastcall *)(_DWORD, _DWORD, _DWORD, _DWORD))unk_7860E8D6)(v2, v88, k, v71) )
            v71 = ((int (__fastcall *)(_DWORD, _DWORD, int))unk_7860E8C8)(v2, v102, k++);
          v72 = J_getArrayLength(v2, v90);
          v73 = getEnv(v2, "java/util/zip/ZipFile");
          v74 = ((int (__fastcall *)(_DWORD, int, _DWORD, _DWORD))unk_78610880)(v2, v72 + 1, v73, 0);
          v75 = J_getObjectClass(v2, v73, "", "(Ljava/io/File;)V");
          v76 = J_NewObjectV(v2, v73, v75, v95);
          ((void (__fastcall *)(_DWORD, _DWORD, _DWORD, _DWORD))unk_7860E8D6)(v2, v74, 0, v76);
          for ( l = 0; l < v72; ((void (__fastcall *)(_DWORD, _DWORD, _DWORD, _DWORD))unk_7860E8D6)(v2, v74, l, v78) )
            v78 = ((int (__fastcall *)(_DWORD, _DWORD, int))unk_7860E8C8)(v2, v90, l++);
          v79 = unk_78625DA8;
          v80 = getEnv(v2, *(_DWORD *)(unk_78625DA8 + 456));
          if ( ((int (__fastcall *)(int, int, int))unk_7861080E)(v2, v91, v80) )
          {
            v81 = *(_DWORD *)(v79 + 208);
            ((void (__fastcall *)(int, int, _DWORD, _DWORD))unk_7860EEFC)(
              v2,
              v91,
              *(_DWORD *)(v79 + 456),
              *(_DWORD *)(v79 + 204));
            ((void (__fastcall *)(int, int, _DWORD, const char *))unk_7860EEFC)(
              v2,
              v91,
              *(_DWORD *)(v79 + 456),
              "mPaths");
            ((void (__fastcall *)(_DWORD, _DWORD, _DWORD, const char *))unk_7860EEFC)(
              v2,
              v91,
              *(_DWORD *)(v79 + 456),
              "mFiles");
            v55 = *(_DWORD *)(v79 + 456);
            v59 = v2;
            v58 = v91;
            v56 = "mZips";
          }
          else
          {
            v82 = *(_DWORD *)(v79 + 208);
            ((void (__fastcall *)(int, int, _DWORD, _DWORD))unk_7860EEFC)(
              v2,
              v91,
              *(_DWORD *)(v79 + 200),
              *(_DWORD *)(v79 + 204));
            ((void (__fastcall *)(_DWORD, _DWORD, _DWORD, const char *))unk_7860EEFC)(
              v2,
              v91,
              *(_DWORD *)(v79 + 200),
              "mFiles");
            v55 = *(_DWORD *)(v79 + 200);
            v59 = v2;
            v58 = v91;
            v56 = "mZips";
          }
          goto LABEL_88;
        }
        v46 = getEnv(v2, "dalvik/system/DexPathList$Element");
        v47 = J_getObjectClass(v2, v46, "", "(Ljava/io/File;Ljava/util/zip/ZipFile;Ldalvik/system/DexFile;)V");
        if ( v47
          || (((void (__fastcall *)(int))unk_78610804)(v2),
              (v47 = J_getObjectClass(v2, v46, "", "(Ljava/io/File;Ljava/io/File;Ldalvik/system/DexFile;)V")) != 0) )
        {
          v48 = J_NewObjectV(v2, v46, v47, 0);
        }
        else
        {
          ((void (__fastcall *)(int))unk_78610804)(v2);
          v49 = J_getObjectClass(v2, v46, "", "(Ljava/io/File;ZLjava/io/File;Ldalvik/system/DexFile;)V");
          if ( !v49 )
          {
            v50 = 0;
LABEL_84:
            v51 = getEnv(v2, "dalvik/system/DexPathList$Element");
            v52 = ((int (__fastcall *)(int, int, int, _DWORD))unk_78610880)(v2, v94 + 1, v51, 0);
            ((void (__fastcall *)(_DWORD, _DWORD, _DWORD, _DWORD))unk_7860E8D6)(v2, v52, 0, v50);
            for ( m = 0; m < v94; ((void (__fastcall *)(_DWORD, _DWORD, _DWORD, _DWORD))unk_7860E8D6)(v2, v52, m, v54) )
              v54 = ((int (__fastcall *)(_DWORD, _DWORD, int))unk_7860E8C8)(v2, v98, m++);
            v55 = *(_DWORD *)(v8 + 212);
            v56 = *(const char **)(v8 + 216);
            v57 = *(_DWORD *)(v8 + 220);
            v58 = v104;
            v59 = v2;
LABEL_88:
            ((void (__fastcall *)(int, int, int, const char *))unk_7860EEFC)(v59, v58, v55, v56);
            dword_78626714 = (*(int (__fastcall **)(int, int))(*(_DWORD *)v2 + 84))(v2, v93);
            log(3, (int)"SecShell", (int)&unk_78622BD8);
            ((void (__fastcall *)(char *))unk_786124A4)(&v112);
            ((void (__fastcall *)(char *))unk_786124A4)(&v110);
            goto LABEL_105;
          }
          v48 = J_NewObjectV(v2, v46, v49, 0);
        }
        v50 = v48;
        goto LABEL_84;
      }
      *(_BYTE *)(v37 + 4) = 1;
      *(_DWORD *)(v37 + 8) = v43;
      if ( v45 == 10 )
        *(_DWORD *)(v84 + 16) = v103;
    }
    *(_DWORD *)(v37 + 12) = 0;
    goto LABEL_75;
  }
  log(3, (int)"SecShell", (int)"wrong code");
  v34 = ((int (__fastcall *)(const char *, signed int))unk_7861EDBC)("/dev/zero", 2);
  v35 = ((int (__cdecl *)(_DWORD, signed int))unk_7861EEEC)(0, v87);
  ((void (__fastcall *)(int))unk_7861EE2C)(v34);
  if ( v35 )
  {
    memmove();
    v103 = v35;
    goto LABEL_50;
  }
  v20 = "SecShell";
  v21 = "mmap fail";
LABEL_48:
  log(3, (int)v20, (int)v21);
LABEL_105:
  result = v107;
  if ( v117 != *v107 )
    _stack_chk_fail(v107);
  return result;
}
*ClassLoader*loadDex(), *multidex 这些关键字是不是想到了什么 ?

Surprise

其实 QEver 的脚本可以直接 dump 出加载后正确完整的 dex,不知道为啥乐固没想到这一点。