Frida特征对抗案例1

警告
本文最后更新于 2023-08-18,文中内容可能已过时。
  • tv.danmaku.bili 7.43.0
  • pixel2 android10.0
  • frida 14.2.2

使用frida以spawn模式启动,可以发现进程直接崩溃,说明存在反调试

1
2
3
Spawned `tv.danmaku.bili`. Resuming main thread!
[Pixel 2::tv.danmaku.bili]-> Process terminated
[Pixel 2::tv.danmaku.bili]->

通常检测逻辑是放在native层的,因此进一步判断是哪个so导致的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
function hook_dlopen() {
    Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
        {
            onEnter: function (args) {
                var pathptr = args[0];
                if (pathptr !== undefined && pathptr != null) {
                    var path = ptr(pathptr).readCString();
                    console.log("load " + path);
                }
            }
        }
    );
}

由so的加载流程可知,so都是是顺序加载,从命令行中当加载libmsaoaidsec.so之后,进程就崩溃了,可以猜测反调试点在libmsaoaidsec.so中

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[Pixel 2::tv.danmaku.bili]-> load /system/framework/oat/arm/com.android.future.usb.accessory.odex
load /system/framework/oat/arm/org.apache.http.legacy.odex
load /data/app/tv.danmaku.bili-j_iiq65L9CsVGLfbrhaTgA==/oat/arm/base.odex
load /data/app/tv.danmaku.bili-j_iiq65L9CsVGLfbrhaTgA==/lib/arm/libblkv.so
load /data/app/tv.danmaku.bili-j_iiq65L9CsVGLfbrhaTgA==/lib/arm/libbili_core.so
load /data/app/tv.danmaku.bili-j_iiq65L9CsVGLfbrhaTgA==/lib/arm/libbilicr.88.0.4324.188.so
load /data/app/tv.danmaku.bili-j_iiq65L9CsVGLfbrhaTgA==/lib/arm/libijkffmpeg.so
load /data/app/tv.danmaku.bili-j_iiq65L9CsVGLfbrhaTgA==/lib/arm/libavif-jni.so
load /data/dalvik-cache/arm/system@product@app@TrichromeLibrary@TrichromeLibrary.apk@classes.dex
load /data/dalvik-cache/arm/system@product@app@WebViewGoogle@WebViewGoogle.apk@classes.dex
load /data/app/tv.danmaku.bili-j_iiq65L9CsVGLfbrhaTgA==/lib/arm/libmsaoaidsec.so
Process terminated

而libmsaoaidsec.so从字面上可知是MSA(移动安全联盟)出品的,确定了so之后,需要进一步确定具体的函数,so的函数执行顺序是.init函数->JNI_OnLoad,先判断下是在JNI_OnLoad前后进行检测的

从libmsaoaidsec.so的export函数表中可以知道JNI_OnLoad的偏移量是0xC6DC,先hook JNI_OnLoad尝试下

 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
function hook_dlopen(soName = '') {
    Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
        {
            onEnter: function (args) {
                var pathptr = args[0];
                if (pathptr !== undefined && pathptr != null) {
                    var path = ptr(pathptr).readCString();
                    if (path.indexOf(soName) >= 0) {
                        this.is_can_hook = true;
                    }
                }
            },
            onLeave: function (retval) {
                if (this.is_can_hook) {
                    hook_JNI_OnLoad()
                }
            }
        }
    );
}
 
function hook_JNI_OnLoad(){
    let module = Process.findModuleByName("libmsaoaidsec.so")
    Interceptor.attach(module.base.add(0xC6DC + 1), {
        onEnter(args){
            console.log("call JNI_OnLoad")
        }
    })
}
 
setImmediate(hook_dlopen, "libmsaoaidsec.so")

结果依旧是进程崩溃

1
2
3
Spawned `tv.danmaku.bili`. Resuming main thread!                        
[Pixel 2::tv.danmaku.bili]-> Process terminated
[Pixel 2::tv.danmaku.bili]->

那么可以断定检测位置在JNI_OnLoad之前,因此需要hook .init函数,选取.init_proc中的外部函数引用来做入口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
int sub_B1B4()
{
  _DWORD *v0; // r5
  int result; // r0
  int v2; // r1
  int v3; // [sp+0h] [bp-20h]
  int v4; // [sp+4h] [bp-1Ch]
  int v5; // [sp+Ch] [bp-14h]

  v0 = off_1FC04;
  v5 = *(_DWORD *)off_1FC04;
  v4 = 0;
  v3 = 0;
  // 选取
  _system_property_get("ro.build.version.sdk", &v3);
  result = atoi((const char *)&v3);
  v2 = *v0 - v5;
  return result;
}

