MIUI隐私行为逆向分析

前言

一直好奇,MIUI的隐私行为咋做的,但是一直没有什么动力去分析。目前公司有个新的活,做出来一套无侵入式的隐私分析沙箱,一开始认为单纯的填写相应API代码就行了,后来实操下来发现真的太天真了,那个UID调用的API无从知晓,而且无法控制UID,所以突然想看看MIUI的隐私行为咋做的,才有这篇小文章。

过程

一开始肯定要定位那个App具体实现了隐私行为功能了,所以直接用adb的命令直接获取到隐私行为界面是在那个App下,然后从手机中提取出apk,再对apk内容进行分析。

adb shell dumpsys window | Select-String mCurrentFocus

mCurrentFocus=Window{d559062 mode=0 rootTaskId=378 u0 com.miui.securitycenter/com.miui.permcenter.settings.PrivacySettingsActivity}

com.miui.securitycenter/com.miui.permcenter.settings.PrivacySettingsActivity这也就是隐私行为的主界面了,所以直接提取出安全中心apk即可。

简单分析了下,

com.miui.permcenter.privacymanager.k.a 单个行为的具体细节

com.miui.permcenter.privacymanager.behaviorrecord.b.b.a App行为时间线

其中包含了应用图标、应用名称、应用行为名称、行为发生的时间、行为时间线(MIUI自己的时间线UI功能,不多做赘述)。

com.miui.permcenter.privacymanager.k.a是主要的行为内容部分,所以掌握这块内容从哪里传过来的就很好办了。

com.miui.permcenter.privacymanager.k.a v0 = (com.miui.permcenter.privacymanager.k.a)this.b.get(arg9); // 设置行为信息

com.miui.permcenter.privacymanager.behaviorrecord.AppBehaviorRecordActivity // App behavior的界面

其中调用com.miui.permcenter.privacymanager.k.a 的为 this.f.add(new a());

com.miui.permcenter.privacymanager.behaviorrecord.c.a // 调用com.miui.permcenter.privacymanager.k.a 并进行初始化

静态分析看了半天,直接用frida trace下吧

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
*** exiting com.miui.permcenter.privacymanager.k.a.a
========================================================================================================================================================================================================Inspecting Fields: => true => class com.miui.permcenter.privacymanager.k.a
java.lang.String a => undefined => undefined
java.lang.String b => undefined => undefined
java.lang.String c => undefined => undefined
long d => undefined => undefined
int e => undefined => undefined
int f => undefined => undefined
java.lang.String g => undefined => undefined
java.lang.String h => undefined => undefined
int i => undefined => undefined
int j => undefined => undefined
int k => undefined => undefined
java.lang.CharSequence l => undefined => undefined
java.lang.CharSequence m => undefined => undefined
java.lang.String n => undefined => undefined
int o => undefined => undefined
java.lang.String p => null => null
int q => 0 => 0
com.miui.appmanager.i r => null => null
int s => 0 => 0
java.lang.String t => null => null
java.util.List u => null => null
boolean v => false => false
[Ljava.lang.reflect.Field;@79b8e47 [Ljava.lang.reflect.Field;@79b8e47 => undefined => undefined
[native function d() {
[native code]
} => undefined => undefined

*** entered com.miui.permcenter.privacymanager.k.a.a
arg[0]: com.miui.permcenter.privacymanager.behaviorrecord.AppBehaviorRecordActivity@1a85b4a => "<instance: android.content.Context, $className: com.miui.permcenter.privacymanager.behaviorrecord.AppBehaviorRecordActivity>"
java.lang.Throwable
at com.miui.permcenter.privacymanager.k.a.a(Native Method)
at com.miui.permcenter.privacymanager.k.a.<init>(Unknown Source:77)
at com.miui.permcenter.privacymanager.k.a.<init>(Native Method)
at com.miui.permcenter.privacymanager.behaviorrecord.c.a(Unknown Source:347)
at com.miui.permcenter.privacymanager.behaviorrecord.AppBehaviorRecordActivity.a(Unknown Source:68)
at com.miui.permcenter.privacymanager.behaviorrecord.AppBehaviorRecordActivity.a(Unknown Source:0)
at com.miui.permcenter.privacymanager.behaviorrecord.AppBehaviorRecordActivity$m.z(Unknown Source:44)
at com.miui.permcenter.privacymanager.behaviorrecord.AppBehaviorRecordActivity$m.z(Unknown Source:0)
at a.j.b.a.A(Unknown Source:0)
at c.c.d.n.c.A(Unknown Source:0)
at a.j.b.a$a.a(Unknown Source:2)
at a.j.b.a$a.a(Unknown Source:2)
at a.j.b.d$b.call(Unknown Source:18)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)

retval: undefined => undefined
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
*** exiting com.miui.permcenter.privacymanager.k.a.a
========================================================================================================================================================================================================Inspecting Fields: => true => class com.miui.permcenter.privacymanager.k.a
java.lang.String a => undefined => undefined
java.lang.String b => undefined => undefined
java.lang.String c => undefined => undefined
long d => undefined => undefined
int e => undefined => undefined
int f => undefined => undefined
java.lang.String g => undefined => undefined
java.lang.String h => undefined => undefined
int i => undefined => undefined
int j => undefined => undefined
int k => undefined => undefined
java.lang.CharSequence l => undefined => undefined
java.lang.CharSequence m => undefined => undefined
java.lang.String n => undefined => undefined
int o => undefined => undefined
java.lang.String p => null => null
int q => 0 => 0
com.miui.appmanager.i r => null => null
int s => 0 => 0
java.lang.String t => null => null
java.util.List u => null => null
boolean v => false => false
[Ljava.lang.reflect.Field;@bc0f586 [Ljava.lang.reflect.Field;@bc0f586 => undefined => undefined
[native function d() {
[native code]
} => undefined => undefined

*** entered com.miui.permcenter.privacymanager.k.a.$init
arg[0]: com.miui.permcenter.privacymanager.behaviorrecord.AppBehaviorRecordActivity@1a85b4a => "<instance: android.content.Context, $className: com.miui.permcenter.privacymanager.behaviorrecord.AppBehaviorRecordActivity>"
arg[1]: com.tencent.mm => "com.tencent.mm"
arg[2]: com.tencent.mm => "com.tencent.mm"
arg[3]: null => "null"
arg[4]: 35184372088832 => "35184372088832"
arg[5]: 0 => 0
arg[6]: 1 => 1
arg[7]: 2022-12-30 09:40 => "2022-12-30 09:40"
arg[8]: 2022-12-30 09:40 => "2022-12-30 09:40"
arg[9]: 1 => 1
arg[10]: 0 => 0
arg[11]: 0 => 0
arg[12]: 0 => 0
java.lang.Throwable
at com.miui.permcenter.privacymanager.k.a.<init>(Native Method)
at com.miui.permcenter.privacymanager.behaviorrecord.c.a(Unknown Source:347)
at com.miui.permcenter.privacymanager.behaviorrecord.AppBehaviorRecordActivity.a(Unknown Source:68)
at com.miui.permcenter.privacymanager.behaviorrecord.AppBehaviorRecordActivity.a(Unknown Source:0)
at com.miui.permcenter.privacymanager.behaviorrecord.AppBehaviorRecordActivity$m.z(Unknown Source:44)
at com.miui.permcenter.privacymanager.behaviorrecord.AppBehaviorRecordActivity$m.z(Unknown Source:0)
at a.j.b.a.A(Unknown Source:0)
at c.c.d.n.c.A(Unknown Source:0)
at a.j.b.a$a.a(Unknown Source:2)
at a.j.b.a$a.a(Unknown Source:2)
at a.j.b.d$b.call(Unknown Source:18)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)

retval: undefined => undefined
*** exiting com.miui.permcenter.privacymanager.k.a.$init

其中的源码:

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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
public static boolean a(Context arg35, String arg36, int arg37, @NonNull List arg38, int[] arg39) {
int v25;
Cursor v29_1;
String v22_1;
String v21_1;
int v20_1;
int v10_1;
List v2_2;
long v33;
int v32;
int v31_1;
int v23;
int v24;
int v3_2;
int v1_4;
int v7;
long v17;
String v5;
String v4_1;
Cursor v6;
String v22;
int v1_1;
StringBuilder v3_1;
String v4;
Cursor v29 = null;
Context v0 = arg35;
List v15 = arg38;
int[] v1 = arg39;
long v13 = System.currentTimeMillis();
boolean v16 = c.f(arg35);
String[] v2 = new String[]{"pkgName", "calleePkg", "permissionId", "mode", "processState", "startTime", "endTime", "count", "user"};
int v12 = 2;
String[] v3 = new String[2];
int v11 = 0;
v3[0] = arg36;
int v10 = 1;
v3[1] = String.valueOf(arg37);
int v9 = 4;
int v8 = 3;
if(v16) {
v2 = (String[])Arrays.copyOf(((Object[])v2), v2.length + 1);
v2[v2.length - 1] = "calleeUser";
v3 = new String[]{arg36, String.valueOf(arg37), arg36, String.valueOf(arg37)};
v4 = "(pkgName == ? AND user == ? ) OR ( calleePkg == ? AND calleeUser == ? )";
}
else {
v4 = "pkgName == ? AND user == ?";
}

String[] v19 = v2;
String[] v21 = v3;
String v20 = v4;
if(v1 == null) {
v22 = "endTime DESC , _id DESC";
}
else if(v1.length == 1) {
v3_1 = new StringBuilder();
v3_1.append("endTime DESC , _id DESC");
v3_1.append(" LIMIT ");
v1_1 = v1[0];
goto label_76;
}
else {
if(v1.length == 2) {
v3_1 = new StringBuilder();
v3_1.append("endTime DESC , _id DESC");
v3_1.append(" LIMIT ");
v3_1.append(v1[0]);
v3_1.append(" OFFSET ");
v1_1 = v1[1];
label_76:
v3_1.append(v1_1);
v22 = v3_1.toString();
goto label_83;
}

v22 = "endTime DESC , _id DESC";
}

label_83:
Cursor v1_2 = null;
try {
v6 = arg35.getContentResolver().query(PermissionContract.RECORD_URI, v19, v20, v21, v22);
goto label_91;
}
catch(Exception v0_1) {
}
catch(Throwable v0_2) {
v29 = v1_2;
miuix.core.util.d.a(v29);
throw v0_2;
}

boolean v31 = false;
goto label_260;
label_91:
if(v6 == null) {
v29_1 = v6;
v31 = false;
}
else {
boolean v1_3 = false;
try {
while(true) {
label_93:
boolean v2_1 = v6.moveToNext();
goto label_99;
}
}
catch(Exception v0_1) {
v29 = v6;
v31 = v1_3;
goto label_258;
label_99:
if(!v2_1) {
v29_1 = v6;
miuix.core.util.d.a(v29_1);
return v1_3;
}

try {
v4_1 = v6.getString(v11);
v5 = v6.getString(v10);
v17 = v6.getLong(v12);
v7 = v6.getInt(8);
}
catch(Exception v0_1) {
goto label_256;
}
catch(Throwable v0_2) {
v29 = v6;
miuix.core.util.d.a(v29);
throw v0_2;
}

if(v16) {
try {
v1_4 = v6.getInt(9);
goto label_113;
}
catch(Exception v0_1) {
}
catch(Throwable v0_2) {
v29 = v6;
miuix.core.util.d.a(v29);
throw v0_2;
}

v1_2 = v6;
v31 = (boolean)v10;
goto label_260;
label_113:
v3_2 = v1_4;
}
else {
v3_2 = arg37;
}

try {
boolean v1_5 = c.c(v5);
}
catch(Exception v0_1) {
goto label_256;
}
catch(Throwable v0_2) {
v29 = v6;
miuix.core.util.d.a(v29);
throw v0_2;
}

if(!v1_5) {
goto label_125;
}

try {
if(!c.e.containsKey(Long.valueOf(v17)) || (c.a(v0, v4_1, v7))) {
goto label_140;
}

label_125:
if(v1_5) {
goto label_149;
}

if(v17 == 2L && !c.g.containsKey(v4_1) || (c.a(v0, v5, v3_2))) {
goto label_140;
}

boolean v1_6 = c.a(v0, v4_1, v5);
}
catch(Exception v0_1) {
v1_2 = v6;
v31 = true;
goto label_260;
}
catch(Throwable v0_2) {
v29 = v6;
miuix.core.util.d.a(v29);
throw v0_2;
}

if(v1_6) {
label_140:
v29 = v6;
v24 = v8;
v23 = v9;
v31_1 = v11;
v32 = v12;
v33 = v13;
v2_2 = v15;
v19 = null;
goto label_273;
}

try {
label_149:
v10_1 = v6.getInt(v8);
v20_1 = v6.getInt(v9);
v21_1 = v6.getString(5);
v22_1 = v6.getString(6);
long v8_1 = c.a(v22_1);
if(v8_1 > v13) {
v29 = v6;
v31_1 = v11;
v32 = v12;
v33 = v13;
v2_2 = v15;
v19 = null;
v23 = 4;
v24 = 3;
goto label_273;
}

if(c.a(v13, v8_1) > 6) {
v29_1 = v6;
v31 = true;
goto label_288;
}

v25 = v17 == 0x20L ? 1 : v6.getInt(7);
if(v16) {
goto label_183;
}
else {
goto label_209;
}

goto label_231;
}
catch(Exception v0_1) {
goto label_256;
}
catch(Throwable v0_2) {
v29 = v6;
miuix.core.util.d.a(v29);
throw v0_2;
}
}
catch(Throwable v0_2) {
v29 = v6;
miuix.core.util.d.a(v29);
throw v0_2;
}

label_183:
Context v2_3 = arg35;
String v3_3 = arg36;
v29 = v6;
long v6_1 = v17;
v24 = 3;
int v8_2 = v10_1;
v23 = 4;
int v9_1 = v20_1;
v19 = null;
String v10_2 = v21_1;
v31_1 = v11;
String v11_1 = v22_1;
v32 = v12;
int v12_1 = v25;
v33 = v13;
int v13_1 = v7;
int v14 = v3_2;
List v0_3 = v15;
try {
super(v2_3, v3_3, v4_1, v5, v6_1, v8_2, v9_1, v10_2, v11_1, v12_1, v13_1, v14, 1);
Object v15_1 = null;
goto label_231;
label_209:
v29 = v6;
v31_1 = v11;
v32 = v12;
v33 = v13;
v0_3 = v15;
v19 = null;
v23 = 4;
v24 = 3;
v15_1 = new a(arg35, arg36, v4_1, v5, v17, v10_1, v20_1, v21_1, v22_1, v25, arg37, 1); // 初始化
label_231:
if(arg38.size() == 0) {
v0_3.add(v15_1);
v2_2 = v0_3;
v0 = arg35;
}
else {
v2_2 = v0_3;
v0 = arg35;
if(!((a)v0_3.get(arg38.size() - 1)).a(v0, ((a)v15_1))) {
v2_2.add(v15_1);
}
}

goto label_273;
}
catch(Exception v0_1) {
}
catch(Throwable v0_2) {
miuix.core.util.d.a(v29);
throw v0_2;
}

label_256:
v31 = v19;
label_258:
v1_2 = v29;
try {
label_260:
Log.e("BehaviorRecord-Utils", "loadAppBehaviorByPkgNameAndUser error", v0_1);
}
catch(Throwable v0_2) {
v29 = v1_2;
miuix.core.util.d.a(v29);
throw v0_2;
}

miuix.core.util.d.a(v1_2);
return v31;
miuix.core.util.d.a(v29);
throw v0_2;
label_273:
v15 = v2_2;
v1_3 = (int)v19;
v10 = (int)v1_3;
v9 = v23;
v8 = v24;
v6 = v29;
v11 = v31_1;
v12 = v32;
v13 = v33;
goto label_93;
}

label_288:
miuix.core.util.d.a(v29_1);
return v31;
}

调用的是这个函数
public a(Context arg3, String arg4, String arg5, String arg6, long arg7, int arg9, int arg10, String arg11, String arg12, int arg13, int arg14, int arg15)

v15_1 = new a(arg35, arg36, v4_1, v5, v17, v10_1, v20_1, v21_1, v22_1, v25, arg37, 1); // 初始化

public a(Context arg3, String arg4, String arg5, String arg6, long arg7, int arg9, int arg10, String arg11, String arg12, int arg13, int arg14, int arg15) { // 确定行为触发的包名和时间

public String a(Resources arg9) { // 确定行为类型,返回触发时间 “Restricted at 10:15”

整体思路,一个packager uid下调用某个隐私API都会被记录,并存放库中。

隐私沙箱两种方案:

  1. 更改art编译模式,trace只trace framework层内容,对特定UID的app进行改变art编译模式。
  2. 研究MIUI Privacy_Manager 隐私行为功能实现,仿照一份写出来trace。

改动art类似于插入式Hook,系统级改动,安全有效,但不确定是否会造成原本正常的app崩溃。

MIUI Privacy_Manager形式更可靠,但是无法选定哪一个UID,以及UID发送给Manager app如何处理。

上次实现的问题是,虽然可以直接改动相应的framework代码,做到那个地方调用,并且能将调用的堆栈,都能够发送,但是对调用的UID为止,并且无法指定UID进行控制,所以不是很符合我的要求。