PrivacySentry:隐私政策守护利器

近年来,工信部对APP个人隐私要求越来越多,之后各大应用市场也开始要求,有违规情况的会导致APP下架或者无法上架。这不,我的app就因为三方SDK频繁获取Android ID 导致无法上架,等SDK商场更新也很浪费时间,所以只能想办法去处理这件事,好在找到了PrivacySentry这个神器,可规避应用市场上架合规检测的大部分问题

背景

近年来,工信部对APP个人隐私要求越来越多,之后各大应用市场也开始要求,有违规情况的会导致APP下架或者无法上架。这不,我的app就因为三方SDK频繁获取Android ID 导致无法上架,等SDK厂商更新也很浪费时间,所以只能想办法去处理这件事,好在找到了PrivacySentry这个神器,可规避应用市场上架合规检测的大部分问题

如何使用

一、配置PrivacySentry

第一步:在根目录的build.gradle中添加依赖

首先,你需要在项目的根目录build.gradle文件中添加以下配置:

gradle复制代码


allprojects {

repositories {

...

maven { url 'https://jitpack.io' }

}

}



buildscript {

dependencies {

// 添加PrivacySentry插件依赖

classpath 'com.github.allenymt.PrivacySentry:plugin-sentry:1.3.4.2'

}

}

注意:这里我们添加了jitpack.io作为Maven仓库,以便能够下载到PrivacySentry的相关依赖。

第二步:在项目中的build.gradle中添加插件和依赖

接下来,你需要在主项目的build.gradle文件中添加以下配置:

gradle复制代码


// 在主项目里添加PrivacySentry插件依赖

apply plugin: 'privacy-sentry-plugin'



dependencies {

// aar依赖

def privacyVersion = "1.3.4.2"

implementation "com.github.allenymt.PrivacySentry:hook-sentry:$privacyVersion"

implementation "com.github.allenymt.PrivacySentry:privacy-annotation:$privacyVersion"



// 代理类的库,如果自己没有代理类,那么必须引用这个aar!!

// 如果不想使用库中本身的代理方法,可以不引入这个aar,但是自己必须实现代理类!!

// 引入privacy-proxy,也可以自定义类代理方法,优先以业务方定义的为准

implementation "com.github.allenymt.PrivacySentry:privacy-proxy:$privacyVersion"

// 1.2.3 新增类替换,主要是为了hook构造函数的参数,按业务方需求自己决定

implementation "com.github.allenymt.PrivacySentry:privacy-replace:$privacyVersion"

}



// 隐私配置

privacy {

// 设置免hook的名单

blackList = []

// 开关PrivacySentry插件功能,核心功能开关,默认为true

enablePrivacy = true



// 开启hook反射的方法,默认为false,按需打开

hookReflex = false

// 配置反射拦截 反射获取小米系统的oaid、aaid、vaid,例如极光、个推、穿山甲等SDK都有获取

reflexMap = ["com.android.id.impl.IdProviderImpl":["getOAID","getAAID","getVAID"]]



// 默认为false,按需打开

hookConstructor = false

// 默认为false,按需打开

hookField = false



//*************以下是分割线,主要是对Service的自启动优化处理,默认为false,按需打开****************

// 处理Manifest文件,主要是处理Service的Priority , 关闭Service的Export

enableProcessManifest = false

// hook Service的部分代码,修复在MIUI上的自启动问题

// 部分Service把自己的Priority设置为1000,这里开启代理功能,可以代理成0

enableReplacePriority = false

replacePriority = 1



// 支持关闭Service的Export功能,默认为false,注意部分厂商通道之类的push(xiaomi、vivo、huawei等厂商的pushService),不能关闭

enableCloseServiceExport = false

// Export白名单Service

serviceExportPkgWhiteList = ["white"]

enableHookServiceStartCommand = false

}

二、初始化PrivacySentry

在完成上述配置后,你需要在应用中进行PrivacySentry的初始化。这里需要注意的是,初始化方法最好在attachBaseContext中第一个调用(从1.3.1版本开始,晚点初始化也不影响检测结果)。

java复制代码


// 初始化PrivacySentry

PrivacySentryBuilder builder = new PrivacySentryBuilder()

// 自定义文件结果的输出名

.configResultFileName("buyer_privacy")

// debug打开,可以看到logcat的堆栈日志

.syncDebug(true)

// 配置写入文件日志 , 线上包这个开关不要打开!!!!,true打开文件输入,false关闭文件输入

.enableFileResult(true)

// 持续写入文件30分钟

.configWatchTime(30 * 60 * 1000)

// 文件输出后的回调

.configResultCallBack(new PrivacyResultCallBack() {

@Override

public void onResultCallBack(@NonNull String s) {

// 回调处理

}

});



// 添加默认结果输出,包含log输出和文件输出

PrivacySentry.Privacy.INSTANCE.init(application, builder);

三、隐私协议确认

在应用中,隐私协议的确认是至关重要的一步。如果在日志中发现“check!!! 还未展示隐私协议,Illegal print”等提示信息,说明此时用户还未同意隐私协议,但应用已经调用了敏感或违规的API。因此,在隐私协议确认时,你需要调用以下方法来告知SDK:

kotlin复制代码


// Kotlin语法

PrivacySentry.Privacy.updatePrivacyShow()

java复制代码


// Java语法

PrivacySentry.Privacy.INSTANCE.updatePrivacyShow();

这一步非常重要,因为它能够确保SDK在检测到隐私协议已经同意后,再对后续的敏感操作进行监控和防护。

  支持自定义配置hook函数
    /**
 * @author yulun
 * @since 2022-01-13 17:57
 * 主要是两个注解PrivacyClassProxy和PrivacyMethodProxy,PrivacyClassProxy代表要解析的类,PrivacyMethodProxy代表要hook的方法配置
 */
@Keep
open class PrivacyProxyResolver {
     
    // kotlin里实际解析的是这个PrivacyProxyCall$Proxy 内部类
    @PrivacyClassProxy
    @Keep
    object Proxy {
 
        // 查询
        @SuppressLint("MissingPermission")
        @PrivacyMethodProxy(
            originalClass = ContentResolver::class,   // hook的方法所在的类名
            originalMethod = "query",   // hook的方法名
            originalOpcode = MethodInvokeOpcode.INVOKEVIRTUAL //hook的方法调用,一般是静态调用和实例调用
        )
        @JvmStatic
        fun query(
            contentResolver: ContentResolver?, //实例调用的方法需要把声明调用对象,我们默认把对象参数放在第一位
            uri: Uri,
            projection: Array<String?>?, selection: String?,
            selectionArgs: Array<String?>?, sortOrder: String?
        ): Cursor? {
            doFilePrinter("query", "查询服务: ${uriToLog(uri)}") // 输入日志到文件
            return contentResolver?.query(uri, projection, selection, selectionArgs, sortOrder)
        }
  
        @RequiresApi(Build.VERSION_CODES.O)
        @PrivacyMethodProxy(
            originalClass = android.os.Build::class,
            originalMethod = "getSerial",
            originalOpcode = MethodInvokeOpcode.INVOKESTATIC //静态调用
        )
        @JvmStatic
        fun getSerial(): String? {
            var result = ""
            try {
                doFilePrinter("getSerial", "读取Serial")
                if (PrivacySentry.Privacy.getBuilder()?.isVisitorModel() == true) {
                return ""
                }
            result = Build.getSerial()
            } catch (e: Exception) {
                e.printStackTrace()
            }
        return result
        }
    }
}

    支持多进程,多进程产出的文件名前缀默认增加进程名

    如何配置替换一个类
    可以参考源码中PrivacyFile的配置,使用PrivacyClassReplace注解,originClass代表你要替换的类,注意要继承originClass的所有构造函数
    可以配置 hookConstructor = false关闭这个功能
/**
 * @author yulun
 * @since 2022-11-18 15:01
 * 代理File的构造方法,如果是自定义的file类,需要业务方单独配置自行处理
 */
@PrivacyClassReplace(originClass = File.class)
public class PrivacyFile extends File {

    public PrivacyFile(@NonNull String pathname) {
        super(pathname);
        record(pathname);
    }

    public PrivacyFile(@Nullable String parent, @NonNull String child) {
        super(parent, child);
        record(parent + child);
    }

    public PrivacyFile(@Nullable File parent, @NonNull String child) {
        super(parent, child);
        record(parent.getPath() + child);
    }

    public PrivacyFile(@NonNull URI uri) {
        super(uri);
        record(uri.toString());
    }

    private void record(String path) {
        PrivacyProxyUtil.Util.INSTANCE.doFilePrinter("PrivacyFile", "访问文件", "path is " + path, PrivacySentry.Privacy.INSTANCE.getBuilder().isVisitorModel(), false);
    }
}

隐私方法调用结果产出

  • 支持hook调用堆栈至文件,默认的时间为1分钟,支持自定义设置时间。

  • 排查结果可参考目录下的demo_result.xls,排查结果支持两个维度查看,第一是结合隐私协议的展示时机和敏感方法的调用时机,第二是统计所有敏感函数的调用次数

  • 排查结果可观察日志,结果文件会在 /storage/emulated/0/Android/data/yourPackgeName/files/xx.xls,需要手动执行下adb pull

  • logcat日志查看:TAG名为PrivacyOfficer

基本原理

  • 编译期注解+hook方案,第一个transform收集需要拦截的敏感函数,第二个transform替换敏感函数,运行期收集日志

  • 为什么不用xposed等框架? 因为想做本地自动化定期排查,第三方hook框架外部依赖性太大

  • 为什么不搞基于lint的排查方式? 工信部对于运行期 敏感函数的调用时机和次数都有限制,代码扫描解决不了这些问题

支持的hook函数列表

支持hook以下功能函数:

  • 支持敏感字段缓存(磁盘缓存、带有时间限制的磁盘缓存、内存缓存)

  • hook替换类 (构造函数)

  • 当前运行进程和任务

  • 系统剪贴板服务

  • 读取设备应用列表

  • 读取 Android SN(Serial,包括方法和变量),系统设备号

  • 读写联系人、日历、本机号码

  • 获取定位、基站信息、wifi信息

  • Mac 地址、IP 地址

  • 读取 IMEI(DeviceId)、MEID、IMSI、ADID(AndroidID)

  • 手机可用传感器,传感器注册,传感器列表

  • 权限请求

常见的合规字段整理

IMEI、MAC地址、MEID、IMSI、SN、ICCID等设备唯一标识符,Android ID、WiFi(WiFi名称、WiFi MAC地址以及设备扫描到的所有WiFi信息),SIM卡信息(IMSI、SIM卡序列号ICCID、手机号、运营商信息),应用安装列表(设备所有已安装应用的包名和应用名),传感器(传感器列表、加速度传感器、温度传感器等),蓝牙信息(设备蓝牙地址和设备扫描到的蓝牙设备信息),基站定位、GPS(用户地理位置信息),账户(各类应用注册的不同账号信息)、剪切板、IP地址、硬件序列号、SDCard信息(公有目录)


来源: 互联网
本文观点不代表码客-全球程序员交流社区立场,不承担法律责任,文章及观点也不构成任何投资意见。

赞 ()

