Hi, I thought I’d share some technical aspects of an upcoming App Cloner feature that allows hiding your Wi-Fi MAC address from cloned apps.
The MAC address uniquely identifies networking hardware, for instance the Wi-Fi adapter in your device. A MAC address may look like 14:1A:A3:9B:1B:58. Traditionally apps had easy access to this address and this was a privacy risk since there was nothing stopping an app from uploading this address to the cloud. A quick scan of nearby Wi-Fi devices could then identify and track your device via this address, even if not connected to any Wi-Fi hotspot.
With the release of Android 6.0 Google started hiding the MAC address from apps to protect the users privacy, which makes sense. Any call to WifiManager.getConnectionInfo().getMacAddress() now returns ’02:00:00:00:00:00′ instead of the real MAC address.
Unfortunately it’s still very easy for apps to bypass this. While many apps may use the WifiManager to read the MAC address, a simple Java call to NetworkInterface.getNetworkInterfaces() allows enumerating the network interfaces until you find ‘wlan0’, on which you can then call getHardwareAddress() to obtain the MAC address.
Apps like Device Info (https://play.google.com/store/apps/details?id=com.alphabetlabs.deviceinfo) demonstrate this, even on Android 7.0.
App Cloner to the rescue 🙂 I’ve implemented a new option called ‘Hide Wi-Fi MAC address’, which does the following.
It hooks into the app’s WifiManager system service to intercept calls to getConnectionInfo(). It then replaces the MAC address on the WifiInfo result. This covers the use of WifiManager before Android 6.0 and reports back ’02:00:00:00:00:00′ instead of the real MAC address.
But this doesn’t cover the direct use of NetworkInterface.getNetworkInterfaces(). The way getNetworkInterfaces() works internally (inside Android itself) is to read the MAC address from the file ‘/sys/class/net/wlan0/address’. To intercept access to this file App Cloner hooks into the app’s Libcore implementation replacing calls to open(). Normally this just forwards all calls, but if it detects access to ‘/sys/class/net/wlan0/address’ it creates a small temporary file containing ’02:00:00:00:00:00′ and then points the file path to this dummy file instead of the original one. The system then reads the MAC address from what it thinks is ‘/sys/class/net/wlan0/address’ but in reality is a redirected file.
Unfortunately on Android 7.0 the story is yet again different. Android 7.0 now uses OpenJDK and its implementation of NetworkInterface.getNetworkInterfaces() is very different. Basically the call almost immediately enters native code with no chance of adding a hook easily (at least in the Java world).
Here I opted for a different approach: during cloning App Cloner replaces all references to ‘java.net.NetworkInterface’ by ‘java.net.NetworkInterfacf’, just a slight name change so that the string pool doesn’t get out of order. App Cloner then injects its own class called ‘java.net.NetworkInterfacf’, which forwards all calls to the original NetworkInterface implementation except for the getHardwareAddress() call, which returns ’02:00:00:00:00:00′.
This covers three different possibilities of apps getting hold of your Wi-Fi MAC address on various versions of Android. Of course, if an app uses native code I’m sure it’s still possible to read out the real MAC address after all. But I found these implementation details quite interesting…
I’ll do some more testing but it should make it into the next update.
That’s good news for privacy advocates.