Tech

MIUI隐私行为逆向分析

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

前言

一直好奇,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下吧

*** 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
*** 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

其中的源码:

    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进行控制,所以不是很符合我的要求。