首页
社区
课程
招聘
[原创]字节黑科技 : HIDE API 调用
发表于: 1天前 747

[原创]字节黑科技 : HIDE API 调用

1天前
747

版本 : 370401

ANDROID 版本 : API 36

主动调用

字节绕过 HIDE API 的模块是 libjato.so, 导出函数是 Java_com_bytedance_common_jato_boost_HiddenApiOpt_nOptimize,将以下的 SO 模块打包进 ANDROID 的项目:

1
libart_sym.so  libbytehook.so  libc++_shared.so  libjato.so  libnpth_dl.so  libshadowhook.so

HiddenApiOpt 的类代码如下:

1
2
3
4
5
6
7
8
9
10
11
package com.bytedance.common.jato.boost;
 
public class HiddenApiOpt {
     
    public static native boolean nOptimize();
 
    static {
        System.loadLibrary("jato");
    }
 
}

测试使用 @hide 注解的函数是 setTargetSdkVersion 源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
    * Sets the target SDK version. Should only be called before the
    * app starts to run, because it may change the VM's behavior in
    * dangerous ways. Defaults to {@link #SDK_VERSION_CUR_DEVELOPMENT}.
    *
    * @param targetSdkVersion the SDK version the app wants to run with.
    *
    * @hide
    */
@UnsupportedAppUsage(maxTargetSdk=0, publicAlternatives="Use the {@code targetSdkVersion}"
    +" attribute in the {@code uses-sdk} manifest tag instead.")
@SystemApi(client = MODULE_LIBRARIES)
public synchronized void setTargetSdkVersion(int targetSdkVersion) {
    this.targetSdkVersion = targetSdkVersion;
    setTargetSdkVersionNative(this.targetSdkVersion);
}

调用代码如下:

1
2
3
4
5
6
7
8
9
10
11
public void VMRuntimeSetTargetSdkVersion(){
    try {
        Class runtimeClass = Class.forName("dalvik.system.VMRuntime");
        Method nativeLoadMethod = runtimeClass.getDeclaredMethod("setTargetSdkVersion",
                new Class[] {int.class});
 
        Log.d("hiddenapi", "setTargetSdkVersion success!");
    } catch (Exception e) {
        e.printStackTrace();
    }
}

执行 VMRuntimeSetTargetSdkVersion 后的报错信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
hiddenapi: Accessing hidden method Ldalvik/system/VMRuntime;->setTargetSdkVersion(I)V (runtime_flags=CorePlatformApi, domain=core-platform, api=blocked,core-platform-api) from Lcom/android/hiddenapi/MainActivity; (domain=app) using reflection: denied
java.lang.NoSuchMethodException: dalvik.system.VMRuntime.setTargetSdkVersion [int]
    at java.lang.Class.getMethod(Class.java:2940)
    at java.lang.Class.getDeclaredMethod(Class.java:2919)
    at com.android.hiddenapi.MainActivity.VMRuntimeSetTargetSdkVersion(MainActivity.java:138)
    at com.android.hiddenapi.MainActivity$1.onClick(MainActivity.java:43)
    at android.view.View.performClick(View.java:8083)
    at android.view.View.performClickInternal(View.java:8060)
    at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0)
    at android.view.View$PerformClick.run(View.java:31549)
    at android.os.Handler.handleCallback(Handler.java:995)
    at android.os.Handler.dispatchMessage(Handler.java:103)
    at android.os.Looper.loopOnce(Looper.java:248)
    at android.os.Looper.loop(Looper.java:338)
    at android.app.ActivityThread.main(ActivityThread.java:9067)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:593)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:932)

反射调用被拒绝 : (domain=app) using reflection: denied

使用字节的绕过模块,然后再调用 VMRuntimeSetTargetSdkVersion 显示反射调用成功。

1
2
HiddenApiOpt mHiddenApiOpt = new HiddenApiOpt();
mHiddenApiOpt.nOptimize();

显示日志如下:

