通常意义上来说,app 可以读取 mac 地址,但仅限于用户已经阅读了隐私内容,并且隐私内容中也告知了用户,app 会运行期间采集 wlan mac 地址等信息。所以如果没等用户同意隐私政策中的内容,就开始提前读取 mac 地址,这是不合法的行为。工信部抽查中如果发现此类行为,app 会被通告整改,整改不力的将强制从应用市场下架,后果还是很严重的。

但这里忽略一个问题,如果我们的 app 本身代码没有读取 mac 地址,那如何是好?自己的代码心中有数,实在不行可以全局搜索,如果第三方库偷偷摸摸的读取 mac 地址,这问题解决起来就有点棘手了。首先我们要复现出来确实存在读取 mac 地址的行为,这里最好的办法就是直接在 android framework 和 Linux kernel 层面截获读取 mac 地址的信息。当然还可以通过安装 VirtualXposed 的方法来定位。

我这里使用 NanoPC-T4 单板搭载 Android 8.0 系统进行实验,对于有android源码来说才能这样定位问题,如果只是 app 开发者,建议参考使用VirtualXposed的方法。

一、获取 mac 地址

你要截获获取 mac 地址信息,当然先要知道哪些接口可以读取 mac 地址,可以在调用接口处加入 log 输出。

1.方法一

先拿到 WifiManager,接着获取到 WifiInfo,通过 WifiInfo 对象的 getMacAddress() 方法得到 mac 地址。

WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
WifiInfo winfo = wifi.getConnectionInfo();
String mac =  winfo.getMacAddress();

2.方法二

使用 cat 命令,或者其他方式读取 /sys/class/net/wlan0/address 节点或 /sys/class/net/eth0/address 下的 mac 地址信息。

cat /sys/class/net/wlan0/address
cat /sys/class/net/eth0/address

二、截获获取 mac 地址方法

以下代码中+号表示需要增加的代码。

1. WifiInfo getMacAddress()

对于通过 WifiInfo 对象的 getMacAddress() 方法得到 mac 地址的方法,在 framework java 类 WifiInfo 中直接写入 log,可以清楚的知道那个 app 调用了此接口。

/frameworks/base/wifi/java/android/net/wifi/WifiInfo.java

...
+ import android.util.Log;
...
    public String getMacAddress() {
+        Log.d(TAG, "getMacAddress mac=" + mMacAddress, new RuntimeException("lhw"));
        return mMacAddress;
    }

这会直接打印出调用堆栈的。比如下面是系统进程在调用(system_process) getMacAddress 方法。

2020-12-08 10:56:13.166 490-810/system_process D/WifiInfo: getMacAddress mac=d4:12:43:86:69:88
    java.lang.RuntimeException: lhw
        at android.net.wifi.WifiInfo.getMacAddress(WifiInfo.java:478)
        at com.android.server.wifi.WifiStateMachine.syncRequestConnectionInfo(WifiStateMachine.java:1781)
        at com.android.server.wifi.WifiServiceImpl.getConnectionInfo(WifiServiceImpl.java:1752)
        at android.net.wifi.IWifiManager$Stub.onTransact(IWifiManager.java:320)
        at android.os.Binder.execTransact(Binder.java:697)

2. cat 节点

这要花一些功夫,要到 Linux 内核中找到供读取节点实现的方法。最后可以查到要在 net-sysfs.c 中相应修改。

kernel/net/core/net-sysfs.c

/* use same locking rules as GIFHWADDR ioctl's */
static ssize_t address_show(struct device *dev, struct device_attribute *attr,
			    char *buf)
{
	struct net_device *ndev = to_net_dev(dev);
	ssize_t ret = -EINVAL;

	read_lock(&dev_base_lock);
	if (dev_isalive(ndev))
		ret = sysfs_format_mac(buf, ndev->dev_addr, ndev->addr_len);
	read_unlock(&dev_base_lock);
+    printk(KERN_ALERT "mac address_show");
	return ret;
}

编译系统整体烧写一下,再来运行app查看是否存在读取 mac 地址违规的问题。下面 mac address_show 这句 log 就表示有 app 读取了获取 mac 地址的节点了。下面的 Log 是通过串口读取的。

android 手机获取mac android 获取mac地址权限_违规

三、总结

真实排查过程中发现,经过 360 加固后,首次运行 app 必现读取 mac 地址,当然这是违规行为了!最后更换为腾讯加固再试,问题得以解决。

另外,我们使用的第三方sdk一定要注意,比如友盟在它的开发文档中有提到如何使用才能合规,我们需要参照 sdk 的指导去使用。

最后,保护个人隐私任重而道远,需要开发者和监管部门共同努力!