37

Android隐私API合规策略及检测方法

 3 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzUzMDk1NTgzNw%3D%3D&%3Bmid=2247485977&%3Bidx=1&%3Bsn=d348b15a11329d61a971cb4136995750
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

背景:

2020年, 为切实加强用户个人信息保护,为人民群众提供更安全、更健康、更干净的信息环境,国家工信部决定开展纵深推进主流APP用户权益检测行动。

APP、SDK处理用户个人信息方面,要求APP、SDK告知用户收集个人信息的目的、方式、范围,且需经用户同意后,方可收集用户个人信息。

策略:

  1. 增加隐私权限说明页。用户未同意隐私政策前,不进行包括三方SDK初始化在内的任何操作;若用户选择不同意,则二次友好挽留,直到用户选择同意。

  2. 移除APP、SDK相关的敏感API调用。SDK中的API的移除是个难点,需要先反编译APP,查看敏感API都有哪些SDK调用,然后通过插件的形式将敏感API的调用全部替换成自实现方法。其实,当前操作完全是兜底机制,没有必须实现的必要性。我们之所以要全部替换,主要是为了防止第1步有漏网之鱼。插件的具体实现,将会在另一篇文章详细介绍。

  3. 检测启动APP未同意隐私权限时,是否有敏感API的调用,以验证1、2两步是否成功满足合规要求。

ayEzeiU.png!mobile3yemim6.png!mobile

检测:

工具:

https://github.com/espduino/Hegui2.0

https://github.com/android-hacker/VirtualXposed

VirtualXposed 原理:

VirtualXposed的工作原理简单来理解,就是先在手机上建立一个“虚拟空间”,需要在这个“虚拟空间”里面安装APP ,并且安装其对应的Xposed框架模块,这样就能让这个模块对该APP 起到作用了。

这种工作模式有点类似于电脑上的虚拟机,安装虚拟机之后,无论怎么折腾,都不会影响主系统的正常动作,而虚拟系统仍然有主系统的全部功能,所以VirtualXposed是一个免Root的虚拟空间方案。

安装到VirtualXposed中的APP,运行起来跟普通应用无任何差别,可以正常收发消息,接收推送,读取本机文件等。

Xposed框架模块原理

Xposed框架模块的工作原理简单来理解,就是Hook。Hook 的这个本领,使它能够将自身的代码融入被Hook的程序进程中,成为目标进程的一个部分。API Hook 技术是一种用于改变API 执行结果的技术,能够将系统的API函数执行重定向。

在Android系统中使用了沙箱机制,普通用户程序的进程空间都是独立的,程序的运行互不干扰。这就使我们希望通过一个程序改变其他程序的某些行为的想法不能直接实现,但是Hook给我们开拓了解决此类问题的思路。

Android系统中应用程序进程都是由Zygote进程孵化出来的,而Zygote进程是由Init进程启动的。Zygote进程在启动时会创建一个Dalvik虚拟机实例,每当它孵化一个新的应用程序进程时,都会将这个Dalvik虚拟机实例复制到新的应用程序进程里面去,从而使得每一个应用程序进程都有一个独立的Dalvik虚拟机实例。

Zygote进程在启动的过程中,除了会创建一个Dalvik虚拟机实例之外,还会将Java运行时库加载到进程中来,以及注册一些Android核心类的JNI方法到前面创建的Dalvik虚拟机实例中去。这就可以将XposedBridge这个jar包加载到每一个Android应用程序中。

XposedBridge有一个私有的JNI方法hookMethodNative。 Android系统启动时,zygote进程加载XposedBridge将所有需要替换的Method通过JNI方法hookMethodNative指向Native方法xposedCallHandler,xposedCallHandler再转换为handleHookedMethod这个Java方法执行用户规定的Hook Func。

Xposed框架模块隐私API检测代码实现

package com.example.xcl;


import android.content.ContentResolver;
import android.location.LocationManager;
import android.util.Log;


import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;


public class HookApi implements IXposedHookLoadPackage {
private static final String TAG = "Xposed";


public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) {


if (lpparam == null) {
return;
}


Log.e(TAG, "Load app packageName:" + lpparam.packageName);


// 判断hook的包名
if (!"指定包名".equals(lpparam.packageName)) {
return;
} else {
Log.e(TAG, "API检测");
}


// 固定格式
XposedHelpers.findAndHookMethod(
android.telephony.TelephonyManager.class.getName(), // 需要hook的方法所在类的完整类名
lpparam.classLoader, // 类加载器,固定这么写就行了
"getDeviceId", // 需要hook的方法名
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
XposedBridge.log("调用getDeviceId()获取了imei");
}
}
);
XposedHelpers.findAndHookMethod(
android.telephony.TelephonyManager.class.getName(),
lpparam.classLoader,
"getDeviceId",
int.class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
XposedBridge.log("调用getDeviceId(int)获取了imei");
}
}
);


XposedHelpers.findAndHookMethod(
android.telephony.TelephonyManager.class.getName(),
lpparam.classLoader,
"getSubscriberId",
int.class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
XposedBridge.log("调用getSubscriberId获取了imsi");
}
}
);


XposedHelpers.findAndHookMethod(
android.net.wifi.WifiInfo.class.getName(),
lpparam.classLoader,
"getMacAddress",
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
XposedBridge.log("调用getMacAddress()获取了mac地址");
}
}
);


XposedHelpers.findAndHookMethod(
java.net.NetworkInterface.class.getName(),
lpparam.classLoader,
"getHardwareAddress",
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
XposedBridge.log("调用getHardwareAddress()获取了mac地址");
}
}
);


XposedHelpers.findAndHookMethod(
android.provider.Settings.Secure.class.getName(),
lpparam.classLoader,
"getString",
ContentResolver.class,
String.class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
XposedBridge.log("调用Settings.Secure.getstring获取了" + param.args[1]);
}
}
);


XposedHelpers.findAndHookMethod(
LocationManager.class.getName(),
lpparam.classLoader,
"getLastKnownLocation",
String.class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
XposedBridge.log("调用getLastKnownLocation获取了GPS地址");
}
}
);
}
}

实践效果:

落地策略前,检测效果如图1,用户未同意隐私政策前,有大量系统API调用。

6zMJvyB.png!mobile

落地策略后,检测效果如图2,用户未同意隐私政策前,无任何系统API调用。

UV7Nreb.png!mobile

拖地先生,从事互联网技术工作,在这里每周两篇文章,聊聊日常的实践和心得。往期推荐:

说说这个公众号

平均响应1000ms到200ms,PHP和Go那家强?

崩溃率从1%到0.02%,iOS稳定性解决之道

七招优化Android包体减少30%

技术产品职业瓶颈?29份腾讯通道材料教你成长

低头赶路,也别忘了抬头看天

加班能解决交付的期望么?

6VZJVze.jpg!mobile

如果对你有帮助,让大家也看看呗~


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK