xiongzhu 10 luni în urmă
părinte
comite
4ae13ef8df
2 a modificat fișierele cu 409 adăugiri și 0 ștergeri
  1. 170 0
      injects/spoof_provider.js
  2. 239 0
      scripts/provider.js

+ 170 - 0
injects/spoof_provider.js

@@ -0,0 +1,170 @@
+import frida from 'frida'
+import fs from 'fs'
+import url from 'url'
+import path from 'path'
+import util from 'util'
+import Vorpal from 'vorpal'
+import { spawn, execSync } from 'child_process'
+import { setTimeout } from 'timers/promises'
+
+const filePath = url.fileURLToPath(import.meta.url)
+const __dirname = path.dirname(filePath)
+
+function loadSource(filePath) {
+    Log.s(`Loading ${filePath}`)
+    return fs.readFileSync(path.resolve(__dirname, filePath)).toString()
+}
+
+class Log {
+    static TAG = ''
+    static format(...msg) {
+        let m = []
+        for (let i = 0; i < msg.length; i++) {
+            if (typeof msg[i] === 'object') {
+                if ('[object Object]' === msg[i].toString()) {
+                    m.push(util.inspect(msg[i]))
+                }
+            } else {
+                m.push(msg[i])
+            }
+        }
+        m = m.join(' ')
+        return m
+    }
+    static i(...msg) {
+        console.log(`\x1b[30m${this.TAG} ${this.format(...msg)}\x1b[0m`)
+    }
+    static w(...msg) {
+        console.log(`\x1b[33m${this.TAG} ${this.format(...msg)}\x1b[0m`)
+    }
+    static e(...msg) {
+        console.log(`\x1b[31m${this.TAG} ${this.format(...msg)}\x1b[0m`)
+    }
+    static s(...msg) {
+        console.log(`\x1b[32m${this.TAG} ${this.format(...msg)}\x1b[0m`)
+    }
+}
+
+let device = null
+let tracers = []
+
+async function stop() {
+    Log.i('[*] Stopping all tracers')
+    for (const tracer of tracers) {
+        Log.i('[*] Stopping', tracer.pid)
+        tracer.session.detach()
+        try {
+            await device.kill(tracer.pid)
+        } catch (error) {}
+    }
+    process.exit(1)
+}
+
+process.on('SIGTERM', stop)
+process.on('SIGINT', stop)
+
+async function main() {
+    device = await frida.getUsbDevice()
+    device.spawnAdded.connect(onSpawnAdded)
+
+    Log.i('[*] Enabling spawn gating')
+    await device.enableSpawnGating()
+    Log.i('[*] Enabled spawn gating')
+
+    // Log.i("[*] Spawning com.google.android.apps.messaging")
+    // const pid = await device.spawn("com.google.android.apps.messaging")
+    // Log.i("[*] Spawned com.google.android.apps.messaging: " + pid)
+    // const tracer = await Tracer.open(pid)
+    // tracers.push(tracer)
+    const processes = await device.enumerateProcesses()
+    for (const process of processes) {
+        if (process.name.toLowerCase().includes('system_server')  ) {
+            console.log('[*] Attaching to', process.pid, process.name)
+            const session = await device.attach(process.pid)
+            const script = await session.createScript(loadSource('../scripts/provider.js'))
+            await script.load()
+        }
+    }
+}
+
+async function onSpawnAdded(spawn) {
+    try {
+        if (
+            spawn.identifier.toLowerCase().includes('settings') ||
+            spawn.identifier.toLowerCase().includes('provider')
+        ) {
+            Log.i('[*] Tracing', spawn.pid, spawn.identifier)
+            const tracer = await Tracer.open(spawn.pid, '../scripts/provider.js')
+            tracers.push(tracer)
+        } else {
+            Log.i('[*] Resuming', spawn.pid, spawn.identifier)
+            await device.resume(spawn.pid)
+        }
+    } catch (e) {
+        Log.e(`err: ${e}`)
+    }
+}
+
+class Tracer {
+    static async open(pid, source) {
+        const tracer = new Tracer(pid, source)
+        await tracer._initialize()
+        return tracer
+    }
+
+    constructor(pid, sourceFile) {
+        this.pid = pid
+        this.sourceFile = sourceFile
+        this.source = loadSource(sourceFile)
+        this.session = null
+        this.script = null
+    }
+
+    async _initialize() {
+        const session = await device.attach(this.pid)
+        this.session = session
+        session.detached.connect(this._onSessionDetached.bind(this))
+
+        const script = await session.createScript(this.source)
+        this.script = script
+        script.message.connect(this._onScriptMessage.bind(this))
+        await script.load()
+
+        // const script_ssl = await session.createScript(source_ssl)
+        // await script_ssl.load()
+
+        try {
+            await device.resume(this.pid)
+        } catch (e) {
+            Log.e(e)
+        }
+    }
+
+    async reload() {
+        if (this.script) {
+            this.script.unload()
+        }
+        this.source = loadSource(this.sourceFile)
+        this.script = await this.session.createScript(this.source)
+        this.script.message.connect(this._onScriptMessage.bind(this))
+        await this.script.load()
+    }
+
+    _onSessionDetached(reason) {
+        Log.i(`[PID ${this.pid}] onSessionDetached(reason='${reason}')`)
+        const i = tracers.findIndex((tracer) => tracer.pid === this.pid)
+        if (i !== -1) {
+            tracers.splice(i, 1)
+        }
+    }
+
+    _onScriptMessage(message, data) {
+        if (message.type === 'error') {
+            Log.e(`[PID ${this.pid}] onScriptMessage()`, message, data ? JSON.stringify(data) : '')
+        } else {
+            Log.i(`[PID ${this.pid}] onScriptMessage()`, message, data ? JSON.stringify(data) : '')
+        }
+    }
+}
+
+main()