1
2
3
4
5
Init libart.so handle: 0xb4000077c46762c0
Load /data/app/~~VGNwNanmQ4UBtavm8SqM-w==/com.android.hiddenapi-VIzkuLnqpv5NAhGt5WmmUw==/base.apk!/lib/arm64-v8a/libjato.so using class loader ns clns-9 (caller=/data/data/com.android.hiddenapi/code_cache/.overlay/base.apk/classes6.dex): ok
 Found the Ldr instruction in GetHiddenApiEnforcementPolicy, target_instruction is 0xb9443d08,  i = 14
Get the offset of enforcement_policy: 43c
setTargetSdkVersion success!

绕过分析

分析 libjato.so 的 Java_com_bytedance_common_jato_boost_HiddenApiOpt_nOptimize 是如何绕过 HIDE API 限制的, 主要代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
v2 = sub_876C4();                           // hook libart GetReflectionCallerAccessContext
v3 = atomic_load((unsigned __int8 *)&qword_15BD30);// 原子读取
if ( (v3 & 1) == 0 && __cxa_guard_acquire(&qword_15BD30) )// 构造加锁
{
    dword_15BD2C = sub_87784();               // GetHiddenApiEnforcementPolicy
    __cxa_guard_release(&qword_15BD30);       // 构造解锁
}
v4 = dword_15BD2C;
if ( dword_15BD2C )
{
    v5 = npth_dlopen("libart.so");
    v6 = (_QWORD *)npth_dlsym(v5, "_ZN3art7Runtime9instance_E");
    if ( v5 )
    npth_dlclose(v5);
    if ( !v6 || (unsigned int)(*(_DWORD *)(*v6 + (unsigned int)dword_15BD2C) - 1) > 1 )
    {
    v0 = v2;
    return v0 & 1;
    }
    *(_DWORD *)((unsigned int)dword_15BD2C + *v6) = 0;// GetHiddenApiEnforcementPolicy + _ZN3art7Runtime9instance_E = 0
    v4 = 1;
}
v0 = v2 | v4;

HOOK

首先分析 sub_876C4 函数:

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
__int64 sub_876C4()
{
  __int64 v0; // x20
  __int64 v1; // x19
  __int64 result; // x0
  __int64 v3; // x0
  const char *v4; // x0
 
  v0 = npth_dlopen("libart.so");
  v1 = npth_dlsym(v0, "_ZN3art9hiddenapi32GetReflectionCallerAccessContextEPNS_6ThreadE");
  if ( v0 )
    npth_dlclose(v0);
  result = 0LL;
  if ( v1 )
  {
    shadowhook_init(0LL, 0LL);
    v3 = shadowhook_hook_sym_addr(v1, sub_874D8, &unk_15BD20);
    if ( (unsigned int)shadowhook_get_errno(v3) )
    {
      v4 = (const char *)shadowhook_to_errmsg();
      __android_log_print(6, "OptimizeHiddenApi", "hook libart GetReflectionCallerAccessContext error, msg: %s", v4);
      return 0LL;
    }
    else
    {
      if ( (sub_73888() & 1) != 0 )
        __android_log_print(3, "OptimizeHiddenApi", " hook libart GetReflectionCallerAccessContext success");
      return 1LL;
    }
  }
  return result;
}

因为 ANDROID 的版本限制, dlopen 是无法直接打开 libart.so 模块的。

在 ibnpth_dl.so 模块中有导出函数 npth_dlopen 和 npth_dlsym。

npth_dlopen

在 npth_dlopen 的函数中,根据 ANDROID 的 API 版本来获取 libart.so 加载模块的基址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int (**sub_39A8())(int (*callback)(struct dl_phdr_info *, size_t, void *), void *data)
{
  n0x14 = npth_dlapilevel_0();
  if ( &dl_iterate_phdr )
  {
    if ( n0x14 - 21 >= 2 )                      // API >= 23
      return &dl_iterate_phdr;
    else
      return (int (**)(int (*)(struct dl_phdr_info *, size_t, void *), void *))sub_3E8C;
  }
  if ( n0x14 > 0x14 )                           // API > 20
    return 0LL;
  d_tag = &sub_3F04;
  if ( n2 == 1 )
    return (int (**)(int (*)(struct dl_phdr_info *, size_t, void *), void *))d_tag;
  if ( n2 == 2 )
    return 0LL;
}