相关推荐

  • 置顶 开发项目接单群,免费入群了

    无论你是Android、ios、java、php,或者你是产品经理、老板,都可以免费入群接单或者发布项目,全程不收取任何费用。

    2025年02月21日 15点27分
  • 鸿蒙Next-AttributeModifier结合@Styles和@Extend深度解析使用

    声明式语法引入了@Styles和@Extend两个装饰器,可以解决复用相同自定义样式的问题,但是存在以下受限场景:

    2025年03月03日 15点40分
  • ConstraintLayout之layout_constraintDimensionRatio属性详解

    layout_constraintDimensionRatio 是 ConstraintLayout 提供的一个强大功能,它可以让 View 按照固定的宽高比例自适应尺寸。使用这个属性,可以在 ConstraintLayout 中根据已知的宽度或高度,自动计算另一个维度,确保 View 保持特定的宽高比。

    2025年03月03日 15点39分
  • Android事件分发时,你浓眉大眼的onTouch()竟然没有执行?

    在开发需求时,有这么一个场景:Activity中有一个ViewGroup作为Parent,ViewGroup里面又有一个Webview作为Child。当一进入页面时,系统输入法自动弹起,而在点击Parent区域时,需要收起系统输入法。背景介绍完毕,当时的第一想法就是通过Parent设置setOnTouchListener,然后在onTouch()回调中来实现:

    2025年03月02日 15点10分
  • Android SDK封装与发布实战指南

    Android SDK封装与发布实战指南

    2025年03月02日 15点05分
  • 鸿蒙Next开发-添加水印以及点击穿透设置

    在鸿蒙Next中,为App全局添加水印可以通过以下方式实现,其中通过窗口添加水印是一种常见且高效的方式。以下是具体方案和实现细节:

    2025年02月26日 23点15分
  • 鸿蒙Next开发-普通函数和箭头函数 this指向的区别以及对UI刷新的影响

    鸿蒙Next开发-普通函数和箭头函数 this指向的区别以及对UI刷新的影响

    2025年02月26日 23点14分
  • 深入探索ArkUI @Builder与@BuilderParam的进阶应用

    在ArkUI的组件化开发体系中,@Builder和@BuilderParam这对装饰器组合扮演着UI模块化的重要角色。二者的差异与配合体现了声明式UI的核心思想:

    2025年02月26日 23点12分
  • Deepseek推荐:Android 开发者需要掌握的系统知识大纲

    一、操作系统基础1. Linux 内核机制内容介绍 Android 基于 Linux 内核,核心机制包括进程管理、内存管理、文件系统、Binder 驱动等。

    2025年02月26日 23点09分
  • Android App 厂商角标适配

    本篇介绍一下笔者在维护IM应用时,设置App角标的相关经验。同时这里设置角标都是基于系统厂商的Launcher,没有适配三方的Launcher应用,因为我们统计下来发现近些年使用三方Launcher应用比较少了,大部分用户还是以系统Launcher为主。所在在我们的项目中,主要是适配各个厂商。

    2025年02月26日 23点07分
  • 鸿蒙Next-方法装饰器以及防抖方法注解实现

    以下是关于 鸿蒙Next(HarmonyOS NEXT)中 MethodDecorator 的详细介绍及使用指南,结合了多个技术来源的实践总结:

    2025年02月26日 22点58分
  • DevEco Studio常用快捷键以及如何跟AndroidStudio的保持同步

    DevEco Studio是华为推出的用于开发HarmonyOS应用的集成开发环境,它提供了丰富的快捷键以提高开发效率,以下为你详细介绍不同操作场景下的常用快捷键:

    2025年02月26日 22点56分
  • Android | 利用ItemDecoration绘制RecyclerView分割线

    RecyclerView.ItemDecoration 是 Android 提供的一种扩展机制,用于为 RecyclerView 的每个子项(Item)添加装饰(Decoration)。它通常用于绘制分割线、边距、背景等,目的是增强 RecyclerView 的显示效果。

    2025年02月26日 22点52分
  • Android 布局优化:利用 ViewStub 和 Merge 提升性能

    提升界面渲染性能是一个至关重要的任务,尤其是在应用启动时,渲染界面需要快速且流畅。为了优化 UI 渲染速度,Android 提供了许多工具,其中 ViewStub 和 Merge 标签是非常有效的布局优化手段。通过合理使用这两者,可以延迟加载不必要的视图、减少布局的嵌套层级,从而加速应用的启动和运行。

    2025年02月26日 22点47分
  • 玩转 ImageView.ScaleType:图片的缩放与裁剪技巧

    ImageView 是最常用的控件之一,它用于展示各种类型的图片。为了能够根据需求调整图片的显示效果,Android 提供了 ImageView.ScaleType 枚举,它可以灵活地控制图片如何适应 ImageView 的尺寸。本文将探讨 ImageView.ScaleType 的不同选项、使用场景及其实现技巧。

    2025年02月26日 22点45分
  • Android加快你的编译速度

    工欲善其事,必先利其器。如果每次运行项目都要花费5-10分钟,那人的心态都要崩了。

    2025年02月25日 15点40分

发表回复

评论列表

点击查看更多

    联系我们

    在线咨询: QQ交谈

    微信:dxmcpjl

    邮件:1529097251#qq.com

    工作时间:周一至周五,9:30-18:30,节假日休息

    微信