+ 239 - 0
scripts/provider.js

@@ -0,0 +1,239 @@
+class Log {
+    static TAG = '[system_server]'
+    static Debug = true
+    static format(...msg) {
+        let m = []
+        for (let i = 0; i < msg.length; i++) {
+            if (typeof msg[i] === 'object') {
+                m.push(msg[i] + '')
+            } else {
+                m.push(msg[i])
+            }
+        }
+        m = m.join(' ')
+        return m
+    }
+    static i(...msg) {
+        if (!this.Debug) return
+        console.log(`\x1b[30m${this.TAG} ${this.format(...msg)}\x1b[0m`)
+    }
+    static w(...msg) {
+        console.log(`\x1b[33m${this.TAG} ${this.format(...msg)}\x1b[0m`)
+    }
+    static e(...msg) {
+        console.log(`\x1b[31m${this.TAG} ${this.format(...msg)}\x1b[0m`)
+    }
+    static s(...msg) {
+        console.log(`\x1b[32m${this.TAG} ${this.format(...msg)}\x1b[0m`)
+    }
+}
+
+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 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 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
+        }
+
+        const classLoaders = Java.enumerateClassLoadersSync()
+        const SettingsProviderClassLoader = classLoaders.find((i) => i.toString().includes('SettingsProvider'))
+        Log.i('SettingsProviderClassLoader: ' + SettingsProviderClassLoader)
+        Java.classFactory.loader = SettingsProviderClassLoader
+        const genRanHex = (size) => [...Array(size)].map(() => Math.floor(Math.random() * 16).toString(16)).join('')
+
+        const ssaidGms = genRanHex(16)
+        const ssaidVending = genRanHex(16)
+        const SettingsProvider = Java.use('com.android.providers.settings.SettingsProvider')
+        SettingsProvider.getSecureSetting.overload('java.lang.String', 'int').implementation = function (key, userId) {
+            const res = this.getSecureSetting(key, userId)
+            Log.i(`getSecureSetting(${key}, ${userId}) = ${res}`)
+            return res
+        }
+        SettingsProvider.getGlobalSetting.overload('java.lang.String').implementation = function (key) {
+            const res = this.getGlobalSetting(key)
+            Log.i(`getGlobalSetting(${key}) = ${res}`)
+            return res
+        }
+        SettingsProvider.getSystemSetting.overload('java.lang.String', 'int').implementation = function (key, userId) {
+            const res = this.getSystemSetting(key, userId)
+            Log.i(`getSystemSetting(${key}, ${userId}) = ${res}`)
+            if (key === 'android_id') {
+                Log.e(`getSystemSetting(${key}, ${userId}) = ${ssaidGms}`)
+            }
+            return res
+        }
+        SettingsProvider.query.overload(
+            'android.net.Uri',
+            '[Ljava.lang.String;',
+            'java.lang.String',
+            '[Ljava.lang.String;',
+            'java.lang.String'
+        ).implementation = function (uri, projection, selection, selectionArgs, sortOrder) {
+            Log.i(`query(${uri}, ${projection}, ${selection}, ${selectionArgs}, ${sortOrder})`)
+            return this.query(uri, projection, selection, selectionArgs, sortOrder)
+        }
+    })
+})