如果 ANDROID API 大于等于 23 ,则调用 dl_iterate_phdr 来遍历所有加载的模块,然后在回调函数中根据模块的名称来获取对应的加载基址,dl_iterate_phdr 的函数原型如下:

1
2
3
4
5
6
7
8
9
10
11
/**
 * [dl_iterate_phdr(3)](e83K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6E0j5h3^5%4i4K6u0W2L8%4u0Y4i4K6u0r3L8r3W2F1N6i4S2Q4x3V1k6E0j5h3&6Q4x3X3c8H3j5h3N6W2M7#2)9J5c8X3#2S2L8U0y4Q4x3V1k6V1L8q4)9#2k6X3W2@1k6i4u0S2N6r3g2Q4y4h3k6H3K9r3c8J5i4K6u0W2x3#2)9J5k6h3S2@1L8h3I4Q4x3U0V1`.
 * calls the given callback once for every loaded shared object. The size
 * argument to the callback lets you determine whether you have a smaller
 * `dl_phdr_info` from before API level 30, or the newer full one.
 * The data argument to the callback is whatever you pass as the data argument
 * to dl_iterate_phdr().
 *
 * Returns the value returned by the final call to the callback.
 */
int dl_iterate_phdr(int (* _Nonnull __callback)(struct dl_phdr_info* _Nonnull __info, size_t __size, void* _Nullable __data), void* _Nullable __data);

如果 ANDROID API 小于 23 , 则读取 /proc/self/maps 的内容,然后解析获取到 libart.so 的基址,解析 /proc/self/maps 内容格式的代码如下:

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
char *__fastcall sub_57A0(unsigned __int64 ptr)
{
  FILE *stream; // x0
  FILE *stream_1; // x19
  const char *s_1; // x0
  char *s_2; // x20
  char *v6; // x0
  unsigned __int64 ptr_2; // [xsp+0h] [xbp-410h] BYREF
  unsigned __int64 ptr_1; // [xsp+8h] [xbp-408h] BYREF
  char s[1024]; // [xsp+10h] [xbp-400h] BYREF
 
  stream = fopen("/proc/self/maps", "r");
  if ( !stream )
    return 0LL;
  stream_1 = stream;
  if ( !fgets(s, 1024, stream) )
    goto LABEL_12;
  while ( 1 )
  {
    if ( sscanf(s, "%lx-%lx r", &ptr_1, &ptr_2) == 2 )
    {
      if ( ptr_1 > ptr )
        goto LABEL_12;
      if ( ptr_2 > ptr )
        break;
    }
    if ( !fgets(s, 1024, stream_1) )
      goto LABEL_12;
  }
  s_1 = (const char *)__strchr_chk(s, 47LL, 1024LL);
  s_2 = (char *)s_1;
  if ( !s_1 )
    goto LABEL_13;
  v6 = strchr(s_1, 32);
  if ( v6 || (v6 = strchr(s_2, 10)) != 0LL )
  {
    *v6 = 0;
    s_2 = strdup(s_2);
  }
  else
  {
LABEL_12:
    s_2 = 0LL;
  }
LABEL_13:
  fclose(stream_1);
  return s_2;
}

npth_dlsym

npth_dlsym 的关键代码如下:

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
_QWORD *__fastcall npth_dlsym_size(_QWORD *ptr, char *s2, _QWORD *a3)
{
  _QWORD *ptr_1; // x19
  __int64 *v6; // x0
  __int64 v7; // x0
  __int64 v8; // x8
  int n6; // w9
 
  if ( !ptr )
    return ptr;
  ptr_1 = ptr;
  v6 = (__int64 *)ptr[6];
  if ( !v6 )
  {
    v6 = (__int64 *)sub_4520(ptr_1[5], ptr_1[4], ptr_1[1]);
    ptr_1[6] = v6;
    if ( !v6 )
    {
      if ( (unsigned int)sub_5BC0() )
        __android_log_print(4LL, "NPTH_DL", "cannot load dynamic sections!");
      return 0LL;
    }
  }
  v7 = sub_4384(v6, s2);
  if ( !v7 )
  {
    if ( (unsigned int)sub_5BC0() )
      __android_log_print(4LL, "NPTH_DL", "cannot found symbol!");
    return 0LL;
  }
  v8 = v7;
  ptr = 0LL;
  n6 = *(_BYTE *)(v8 + 4) & 0xF;
  if ( n6 != 6 && n6 != 10 )
  {
    if ( a3 )
      *a3 = *(_QWORD *)(v8 + 16);
    return (_QWORD *)(*(_QWORD *)(v8 + 8) + ptr_1[1]);
  }
  return ptr;
}

