function trace(tag) { Log.e((tag || '') + Java.use('android.util.Log').getStackTraceString(Java.use('java.lang.Throwable').$new())) } function randomMac() { var mac = '00:16:3e' for (var i = 0; i < 3; i++) { mac += ':' + ('00' + Math.floor(Math.random() * 256).toString(16)).slice(-2) } return mac } function buff2json(buf) { console.log(`buffer length: ${buf.byteLength}`) try { var decoded = String.fromCharCode(...new Uint8Array(buf)) console.log(`decoded: ${decoded}`) return JSON.parse(decoded.trim()) } catch (e) { console.error(e) return null } } class Interaction { failure(err) { console.error(err.message) Java.use('android.util.Log').d('frida-system_server', err.message) } accepted(connection) { console.warn('accepted') connection.input.read(2000).then((data) => { Java.use('android.util.Log').d('frida-system_server', data + '') try { const json = buff2json(data) console.log('received', json) this.messageFn && this.messageFn(json) } catch (e) {} connection.close() }) } accept_loop(listener) { var next_iter = this.accept_loop.bind(this, listener) listener .accept() .then(this.accepted.bind(this)) .catch(this.failure.bind(this)) .finally(function () { setImmediate(next_iter) }) } listened(listener) { console.warn('listened') this.accept_loop(listener) } start(port, messageFn) { this.messageFn = messageFn console.warn('starting on port', port) Socket.listen({ family: 'ipv4', host: '0.0.0.0', port: port }) .then(this.listened.bind(this)) .catch(this.failure.bind(this)) } } setImmediate(() => { Java.perform(function () { const Log = Java.use('android.util.Log') const Uri = Java.use('android.net.Uri') const File = Java.use('java.io.File') const BufferedReader = Java.use('java.io.BufferedReader') const FileInputStream = Java.use('java.io.FileInputStream') const FileOutputStream = Java.use('java.io.FileOutputStream') const InputStreamReader = Java.use('java.io.InputStreamReader') const OutputStreamWriter = Java.use('java.io.OutputStreamWriter') function log(msg) { console.log(`\x1b[32m[system_server] ${msg}\x1b[0m`) Log.d('frida-system_server', msg + '') } function getContext() { try { var ActivityThread = Java.use('android.app.ActivityThread') var application = ActivityThread.currentApplication() return application.getApplicationContext() } catch (e) { console.log(e) return null } } function readFile(file) { if (!file.exists()) { return null } var fileInputStream = FileInputStream.$new(file) var inputStreamReader = InputStreamReader.$new(Java.cast(fileInputStream, Java.use('java.io.InputStream'))) var bufferedReader = BufferedReader.$new(inputStreamReader) var line var content = '' while ((line = bufferedReader.readLine()) !== null) { content += line + '\n' } bufferedReader.close() inputStreamReader.close() fileInputStream.close() return content } function writeFile(file, content) { if (!file.exists()) { file.createNewFile() } var fileOutputStream = FileOutputStream.$new(file) var outputStreamWriter = OutputStreamWriter.$new( Java.cast(fileOutputStream, Java.use('java.io.OutputStream')) ) outputStreamWriter.write(content, 0, content.length) outputStreamWriter.flush() outputStreamWriter.close() fileOutputStream.close() } function readConfig() { const configFile = File.$new('/data/system/config.json') log(`read config from ${configFile.getAbsolutePath()}`) const json = readFile(configFile) if (!json) { return {} } else { log(`config: ${json}`) return JSON.parse(json) } } function saveConfig(config) { const configFile = File.$new('/data/system/config.json') log(`save config to ${configFile.getAbsolutePath()}`) const json = JSON.stringify(config) log(`config: ${json}`) writeFile(configFile, json) } function queryConfig(key) { const context = getContext() if (!context) { return null } const cr = context.getContentResolver() const uri = Uri.parse('content://SimInfo') const cursor = cr.query(uri, null, null, null, null) if (!cursor) { return null } if (!cursor.moveToFirst()) { cursor.close() return null } const idx = cursor.getColumnIndex(key) if (idx < 0) { cursor.close() return null } const value = cursor.getString(idx) cursor.close() return value } // saveConfig({ a: 1 }) let config = readConfig() const DeviceIdentifiersPolicy = Java.use( 'com.android.server.os.DeviceIdentifiersPolicyService$DeviceIdentifiersPolicy' ) DeviceIdentifiersPolicy.getSerial.overload().implementation = function () { const original = this.getSerial() const spoof = readConfig().serialNo || original log(`DeviceIdentifiersPolicy.getSerial() called, returning: ${spoof}, original: ${original}`) return spoof } DeviceIdentifiersPolicy.getSerialForPackage.overload('java.lang.String', 'java.lang.String').implementation = function (callingPackage, callingFeatureId) { const original = this.getSerialForPackage(callingPackage, callingFeatureId) const spoof = readConfig().serialNo || original log(`DeviceIdentifiersPolicy.getSerialForPackage(${callingPackage}, ${callingFeatureId}) called`) log(` ${original} -> ${spoof}`) return spoof } const classLoaders = Java.enumerateClassLoadersSync() for (let i of classLoaders) { if (i.toString().includes('service-connectivity')) { log(i) Java.classFactory.loader = i } } const ConnectivityService = Java.use('com.android.server.ConnectivityService') log(ConnectivityService) // ConnectivityService.getActiveNetworkInfo.overload().implementation = function () { // log('getActiveNetworkInfo') // const res = this.getActiveNetworkInfo() // log(res) // return res // } ConnectivityService.getNetworkInfo.overload('int').implementation = function (networkType) { log('getNetworkInfo') const res = this.getNetworkInfo(networkType) log(res) return res } const InterfaceParams = Java.use('android.net.connectivity.com.android.net.module.util.InterfaceParams') InterfaceParams.getByName.overload('java.lang.String').implementation = function (name) { log('getByName') const res = this.getByName(name) log(res) return res } InterfaceParams.getMacAddress.overload('java.net.NetworkInterface').implementation = function ( networkInterface ) { log('getMacAddress') const res = this.getMacAddress(networkInterface) log(res) return res } const NetworkInterface = Java.use('java.net.NetworkInterface') NetworkInterface.$init.overload().implementation = function () { log('NetworkInterface') const res = this.$new() log(res) return res } NetworkInterface.$init.overload('java.lang.String', 'int', '[Ljava.net.InetAddress;').implementation = function (name, index, addrs) { log('NetworkInterface') const res = this.$new(name, index, addrs) log(res) return res } const wifiClassLoader = classLoaders.find((i) => i.toString().includes('wifi')) Java.classFactory.loader = wifiClassLoader const WifiServiceImpl = Java.use('com.android.server.wifi.WifiServiceImpl') WifiServiceImpl.getFactoryMacAddresses.overload().implementation = function () { const original = this.getFactoryMacAddresses() const spoof = [readConfig().mac || randomMac()] log(`WifiServiceImpl.getFactoryMacAddresses() called`) log(` ${original} -> ${spoof}`) return spoof } WifiServiceImpl.getConnectionInfo.overload('java.lang.String', 'java.lang.String').implementation = function ( callingPackage, callingFeatureId ) { const original = this.getConnectionInfo(callingPackage, callingFeatureId) const originalMac = original.getMacAddress() const originalBSSID = original.getBSSID() const spoofedMac = readConfig().mac || randomMac() const spoofedBSSID = readConfig().bssid || randomMac() original.setMacAddress(spoofedMac) original.setBSSID(spoofedBSSID) log(`WifiServiceImpl.getConnectionInfo(${callingPackage}, ${callingFeatureId}) called`) log(` MAC: ${originalMac} -> ${spoofedMac}`) log(` BSSID: ${originalBSSID} -> ${spoofedBSSID}`) return original } const btClassLoader = classLoaders.find((i) => i.toString().includes('service-bluetooth')) Java.classFactory.loader = btClassLoader const BluetoothManagerService = Java.use('com.android.server.bluetooth.BluetoothManagerService') BluetoothManagerService.getAddress.overload('android.content.AttributionSource').implementation = function ( source ) { const res = this.getAddress(source) log(`BluetoothManagerService.getAddress() called: packageName:${source.getPackageName()} -> ${res}`) return res } }) })