以pthread_create函数为例,尝试下是否有启动线程来做检测

 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
43
44
45
46
47
function hook_dlopen(soName = '') {
    Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
        {
            onEnter: function (args) {
                var pathptr = args[0];
                if (pathptr !== undefined && pathptr != null) {
                    var path = ptr(pathptr).readCString();
                    if (path.indexOf(soName) >= 0) {
                        locate_init()
                    }
                }
            }
        }
    );
}
 
function locate_init() {
    let secmodule = null
    Interceptor.attach(Module.findExportByName(null, "__system_property_get"),
        {
            // _system_property_get("ro.build.version.sdk", v1);
            onEnter: function (args) {
                secmodule = Process.findModuleByName("libmsaoaidsec.so")
                var name = args[0];
                if (name !== undefined && name != null) {
                    name = ptr(name).readCString();
                    if (name.indexOf("ro.build.version.sdk") >= 0) {
                        hook_pthread_create()
                    }
                }
            }
        }
    );
}

function hook_pthread_create(){
    var base = Process.findModuleByName("libmsaoaidsec.so").base
    console.log("libmsaoaidsec.so --- " + base)
    Interceptor.attach(Module.findExportByName("libc.so", "pthread_create"),{
        onEnter(args){
            let func_addr = args[2]
            console.log("The thread function address is " + func_addr + " offset:" + (func_addr-base).toString(16))
        }
    })
}
 
setImmediate(hook_dlopen, "libmsaoaidsec.so")

可以看到,创建了两个线程,分别是在0x11129和0x10975

1
2
3
4
5
6
Spawned `tv.danmaku.bili`. Resuming main thread!                        
[Pixel 2::tv.danmaku.bili]-> libmsaoaidsec.so --- 0xb3689000
The thread function address is 0xeab3fc15 offset:374b6c15
The thread function address is 0xeab3fc15 offset:374b6c15
The thread function address is 0xb369a129 offset:11129
The thread function address is 0xb3699975 offset:10975

继续通过ida来确定反调试检测

1
2
3
4
5
6
7
8
9
while ( 1 )
  {
    v49 = sub_10BE8();
    v50 = sub_10D1C(v49);
    v51 = sub_10DEC(v50);
    sub_16B48(v51);
    // 每4秒触发
    sleep(4u);
  }

0x11129前面大部分都是关于字符串的解密,在while中才到了具体的检测逻辑

 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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
int sub_10BE8()
{
  char *v0; // r4
  _DWORD *v1; // r5
  DIR *v2; // r0
  DIR *v3; // r6
  struct dirent *v4; // r0
  DIR *v5; // r5
  struct dirent *v6; // r7
  const char *v7; // r7
  int v8; // r0
  int v9; // r7
  unsigned int v10; // r4
  int result; // r0
  _DWORD *v12; // [sp+0h] [bp-438h]
  char *v13; // [sp+4h] [bp-434h]
  char v14; // [sp+18h] [bp-420h]
  char buf; // [sp+1Ch] [bp-41Ch]
  char haystack[512]; // [sp+20h] [bp-418h]
  char s; // [sp+220h] [bp-218h]

  v0 = &v14;
  v1 = off_1FC04;
  *(_DWORD *)&v14 = *(_DWORD *)off_1FC04;
  v2 = opendir(&byte_21018);                    // /proc/self/task
  if ( v2 )
  {
    v3 = v2;
    v12 = v1;
    v13 = &v14;
    v4 = readdir(v2);                           // 遍历读取
    v5 = v3;
    if ( v4 )
    {
      v6 = v4;
      do
      {
        _aeabi_memclr4(&s, 512);
        v7 = &v6->d_name[8];
        if ( strcmp(v7, ".") )
        {
          if ( strcmp(v7, "..") )
          {
            snprintf(&s, 0x200u, &byte_21028, v7, v12, v13);
            v5 = v3;
            v8 = openat(-100, &s, 0x80000, 0);
            if ( v8 )
            {
              v9 = v8;
              _aeabi_memclr4(haystack, 512);
              v10 = 0;
              do
              {
                if ( read(v9, &buf, 1u) != 1 )
                  break;
                if ( buf == 10 )
                  break;
                haystack[v10++] = buf;
              }
              while ( v10 < 0x1FF );
              if ( strstr(haystack, &byte_21042) || strstr(haystack, &byte_2104E) )// 搜索gum-js-loop和gmain
                goto LABEL_17;                  // 如果存在的话就exit退出进程
              close(v9);
            }
          }
        }
        v6 = readdir(v5);
      }
      while ( v6 );
    }
    closedir(v5);
    v0 = v13;
    v1 = v12;
  }
  result = *v1 - *(_DWORD *)v0;
  if ( *v1 != *(_DWORD *)v0 )
LABEL_17:
    exit(0);
  return result;
}
 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