核心的函数有两个 v6 = (__int64 *)sub_4520(ptr_1[5], ptr_1[4], ptr_1[1]); 和 v7 = sub_4384(v6, s2);

其中 sub_4520 函数是动态节 (Dynamic Section) 解析器。

sub_4384 函数是符号查找器。它使用解析出的动态节信息,在符号表中查找指定的符号名。

npth_dlsym 模拟了系统 dlsym 的功能,手动解析 ELF 的格式信息,最终返回函数的指针。

GetReflectionCallerAccessContext

GetReflectionCallerAccessContext 函数的源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
hiddenapi::AccessContext GetReflectionCallerAccessContext(Thread* self)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  // Walk the stack and find the first frame not from java.lang.Class,
  // java.lang.invoke or java.lang.reflect. This is very expensive.
  // Save this till the last.
  ...
  FirstExternalCallerVisitor visitor(self);
  visitor.WalkStack();
  // Construct AccessContext from the calling class found on the stack.
  // If the calling class cannot be determined, e.g. unattached threads,
  // we conservatively assume the caller is trusted.
  ObjPtr<mirror::Class> caller =
      (visitor.caller == nullptr) ? nullptr : visitor.caller->GetDeclaringClass();
  return caller.IsNull() ? AccessContext(/* is_trusted= */ true) : AccessContext(caller);
}

HOOK _ZN3art9hiddenapi32GetReflectionCallerAccessContextEPNS_6ThreadE 代码如下:

1
v3 = shadowhook_hook_sym_addr(v1, sub_874D8, &unk_15BD20);
1
2
3
4
5
6
7
8
9
10
11
12
13
__int64 __usercall sub_874D8@<X0>(_QWORD *a1@<X8>)
{
  __int64 v1; // x30
  __int64 mode; // x0
 
  *a1 = 0LL;
  a1[1] = 0LL;
  a1[2] = 0LL;
  mode = shadowhook_get_mode();
  if ( !(_DWORD)mode )
    return shadowhook_pop_stack(v1);
  return mode;
}

GetReflectionCallerAccessContext 的参数是 Thread 变量, HOOK 函数将 Thread 变量的前 24 个字节数据清零。

当 caller.IsNull() 时 返回了 AccessContext(/* is_trusted= */ true); 从而绕过了 HIDE API 的调用限制。

HiddenApiPolicy

获取 hidden_api_policy_ 字段偏移的代码在 sub_87784() :

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
__int64 sub_87784()
{
  __int64 v0; // x0
  __int64 v1; // x19
  _DWORD *v2; // x0
  __int64 v3; // x21
  bool v4; // cf
  int n706675680; // w11
  unsigned int v6; // w20
  int n8; // w8
  int v8; // w9
  unsigned int v9; // w20
 
  v0 = npth_dlopen_force("libopenjdkjvmti.so");
  if ( v0 )
  {
    v1 = v0;
    v2 = (_DWORD *)npth_dlsym_force(v0, "_ZN12openjdkjvmti9ClassUtil29GetHiddenApiEnforcementPolicyEP9_jvmtiEnvPi");
    if ( !v2 )
      goto LABEL_19;
    v3 = -49LL;
    while ( 1 )
    {
      if ( *v2 == 706675680 || *v2 == -113245944 )
      {
        n706675680 = v2[2];
        if ( n706675680 == 706675680 || n706675680 == -1191182296 )
          break;
      }
      v4 = __CFADD__(v3++, 1LL);
      ++v2;
      if ( v4 )
        goto LABEL_19;
    }
    v6 = v2[1];
    if ( (sub_73888() & 1) != 0 )
      __android_log_print(
        3,
        "OptimizeHiddenApi",
        " Found the Ldr instruction in GetHiddenApiEnforcementPolicy, target_instruction is %p,  i = %d",
        (const void *)v6,
        v3 + 50);
    if ( (~v6 & 0xB8400000) != 0 )
    {
LABEL_19:
      v9 = 0;
    }
    else
    {
      if ( (v6 & 0x40000000) != 0 )
        n8 = 8;
      else
        n8 = 4;
      if ( (v6 & 0x1000000) != 0 )
        v8 = (v6 >> 10) & 0xFFF;
      else
        v8 = (v6 >> 12) & 0x1FF;
      v9 = v8 * n8;
      __android_log_print(4, "OptimizeHiddenApi", "Get the offset of enforcement_policy: %x ", v8 * n8);
    }
    npth_dlclose_force(v1);
  }
  else
  {
    return 0;
  }
  return v9;
}

