背景

互联网时代,隐私变得越来越重要,各个互联网公司对隐私的意识也变得逐渐增强。于此同时,工信部也开启了新一轮的印象排查,明确要求了,针对App在用户同意相关隐私政策之前不允许App开发者收集用户相关隐私信息,特别是可定位设备的唯一信息,例如:网卡硬件地址(mac地址),设备imei信息,设备已经安装的应用列表信息等等。

整改措施

    1. 排查App各业务层系统隐私API的调用情况
    1. 排查引入的第三方SDK对隐私API的调用情况
存在的问题

自身业务对于系统API隐私API的调用,是业务开发人员可控制的,因为我们可以轻而易举的完成整改要求,在用户同意隐私政策之前不进行相关调用。然而问题是,第三方SDK的相关调用,如何能保证用户同意之前不允许相关SDK调用隐私API呢?

三方SDK隐私API调用解决方案
  1. 用户同意隐私政策前尽量不初始化三方SDK
  2. 通过Hook相关隐私API的调用排查不合规的SDK,并对其进行统一修改

想要统一对隐私API进行Hook操作,那么我们肯定是需要使用AOP编程思想,在相关隐私API被调用之前,判断使用是否同意过隐私增长,如果没有同意,则返回空数据不让其调用隐私API。

  1. AOP框架的选择

    AOP框架有很多,哪一种更合适? 这个看自身业务需求,如果说自身业务已经接入了相关的AOP框架(如:ASM,AspectJ等),则直接对相关API进行Hook即可。如果目前的业务还没有使用此类框架,我们推荐基于ASM的框架,虽然AspectJ能力更强对开发者更友好,但是处理简单的隐私API调用确实是大材小用了。

  2. 处理方案示例:

    下边我们就以滴滴开源的以ASM框架为基础的轻量级框架DroidAssist为例,简单处理相关业务:

    4.1 首先DroidAssit接入,不在详细描述

    4.2 Hook系统API调用:mac地址相关API,imei相关API,已安装的应用列表相关API

    伪代码逻辑

    1
    2
    3
    4
    5
    if(!isPrivcayAgreen) {
    return ""
    } else {
    return do system api
    }
    4.3 添加代码织入逻辑
    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
    <DroidAssist>
    <Global>
    <Filter>
    <Include>*</Include>
    </Filter>
    </Global>
    <Replace>
    <!-- mac地址-->
    <MethodCall>
    <Source>
    byte[] java.net.NetworkInterface.getHardwareAddress()
    </Source>
    <Target>
    {
    if (com.netease.ASMPrivacyUtil.isRejectMode()) {
    $_= com.netease.ASMPrivacyUtil.getHardwareAddress();
    } else {
    $_ =$proceed($$);
    }
    }
    </Target>
    <Filter>
    <Exclude>com.netease.ASMPrivacyUtil</Exclude>
    </Filter>
    </MethodCall>
    <!-- mac地址-->
    <MethodCall>
    <Source>
    java.lang.String android.net.wifi.WifiInfo.getMacAddress()
    </Source>
    <Target>
    {
    if (com.netease.ASMPrivacyUtil.isRejectMode()) {
    $_= com.netease.ASMPrivacyUtil.getMacAddress();
    } else {
    $_ =$proceed($$);
    }
    }
    </Target>
    <Filter>
    <Exclude>com.netease.ASMPrivacyUtil</Exclude>
    </Filter>
    </MethodCall>
    <!-- imsi-->
    <MethodCall>
    <Source>
    java.lang.String android.telephony.TelephonyManager.getSubscriberId()
    </Source>
    <Target>
    {
    if (com.netease.ASMPrivacyUtil.isRejectMode()) {
    $_= com.netease.ASMPrivacyUtil.getSubscriberId();
    } else {
    $_ =$proceed($$);
    }
    }
    </Target>
    <Filter>
    <Exclude>com.netease.ASMPrivacyUtil</Exclude>
    </Filter>
    </MethodCall>
    <!-- imei-->
    <MethodCall>
    <Source>
    java.lang.String android.telephony.TelephonyManager.getDeviceId()
    </Source>
    <Target>
    {
    if (com.netease.ASMPrivacyUtil.isRejectMode()) {
    $_= com.netease.ASMPrivacyUtil.getDeviceId();
    } else {
    $_ =$proceed($$);
    }
    }
    </Target>
    <Filter>
    <Exclude>com.netease.ASMPrivacyUtil</Exclude>
    </Filter>
    </MethodCall>
    <!-- 应用列表相关 start-->
    <MethodCall>
    <Source>
    java.util.List android.content.pm.PackageManager.getInstalledApplications(int)
    </Source>
    <Target>
    {
    if (com.netease.ASMPrivacyUtil.isRejectMode()) {
    $_= com.netease.ASMPrivacyUtil.getInstalledApplications($$);
    } else {
    $_ =$proceed($$);
    }
    }
    </Target>
    <Filter>
    <Exclude>com.netease.ASMPrivacyUtil</Exclude>
    </Filter>
    </MethodCall>

    <MethodCall>
    <Source>
    java.util.List android.content.pm.PackageManager.getInstalledApplicationsAsUser(int,int)
    </Source>
    <Target>
    {
    if (com.netease.ASMPrivacyUtil.isRejectMode()) {
    $_= com.netease.ASMPrivacyUtil.getInstalledApplicationsAsUser($$);
    } else {
    $_ =$proceed($$);
    }
    }
    </Target>
    <Filter>
    <Exclude>com.netease.ASMPrivacyUtil</Exclude>
    </Filter>
    </MethodCall>

    <MethodCall>
    <Source>
    java.util.List android.content.pm.PackageManager.getInstalledModules(int)
    </Source>
    <Target>
    {
    if (com.netease.ASMPrivacyUtil.isRejectMode()) {
    $_= com.netease.ASMPrivacyUtil.getInstalledModules($$);
    } else {
    $_ =$proceed($$);
    }
    }
    </Target>
    <Filter>
    <Exclude>com.netease.ASMPrivacyUtil</Exclude>
    </Filter>
    </MethodCall>

    <MethodCall>
    <Source>
    java.util.List android.content.pm.PackageManager.getInstalledPackages(int)
    </Source>
    <Target>
    {
    if (com.netease.ASMPrivacyUtil.isRejectMode()) {
    $_= com.netease.ASMPrivacyUtil.getInstalledPackages($$);
    } else {
    $_ =$proceed($$);
    }
    }
    </Target>
    <Filter>
    <Exclude>com.netease.ASMPrivacyUtil</Exclude>
    </Filter>
    </MethodCall>
    <!-- 应用列表相关 end-->
    </Replace>
    </DroidAssist>
    4.4 对比织入前后的代码差异

    织入前代码

    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
    private static String getMacBelowM(Context context) {
    try {
    WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
    WifiInfo wifiInfo = wifiManager.getConnectionInfo();
    if (wifiInfo != null) {
    return wifiInfo.getMacAddress();
    }
    } catch (Exception var3) {
    var3.printStackTrace();
    }

    return "";
    }


    private static String getMacUpM() {
    try {
    String interfaseName = "wlan0";
    List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
    Iterator var2 = interfaces.iterator();

    while (var2.hasNext()) {
    NetworkInterface intf = (NetworkInterface) var2.next();
    if (intf.getName().equalsIgnoreCase(interfaseName)) {
    byte[] mac = intf.getHardwareAddress();
    if (mac == null) {
    return "";
    }

    StringBuilder builder = new StringBuilder();
    byte[] var6 = mac;
    int var7 = mac.length;

    for (int var8 = 0; var8 < var7; ++var8) {
    byte aMac = var6[var8];
    builder.append(String.format("%02X:", aMac));
    }

    if (builder.length() > 0) {
    builder.deleteCharAt(builder.length() - 1);
    }

    return builder.toString();
    }
    }
    } catch (Exception var10) {
    var10.printStackTrace();
    }

    return "";
    }

    织入后代码

    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
    private static String getMacBelowM(Context context) {
    try {
    WifiInfo wifiInfo = ((WifiManager) context.getSystemService("wifi")).getConnectionInfo();
    if (wifiInfo != null) {
    return PrivacyAPINullImpl.isRejectMode() ? PrivacyAPINullImpl.getMacAddress() : wifiInfo.getMacAddress();
    }
    } catch (Exception var3) {
    var3.printStackTrace();
    }
    return "";
    }

    private static String getMacUpM() {
    String str = "";
    try {
    String interfaseName = "wlan0";
    for (NetworkInterface intf : Collections.list(NetworkInterface.getNetworkInterfaces())) {
    if (intf.getName().equalsIgnoreCase(interfaseName)) {
    byte[] mac = PrivacyAPINullImpl.isRejectMode() ? PrivacyAPINullImpl.getHardwareAddress() : intf.getHardwareAddress();
    if (mac == null) {
    return str;
    }
    StringBuilder builder = new StringBuilder();
    byte[] var6 = mac;
    int var7 = mac.length;
    for (int var8 = 0; var8 < var7; var8++) {
    builder.append(String.format("%02X:", new Object[]{Byte.valueOf(var6[var8])}));
    }
    if (builder.length() > 0) {
    builder.deleteCharAt(builder.length() - 1);
    }
    return builder.toString();
    }
    }
    } catch (Exception var10) {
    var10.printStackTrace();
    }
    return str;
    }

源码下载

实现源码如下:https://github.com/JeffWangGithub/PrivacyAPIHook