43
44
45
46
47
48
49
50
51
int sub_10D1C()
{
  _DWORD *v0; // r6
  DIR *v1; // r0
  DIR *v2; // r4
  struct dirent *v3; // r0
  struct dirent *v4; // r7
  int result; // r0
  int v6; // [sp+14h] [bp-48Ch]
  char v7; // [sp+18h] [bp-488h]
  int v8; // [sp+28h] [bp-478h]
  char path; // [sp+84h] [bp-41Ch]
  char buf; // [sp+284h] [bp-21Ch]

  v0 = off_1FC04;
  v6 = *(_DWORD *)off_1FC04;
  v1 = opendir(&byte_21054);                    // /proc/self/fd
  if ( v1 )
  {
    v2 = v1;
    v3 = readdir(v1);
    if ( v3 )
    {
      v4 = v3;
      while ( 1 )
      {
        _aeabi_memclr4(&buf, 512);
        _aeabi_memclr4(&path, 512);
        snprintf(&path, 0x200u, "/proc/self/fd/%s", &v4->d_name[8]);
        lstat(&path, (struct stat *)&v7);
        if ( (v8 & 0xF000) == 40960 )
        {
          readlink(&path, &buf, 0x200u);        // 读取文件指向
          if ( strstr(&buf, &byte_21062) )      // linjector
            break;
        }
        v4 = readdir(v2);
        if ( !v4 )
          goto LABEL_7;
      }
LABEL_9:
      exit(0);
    }
LABEL_7:
    closedir(v2);
  }
  result = *v0 - v6;
  if ( *v0 != v6 )
    goto LABEL_9;
  return result;
}
  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
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
char *sub_10DEC()
{
  ......

  v31 = off_1FC04;
  v0 = *(_DWORD *)off_1FC04;
  v32 = &v38;
  *(_DWORD *)&v38 = v0;
  v50 = 0;
  v47 = 0;
  v46 = 0;
  v48 = &v46;
  v37 = &v46;
  v49 = &v46;
  v1 = fopen(&byte_2106C, "r");                 // 读取maps
  if ( v1 )
  {
    v2 = v1;
    v56 = 0;
    v57 = 0;
    v58 = 0;
    v55 = 0;
    v54 = 0;
    v53 = 0;
    _aeabi_memclr4(&haystack, 4096);
    if ( !feof(v2) )
    {
      v36 = (_DWORD **)-12;
      v3 = &byte_2107C;
      v34 = (char *)off_1FC20;
      v35 = &byte_2107C;
      do
      {
        if ( !fgets((char *)&v25 + (_DWORD)&stru_1084 + 4, 4096, v2) )
          break;
        v29 = &haystack;
        v28 = &v42;
        v27 = (int *)((char *)&v25 + (_DWORD)&stru_1064 + 12);
        v26 = &v41;
        v25 = &v56;
        if ( sscanf(
               (const char *)&v25 + (_DWORD)&stru_1084 + 4,
               "%lx-%lx %s %lx %s %ld %s",
               &v44,
               &v43,
               &v56,
               &v41,
               (char *)&v25 + (_DWORD)&stru_1064 + 12,
               &v42,
               &haystack) == 7                  // 遍历读取每行内容
          && strstr(&haystack, v3)              // 是否存在/data/local/tmp的文件映射
          && strchr((const char *)&v25 + (_DWORD)&stru_1074 + 8, 120)
          && strchr((const char *)&v25 + (_DWORD)&stru_1074 + 8, 114) )
        {
          sub_187A8(&v40, &haystack, (int)&v39);

          std::_Rb_tree<std::string,std::string,std::_Identity<std::string>,std::less<std::string>,std::allocator<std::string>>::_M_insert_unique<std::string>(
            &v51,
            &v45,
            &v40);                              // 如果存在文件特征,插入数组
          v4 = (char *)v36 + v40;
          if ( (char *)v36 + v40 != v34 && sub_4D00((int)(v4 + 8), (int)v36 + 11, 4) <= 0 )
            sub_18680(v4);
          v3 = v35;
        }
      }
      while ( !feof(v2) );
    }
    fclose(v2);
  }
  v5 = (int)v48;
  if ( v48 == v37 )
  {
LABEL_35:
    std::_Rb_tree<std::string,std::string,std::_Identity<std::string>,std::less<std::string>,std::allocator<std::string>>::_M_erase(
      &v45,
      v47);
    v15 = 0;
    v47 = 0;
    v48 = v37;
    v49 = v37;
    v50 = 0;
  }
  else
  {
    v33 = -12;
    v35 = &byte_2108C;
    v34 = &byte_21097;
    while ( 1 )
    {
      v6 = operator new(0xB4u);
      _aeabi_memclr4(v6 + 56, 41);
      *(_DWORD *)v6 = 0;
      *(_DWORD *)(v6 + 4) = 0;
      *(_DWORD *)(v6 + 8) = 0;
      *(_DWORD *)(v6 + 12) = 0;
      *(_DWORD *)(v6 + 16) = 0;
      *(_DWORD *)(v6 + 20) = 0;
      *(_DWORD *)(v6 + 24) = 0;
      *(_DWORD *)(v6 + 28) = 0;
      _aeabi_memclr4(v6 + 100, 80);
      v7 = *(const char **)(v5 + 16);
      *(_DWORD *)(v6 + 176) = 0;
      if ( !sub_EBC0((void **)v6, v7) || !sub_E7F0((_DWORD *)(v6 + 12), *(_DWORD *)v6) )
        break;
      if ( sub_E6C0((_DWORD *)(v6 + 12), v35, v34) )// _AGENT_1.0 frida-agent
        goto LABEL_52;
      v8 = *(FILE **)(v6 + 8);
      if ( v8 )
        fclose(v8);
      ......
LABEL_52:

    exit(0);
  return "_class_type_infoEPKvRNS1_15__upcast_resultE";
}
 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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
int sub_16B48()
{
  _DWORD *v0; // r5
  int *v1; // r6
  unsigned int v2; // r5
  char *v3; // r4
  int v4; // r0
  _DWORD *v6; // [sp+8h] [bp-40h]
  int v7; // [sp+Ch] [bp-3Ch]
  int v8; // [sp+10h] [bp-38h]
  int v9; // [sp+14h] [bp-34h]
  int v10; // [sp+18h] [bp-30h]
  int v11; // [sp+1Ch] [bp-2Ch]
  int v12; // [sp+20h] [bp-28h]
  int v13; // [sp+24h] [bp-24h]
  int v14; // [sp+28h] [bp-20h]
  int v15; // [sp+2Ch] [bp-1Ch]
  int v16; // [sp+30h] [bp-18h]

  v0 = off_1FC04;
  v16 = *(_DWORD *)off_1FC04;
  if ( sub_167E0() )                            // 检测libart.so
  {
    v6 = v0;
    v7 = 2013882270;
    v8 = 1107745203;
    v9 = 1185388969;
    v10 = 2013910933;
    v11 = 1186354873;
    v12 = 1208457641;
    v13 = 2013898649;
    v14 = 1178184103;
    v15 = -1448633943;
    LOBYTE(v7) = 7;
    v1 = dword_1D914;
    v2 = 1;
    do
    {
      *((_BYTE *)&v7 + v2) ^= LOBYTE(v1[-3 * (v2 / 3)]);
      ++v1;
      ++v2;
    }
    while ( v2 != 36 );
    v15 = 248;
    v3 = (char *)mmap(0, 0x24u, 7, 34, -1, 0);
    if ( v3 == (char *)-1 )
    {
      v0 = v6;
    }
    else
    {
      _aeabi_memcpy(v3, &v7, 36);               // v7复制到v3
      j___clear_cache(v3, v3 + 36);
      v0 = v6;
      if ( v3 )
      {
        ((void (__fastcall *)(_DWORD))v3)(0);   // 执行v3通过svc exit_group
        v4 = sysconf(40);
        munmap(v3, v4);
      }
    }
  }
  return *v0 - v16;
}

libart.so的检测逻辑

  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
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
signed int sub_167E0()
{
  .....

  v0 = off_1FC04;
  v38 = *(_DWORD *)off_1FC04;
  v1 = dword_22640;
  if ( dword_22640 )
    goto LABEL_25;
  v2 = *off_1FC08;
  if ( (unsigned int)(*off_1FC08 - 21) <= 1 )
  {
    v3 = dlopen("libart.so", 0);                // dlopen libart
    if ( !v3 )
      goto LABEL_24;
    handle = v3;
    v25 = v0;
    s = -1427636794;
    v27 = -1762796602;
    v28 = -858404453;
    v29 = -724511763;
    v30 = -923673150;
    v31 = -403186483;
    v32 = -190842678;
    v33 = -924066866;
    v34 = -605642533;
    v35 = -305337619;
    v36 = -486684977;
    v37 = 203;
    v4 = strlen((const char *)&s);
    if ( v4 >= 1 )
    {
      LOBYTE(s) = 95;
      if ( v4 != 1 )
      {
        v5 = dword_1D914;
        v6 = (char *)&s + 1;
        v7 = v4 - 1;
        v8 = 1;
        do
        {
          *v6 ^= LOBYTE(v5[-3 * (v8 / 3)]);
          ++v6;
          --v7;
          ++v5;
          ++v8;
        }
        while ( v7 );
      }
    }
    dword_22640 = (int)dlsym(handle, (const char *)&s);// 获取_ZN3art9ArtMethod12PrettyMethodEb的地址
    dlclose(handle);
    goto LABEL_23;
  }
  if ( (unsigned int)(v2 - 23) <= 2 )
  {
    v9 = sub_EFEC((int)"libart.so");
    if ( !v9 )
      goto LABEL_24;
    handlea = (void *)v9;
    v25 = v0;
    s = -1427636794;
    v27 = -1762796602;
    v28 = -858404453;
    v29 = -724511763;
    v30 = -923673150;
    v31 = -403186483;
    v32 = -661587766;
    v33 = -1026236971;
    v34 = -842468899;
    BYTE2(v35) = 0;
    LOWORD(v35) = -14884;
    v10 = strlen((const char *)&s);             // _ZN3art12PrettyMethodEPNS%
    if ( v10 >= 1 )
    {
      LOBYTE(s) = 95;
      if ( v10 != 1 )
      {
        v11 = dword_1D914;
        v12 = (char *)&s + 1;
        v13 = v10 - 1;
        v14 = 1;
        do
        {
          *v12 ^= LOBYTE(v11[-3 * (v14 / 3)]);
          ++v12;
          --v13;
          ++v11;
          ++v14;
        }
        while ( v13 );
      }
    }
    goto LABEL_22;
  }
  if ( v2 >= 26 )
  {
    v15 = sub_EFEC((int)"libart.so");
    if ( v15 )
    {
      handlea = (void *)v15;
      v25 = v0;
      s = -1427636794;
      v27 = -1628578874;
      v28 = -455873560;
      v29 = -155069444;
      v30 = -139749181;
      v31 = -573309733;
      v32 = -305337632;
      v33 = -486684977;
      LOWORD(v34) = 203;
      v16 = strlen((const char *)&s);
      if ( v16 >= 1 )
      {
        LOBYTE(s) = 95;
        if ( v16 != 1 )
        {
          v17 = dword_1D914;
          v18 = (char *)&s + 1;
          v19 = v16 - 1;
          v20 = 1;
          do
          {
            *v18 ^= LOBYTE(v17[-3 * (v20 / 3)]);
            ++v18;
            --v19;
            ++v17;
            ++v20;
          }
          while ( v19 );
        }
      }
LABEL_22:
      dword_22640 = sub_F118((int)handlea, (int)&s);
      sub_F15C(handlea);
LABEL_23:
      v0 = v25;
      goto LABEL_24;
    }
  }
LABEL_24:
  v1 = dword_22640;
  if ( !dword_22640 )
  {
LABEL_26:
    result = 0;
    goto LABEL_27;
  }
LABEL_25:
  result = 1;
  if ( *(_DWORD *)(v1 & 0xFFFFFFFE) != -268371745 )// 判断函数的首个指令是否为0xF000F8DF对应的是LDR PC, [PC, #0]
    goto LABEL_26;
LABEL_27:
  v22 = *v0 - v38;
  return result;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
void __noreturn sub_10974()
{
  int v0; // r0

  while ( 1 )
  {
    v0 = sub_1041C();
    if ( v0 == -1 || v0 && !sub_1025C(v0) || sub_10850() == 777 )
      // 异常触发
      sub_AF84();
    // 每十秒检测一次
    sleep(0xAu);
  }
}

可以看到存在三个检测方法:sub_1041C、sub_1025C、sub_10850,检测逻辑是每十秒触发一次

  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
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
int sub_1041C()
{
  signed int v0; // r0
  int *v1; // r7
  char *v2; // r4
  int v3; // r5
  unsigned int v4; // r6
  signed int v5; // r4
  FILE *v6; // r0
  signed int v7; // r0
  int *v8; // r4
  char *v9; // r6
  int v10; // r5
  unsigned int v11; // r7
  char *v12; // r0
  int v13; // r0
  int result; // r0
  FILE *stream; // [sp+4h] [bp-84Ch]
  _DWORD *v16; // [sp+8h] [bp-848h]
  int v17; // [sp+18h] [bp-838h]
  char s[4]; // [sp+1Ch] [bp-834h]
  int v19; // [sp+20h] [bp-830h]
  __int16 v20; // [sp+24h] [bp-82Ch]
  char v21; // [sp+26h] [bp-82Ah]
  char haystack; // [sp+28h] [bp-828h]
  int v23; // [sp+428h] [bp-428h]
  int v24; // [sp+42Ch] [bp-424h]
  int v25; // [sp+430h] [bp-420h]
  int v26; // [sp+434h] [bp-41Ch]
  int v27; // [sp+438h] [bp-418h]

  v16 = off_1FC04;
  v17 = *(_DWORD *)off_1FC04;
  _aeabi_memclr4(&v27, 1024);
  v26 = 14340845;
  v25 = -925635962;
  v24 = -1011054908;
  v23 = -153364554;
  v0 = strlen((const char *)&v23);
  if ( v0 >= 1 )
  {
    LOBYTE(v23) = 47;
    if ( v0 != 1 )
    {
      v1 = dword_1D87C;
      v2 = (char *)&v23 + 1;
      v3 = v0 - 1;
      v4 = 1;
      do
      {
        *v2 ^= LOBYTE(v1[-3 * (v4 / 3)]);
        ++v2;
        --v3;
        ++v1;
        ++v4;
      }
      while ( v3 );
    }
  }
  v5 = -1;
  sprintf((char *)&v27, (const char *)&v23, dword_2100C);
  v6 = fopen((const char *)&v27, "r");          // 读取/proc/30087/status获取指针
  if ( v6 )
  {
    stream = v6;
    _aeabi_memclr4(&haystack, 1024);
    v21 = 0;
    v20 = (unsigned int)&off_A3CC + 1;
    v19 = -825631806;
    *(_DWORD *)s = -87501363;
    v7 = strlen(s);
    if ( v7 >= 1 )
    {
      s[0] = 84;
      if ( v7 != 1 )
      {
        v8 = dword_1D87C;
        v9 = &s[1];
        v10 = v7 - 1;
        v11 = 1;
        do
        {
          *v9 ^= LOBYTE(v8[-3 * (v11 / 3)]);
          ++v9;
          --v10;
          ++v8;
          ++v11;
        }
        while ( v10 );
      }
    }
    while ( fgets(&haystack, 1024, stream) )
    {
      v12 = strstr(&haystack, s);               // 在status内容中搜索TracerPid:
      if ( v12 )
      {
        v13 = atoi(v12 + 10);                   // 获取TracerPid值
        if ( v13 )
        {
          v5 = v13;
          fclose(stream);
          goto LABEL_15;
        }
        break;
      }
    }
    fclose(stream);
    v5 = 0;
  }
LABEL_15:
  result = *v16 - v17;                          // 返回TracerPid
  if ( *v16 == v17 )
    result = v5;
  return result;
}

sub_1041C函数主要是为了获取TracerPid

  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
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
int __fastcall sub_1025C(int a1)
{
  int v1; // r5
  signed int v2; // r2
  signed int v3; // r0
  int *v4; // r6
  char *v5; // r5
  int v6; // r4
  unsigned int v7; // r7
  FILE *v8; // r0
  signed int v9; // r0
  int *v10; // r7
  char *v11; // r4
  int v12; // r5
  unsigned int v13; // r6
  char *v14; // r0
  int result; // r0
  int v16; // [sp+4h] [bp-84Ch]
  FILE *stream; // [sp+8h] [bp-848h]
  _DWORD *v18; // [sp+10h] [bp-840h]
  int v19; // [sp+1Ch] [bp-834h]
  char s[4]; // [sp+20h] [bp-830h]
  __int16 v21; // [sp+24h] [bp-82Ch]
  char haystack; // [sp+28h] [bp-828h]
  int v23; // [sp+428h] [bp-428h]
  int v24; // [sp+42Ch] [bp-424h]
  int v25; // [sp+430h] [bp-420h]
  int v26; // [sp+434h] [bp-41Ch]
  int v27; // [sp+438h] [bp-418h]

  v1 = a1;
  v18 = off_1FC04;
  v19 = *(_DWORD *)off_1FC04;
  _aeabi_memclr4(&v27, 1024);
  v26 = 14340845;
  v25 = -925635962;
  v24 = -1011054908;
  v23 = -153364554;
  v2 = 0;
  if ( v1 != -1 )
  {
    v16 = v1;
    v3 = strlen((const char *)&v23);
    if ( v3 >= 1 )
    {
      LOBYTE(v23) = 47;
      if ( v3 != 1 )
      {
        v4 = dword_1D87C;
        v5 = (char *)&v23 + 1;
        v6 = v3 - 1;
        v7 = 1;
        do
        {
          *v5 ^= LOBYTE(v4[-3 * (v7 / 3)]);
          ++v5;
          --v6;
          ++v4;
          ++v7;
        }
        while ( v6 );
      }
    }
    sprintf((char *)&v27, (const char *)&v23, v16);
    v8 = fopen((const char *)&v27, "r");        // 同理获取/proc/TracerPid/status
    v2 = 0;
    if ( v8 )
    {
      stream = v8;
      _aeabi_memclr4(&haystack, 1024);
      v21 = 157;
      *(_DWORD *)s = -37685303;                 // ppid
      v9 = strlen(s);
      if ( v9 >= 1 )
      {
        s[0] = 80;
        if ( v9 != 1 )
        {
          v10 = dword_1D87C;
          v11 = &s[1];
          v12 = v9 - 1;
          v13 = 1;
          do
          {
            *v11 ^= LOBYTE(v10[-3 * (v13 / 3)]);
            ++v11;
            --v12;
            ++v10;
            ++v13;
          }
          while ( v12 );
        }
      }
      do
      {
        if ( !fgets(&haystack, 1024, stream) )
          goto LABEL_14;
        v14 = strstr(&haystack, s);
      }                                         // 获取ppid值
      while ( !v14 );
      if ( atoi(v14 + 5) == dword_2100C )       // 比较ppid和当前进程
      {
LABEL_14:
        fclose(stream);
        v2 = 1;
        goto LABEL_16;
      }
      fclose(stream);
      v2 = 0;
    }
  }
LABEL_16:
  result = *v18 - v19;
  if ( *v18 == v19 )
    result = v2;
  return result;
}

sub_1025C的作用是检测TracerPid的ppid是否是当前进程

 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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
int sub_10850()
{
  char *v0; // r6
  _DWORD *v1; // r7
  __pid_t v2; // r0
  DIR *v3; // r5
  signed int v4; // r4
  struct dirent *v5; // r0
  int v6; // r1
  int v7; // r6
  char *v8; // r0
  __pid_t v9; // r0
  int v10; // r6
  char *v11; // r0
  int v12; // r7
  int result; // r0
  int v15; // [sp+0h] [bp-A28h]
  _DWORD *v16; // [sp+4h] [bp-A24h]
  char *v17; // [sp+8h] [bp-A20h]
  char v18; // [sp+Ch] [bp-A1Ch]
  char buf; // [sp+10h] [bp-A18h]
  int v20; // [sp+410h] [bp-618h]

  v0 = &v18;
  v1 = off_1FC04;
  *(_DWORD *)&v18 = *(_DWORD *)off_1FC04;
  _aeabi_memclr4((char *)&v15 + (_DWORD)&stru_804 + 12, 512);
  _aeabi_memclr4(&v20, 1024);
  _aeabi_memclr4(&buf, 1024);
  v2 = getpid();
  sprintf((char *)&v20, "/proc/%d/task", v2);   // 获取当前进程的task目录
  v3 = opendir((const char *)&v20);             // 获取目录指针
  v4 = -1;
  if ( v3 )
  {
    v16 = v1;
    v17 = &v18;
LABEL_3:
    while ( 1 )
    {
      v5 = readdir(v3);                         // 遍历读取文件

      if ( !v5 )
        break;
      LOBYTE(v6) = v5->d_name[8];
      v7 = (int)&v5->d_name[8];
      if ( v5->d_name[8] )
      {
        v8 = &v5->d_name[9];
        while ( (unsigned int)(unsigned __int8)v6 - 48 > 9 )
        {
          v6 = (unsigned __int8)*v8++;
          if ( !v6 )
            goto LABEL_3;
        }
        v9 = getpid();
        sprintf((char *)&v15 + (_DWORD)&stru_804 + 12, "/proc/%d/task/%s/stat", v9, v7);// 获取主进程信息
        v10 = open((const char *)&v15 + (_DWORD)&stru_804 + 12, 0);
        if ( v10 == -1 || read(v10, &buf, 0x400u) == -1 )
          goto LABEL_18;
        v11 = &buf;
        do
          v12 = (int)(v11 + 1);
        while ( *v11++ != 41 );                 // 读取长度41的内容
        close(v10);
        if ( (*(unsigned __int8 *)(v12 + 1) | 0x20) == 116 && *(_BYTE *)(v12 + 2) == 32 )// 判断status状态
        {
          v4 = 777;
          goto LABEL_18;
        }
      }
    }
    closedir(v3);
    v4 = 0;
LABEL_18:
    v0 = v17;
    v1 = v16;
  }
  result = *v1 - *(_DWORD *)v0;
  if ( *v1 == *(_DWORD *)v0 )
    result = v4;
  return result;
}

sub_10850函数功能是判断当前进程的state状态,为T的时候则表示进程处于被调试状态

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
int sub_AF84()
{
  _DWORD *v0; // r4
  int v1; // ST04_4

  v0 = off_1FC04;
  v1 = *(_DWORD *)off_1FC04;
  sub_F758();
  sub_E498();
  //调用svc exit_group退出
  sub_15C48(0); 
  return *v0 - v1;
}
 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
43
44
45
46
47
48
49
#include <algorithm>
#include <cstring>
#include<iomanip>
#include <iostream>
#include <ostream>
using namespace std;

int main()
{
   size_t v0;
   // 设置字节数 1或者4 当数据元素为4个字节时设1,为1个字节时设4
   // int type = 4;
   // unsigned int v21[18] = {
   //    0xFF,          
   //    0xD5,
   //    0xC0,
   //    0xFD,
   //    0xC6,
   //    0x84,
   //    0xF8,
   //    0xC0,
   //    0xCC,
   //    0xF7,
   //    0xD3,
   //       0,
   //       0
   // };
   int type = 1;
   unsigned int v21[18] = {
      0x78096707,          
      0x4206D9B3,
      0x46A799A9,
      0x7809D795,
      0x46B656B9,
      0x480799A9,
      0x7809A799,
      0x4639A9A7,
      0xA9A799A9
   };
   unsigned char* decode = reinterpret_cast<unsigned char*>(v21);
   v0 = strlen((const char *)decode);
   cout<<v0<<endl;
   unsigned short temp[] = {0x99,0xA7,0xA9};
   for(int i=0;i<80;i+=type){
      unsigned short j = (unsigned short)*(decode +i);
      unsigned short k = temp[i%3];
      cout<<(unsigned char)(j^k);
   }
}
 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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
function hook_dlopen(soName = '') {
    Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
        {
            onEnter: function (args) {
                var pathptr = args[0];
                if (pathptr !== undefined && pathptr != null) {
                    var path = ptr(pathptr).readCString();
                    if (path.indexOf(soName) >= 0) {
                        locate_init()
                    }
                }
            }
        }
    );
}
 
function locate_init() {
    let secmodule = null
    Interceptor.attach(Module.findExportByName(null, "__system_property_get"),
        {
            // _system_property_get("ro.build.version.sdk", v1);
            onEnter: function (args) {
                secmodule = Process.findModuleByName("libmsaoaidsec.so")
                var name = args[0];
                if (name !== undefined && name != null) {
                    name = ptr(name).readCString();
                    if (name.indexOf("ro.build.version.sdk") >= 0) {
                        // 这是.init_proc刚开始执行的地方,是一个比较早的时机点
                        // do something
                        // hook_pthread_create()
                        bypass()
                    }
                }
            }
        }
    );
}
 
function hook_pthread_create(){
    var base = Process.findModuleByName("libmsaoaidsec.so").base
    console.log("libmsaoaidsec.so --- " + base)
    Interceptor.attach(Module.findExportByName("libc.so", "pthread_create"),{
        onEnter(args){
            let func_addr = args[2]
            console.log("The thread function address is " + func_addr + " offset:" + (func_addr-base).toString(16))
        }
    })
}
 
function nop(addr) {
    Memory.patchCode(ptr(addr), 4, code => {
        const cw = new ThumbWriter(code, { pc: ptr(addr) });
        cw.putNop();
        cw.putNop();
        cw.flush();
    });
}
 
function bypass(){
    let module = Process.findModuleByName("libmsaoaidsec.so")
    nop(module.base.add(0x10ADE))
    nop(module.base.add(0x113F2))
}
 
setImmediate(hook_dlopen, "libmsaoaidsec.so")

从上面的检测方式可以大概总结如下:

  • frida特征
  • inlinehoook特征
  • trace特征

通常都是通过strstr来做判断,但是某些情况,例如验证inlinehook指令时无法直接定位,杀死进程通常都是使用exit或是通过svc exit_group来操作

完整代码看这里libmsaoaidsec.js

相关内容