首先获取 libopenjdkjvmti.so 的 openjdkjvmti::ClassUtil::GetHiddenApiEnforcementPolicy 的符号地址:

1
2
3
4
5
...
v0 = npth_dlopen_force("libopenjdkjvmti.so");
...
v2 = (_DWORD *)npth_dlsym_force(v0, "_ZN12openjdkjvmti9ClassUtil29GetHiddenApiEnforcementPolicyEP9_jvmtiEnvPi");
...

然后从函数的开始地址进行二机制搜索:

1
2
3
4
5
6
7
8
9
10
11
12
13
while ( 1 )
{
    if ( *v2 == 706675680 || *v2 == -113245944 )
    {
    n706675680 = v2[2];
    if ( n706675680 == 706675680 || n706675680 == -1191182296 )
        break;
    }
    v4 = __CFADD__(v3++, 1LL);
    ++v2;
    if ( v4 )
    goto LABEL_19;
}

ZN12openjdkjvmti9ClassUtil29GetHiddenApiEnforcementPolicyEP9_jvmtiEnvPi 返回 hidden_api_policy 的汇编代码如下:

1
2
3
4
5
6
7
48 04 00 D0             ADRP            X8, #_ZN3art7Runtime9instance_E_ptr@PAGE
08 D9 45 F9             LDR             X8, [X8,#_ZN3art7Runtime9instance_E_ptr@PAGEOFF]
E0 03 1F 2A             MOV             W0, WZR
08 01 40 F9             LDR             X8, [X8] ; art::Runtime::instance_
08 3D 44 B9             LDR             W8, [X8,#0x43C]
28 00 00 B9             STR             W8, [X1]
C0 03 5F D6             RET

-113245944 = 0xFFFFFFFFF9400108 -> LDR X8, [X8] ; art::Runtime::instance_

-1191182296 = 0xFFFFFFFFB9000028 -> STR W8, [X1]

根据上下文定位到 LDR W8, [X8,#0x43C];

最后解析获取到 hidden_api_policy_ 的偏移是 0x43C

获取到偏移后再获取 _ZN3art7Runtime9instance_E 的地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
v3 = atomic_load((unsigned __int8 *)&qword_15BD30);// 原子读取
if ( (v3 & 1) == 0 && __cxa_guard_acquire(&qword_15BD30) )// 构造加锁
{
    dword_15BD2C = sub_87784();               // GetHiddenApiEnforcementPolicy
    __cxa_guard_release(&qword_15BD30);       // 构造解锁
}
v4 = dword_15BD2C;
if ( dword_15BD2C )
{
    v5 = npth_dlopen("libart.so");
    v6 = (_QWORD *)npth_dlsym(v5, "_ZN3art7Runtime9instance_E");
    if ( v5 )
    npth_dlclose(v5);
    if ( !v6 || (unsigned int)(*(_DWORD *)(*v6 + (unsigned int)dword_15BD2C) - 1) > 1 )
    {
    v0 = v2;
    return v0 & 1;
    }
    *(_DWORD *)((unsigned int)dword_15BD2C + *v6) = 0;// GetHiddenApiEnforcementPolicy + _ZN3art7Runtime9instance_E = 0
    v4 = 1;
}

最后根据 Runtime 实例加上 hidden_api_policy_ 偏移来定位到 hidden_api_policy_ 变量的地址

hidden_api_policy_ 是一个枚举的类型:

1
2
3
4
5
6
enum class EnforcementPolicy {
  kDisabled             = 0,
  kJustWarn             = 1,  // keep checks enabled, but allow everything (enables logging)
  kEnabled              = 2,  // ban conditionally blocked & blocklist
  kMax = kEnabled,
};

最后将 hidden_api_policy_ 设置为 0 来关闭 HIDE API 的执行策略

绕过代码

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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <elf.h>
#include <android/log.h>
#include <link.h>
#include <jni.h>
#include <cstring>
 
struct user_data_t {
    const char* target_name;
    uintptr_t base_addr;
    bool found;
};
 
// 回调函数:遍历每个模块时被调用
static int callback(struct dl_phdr_info *info, size_t size, void *data) {
    user_data_t* user_data = (user_data_t*)data;
    const char* path = info->dlpi_name;
    const char* basename = strrchr(path, '/');
    if (basename) {
        basename++;
    } else {
        basename = path;
    }
    if (strstr(basename, user_data->target_name) != nullptr) {
        user_data->target_name = info->dlpi_name;
        user_data->base_addr = info->dlpi_addr;
        user_data->found = true;
        return 1;
    }
    return 0;
}
 
// 获取内存中动态库的基地址
user_data_t npth_dlopen_(const char* module_name) {
    user_data_t user_data;
    user_data.target_name = module_name;
    user_data.base_addr = 0;
    user_data.found = false;
    int result = dl_iterate_phdr(callback, &user_data);
    return user_data;
}
 
// 手动解析 ELF 符号 (简化版)
void* npth_dlsym_symtab_(const char* library_path, const char* symbol_name) {
 
    user_data_t user_data = npth_dlopen_(library_path);
    if (!user_data.found) return nullptr;
 
    int fd = open(user_data.target_name, O_RDONLY);
    if (fd < 0) return nullptr;
 
    off_t size = lseek(fd, 0, SEEK_END);
    void* elf_data = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
    close(fd);
 
    Elf64_Ehdr* ehdr = (Elf64_Ehdr*)elf_data;
    Elf64_Shdr* shdr = (Elf64_Shdr*)((uintptr_t)elf_data + ehdr->e_shoff);
 
    Elf64_Sym* symtab = nullptr;
    char* strtab = nullptr;
    int sym_count = 0;
 
    for (int i = 0; i < ehdr->e_shnum; i++) {
        if (shdr[i].sh_type == SHT_DYNSYM) {
            symtab = (Elf64_Sym*)((uintptr_t)elf_data + shdr[i].sh_offset);
            sym_count = shdr[i].sh_size / sizeof(Elf64_Sym);
        } else if (shdr[i].sh_type == SHT_STRTAB && i != ehdr->e_shstrndx) {
            strtab = (char*)((uintptr_t)elf_data + shdr[i].sh_offset);
        }
    }
 
    void* target_addr = nullptr;
    for (int i = 0; i < sym_count; i++) {
        char *name = strtab + symtab[i].st_name;
        int addr = symtab[i].st_value;
        if (strcmp(strtab + symtab[i].st_name, symbol_name) == 0) {
            target_addr = (void*)(user_data.base_addr + symtab[i].st_value);
            break;
        }
    }
 
    munmap(elf_data, size);
    return target_addr;
}
 
uint32_t bytedance_get_hidden_api_policy_(){
 
    uint64_t counter = -49LL;
    uint32_t* current_addr;
    uint32_t target_instruction;
    uint32_t offset = 0;
    bool found = false;
 
    void  *get_hidden_api_enforcement_policy_addr = npth_dlsym_symtab_("libopenjdkjvmti.so","_ZN12openjdkjvmti9ClassUtil29GetHiddenApiEnforcementPolicyEP9_jvmtiEnvPi");
    if (!get_hidden_api_enforcement_policy_addr) {
        return 0;
    }
 
    current_addr = (uint32_t*)get_hidden_api_enforcement_policy_addr;
 
    while (1) {
        if (*current_addr == 706675680 || *current_addr == -113245944) {
            uint32_t next_check = current_addr[2];
            if (next_check == 706675680 || next_check == -1191182296) {
                target_instruction = current_addr[1];
                found = true;
                break;
            }
        }
        current_addr++;
        counter++;
        if (counter == 0) {
            break;
        }
    }
 
    if (found) {
        __android_log_print(
                4,
                "OptimizeHiddenApi",
                " Found the Ldr instruction in GetHiddenApiEnforcementPolicy, target_instruction is %p,  i = %d",
                (void*)target_instruction,
                (int)(counter + 50));
        if ((~target_instruction & 0xB8400000) != 0) {
            offset = 0;
        } else {
            int scale;
            int raw_offset;
 
            if ((target_instruction & 0x40000000) != 0) {
                scale = 8;
            } else {
                scale = 4;
            }
 
            if ((target_instruction & 0x1000000) != 0) {
                raw_offset = (target_instruction >> 10) & 0xFFF;
            } else {
                raw_offset = (target_instruction >> 12) & 0x1FF;
            }
            offset = raw_offset * scale;
            __android_log_print(
                    4,
                    "OptimizeHiddenApi",
                    "Get the offset of enforcement_policy: %x",
                    offset
            );
        }
    }
 
    return offset;
}
 
uint32_t bytedance_set_hidden_api_policy_() {
    // 1. 查找HiddenApiPolicy字段的偏移地址
    uint32_t hidden_api_policy_offset = bytedance_get_hidden_api_policy_();
    if (!hidden_api_policy_offset) {
        return 0;
    }
    // 2. 查找_ZN3art7Runtime9instance_E函数的符号
    uint64_t** art_Runtime_instance_ptr_ptr = (uint64_t**)npth_dlsym_symtab_("libart.so","_ZN3art7Runtime9instance_E");;
    if (!art_Runtime_instance_ptr_ptr) {
        return 0;
    }
    uint64_t* runtime_instance = *art_Runtime_instance_ptr_ptr;
 
    // 3. 计算隐藏API策略的地址
    uint32_t* policy_ptr = (uint32_t*)((uint64_t)runtime_instance + hidden_api_policy_offset);
    if (*policy_ptr < 1 || *policy_ptr > 2) {
        return 0;
    }
    *policy_ptr = 0;
    return 1;
}
 
extern "C" JNIEXPORT void JNICALL
Java_com_android_hiddenapi_MainActivity_bytedance(JNIEnv *env, jobject thiz) {
    bytedance_set_hidden_api_policy_();
}

关闭策略运行前后的日志如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
hiddenapi: Accessing hidden method Ldalvik/system/VMRuntime;->setTargetSdkVersion(I)V (runtime_flags=CorePlatformApi, domain=core-platform, api=blocked,core-platform-api) from Lcom/android/hiddenapi/MainActivity; (domain=app) using reflection: denied
java.lang.NoSuchMethodException: dalvik.system.VMRuntime.setTargetSdkVersion [int]
    at java.lang.Class.getMethod(Class.java:2940)
    at java.lang.Class.getDeclaredMethod(Class.java:2919)
    at com.android.hiddenapi.MainActivity.VMRuntimeSetTargetSdkVersion(MainActivity.java:142)
    at com.android.hiddenapi.MainActivity$1.onClick(MainActivity.java:43)
    at android.view.View.performClick(View.java:8083)
    at android.view.View.performClickInternal(View.java:8060)
    at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0)
    at android.view.View$PerformClick.run(View.java:31549)
    at android.os.Handler.handleCallback(Handler.java:995)
    at android.os.Handler.dispatchMessage(Handler.java:103)
    at android.os.Looper.loopOnce(Looper.java:248)
    at android.os.Looper.loop(Looper.java:338)
    at android.app.ActivityThread.main(ActivityThread.java:9067)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:593)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:932)
 Found the Ldr instruction in GetHiddenApiEnforcementPolicy, target_instruction is 0xb9443d08,  i = 14
Get the offset of enforcement_policy: 43c
setTargetSdkVersion success!

[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

最后于 7小时前 被易之生生编辑 ,原因:
收藏
免费 8
支持
分享
最新回复 (2)
雪    币: 1506
活跃值: (3873)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
2
666
8小时前
0
雪    币: 7344
活跃值: (23989)
能力值: ( LV12,RANK:550 )
在线值:
发帖
回帖
粉丝
3
有点东西
7小时前
0
游客
登录 | 注册 方可回帖
返回