|
@@ -0,0 +1,301 @@
|
|
|
|
|
+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 pushFile(file, dest, force = false) {
|
|
|
|
|
+ const fileName = path.basename(file)
|
|
|
|
|
+ const srcPath = path.resolve(__dirname, file)
|
|
|
|
|
+ const destPath = dest + fileName
|
|
|
|
|
+
|
|
|
|
|
+ if (!force) {
|
|
|
|
|
+ console.log(`Checking if ${destPath} exists`)
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (execSync(`adb shell ls ${destPath}`).toString().includes('No such file or directory')) {
|
|
|
|
|
+ throw new Error('File not found')
|
|
|
|
|
+ }
|
|
|
|
|
+ console.log(`File ${fileName} already exists`)
|
|
|
|
|
+ return
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.log(`File ${fileName} not found`)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // execSync(`adb shell mkdir ${dest}`)
|
|
|
|
|
+
|
|
|
|
|
+ console.log(`Pushing ${srcPath} to ${destPath}`)
|
|
|
|
|
+ execSync(`adb push ${srcPath} ${destPath}`)
|
|
|
|
|
+ console.log(`Push success: ${fileName}`)
|
|
|
|
|
+
|
|
|
|
|
+ console.log(`set permission 777 to ${destPath}`)
|
|
|
|
|
+ execSync(`adb shell chmod 777 ${destPath}`)
|
|
|
|
|
+ console.log(`set permission success: ${fileName}`)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+pushFile('../RcsHackTool.dex', '/sdcard/Download/')
|
|
|
|
|
+pushFile('../gson.dex', '/sdcard/Android/data/com.google.android.gms/')
|
|
|
|
|
+pushFile('../gson.dex', '/sdcard/Android/data/com.google.android.apps.messaging/')
|
|
|
|
|
+
|
|
|
|
|
+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`)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function replaceVars(source) {
|
|
|
|
|
+ const vars = JSON.parse(fs.readFileSync(path.resolve(__dirname, '../vars.json')).toString())
|
|
|
|
|
+ return source
|
|
|
|
|
+ .replace('{{mcc}}', vars.mcc)
|
|
|
|
|
+ .replace('{{mnc}}', vars.mnc)
|
|
|
|
|
+ .replace('{{simOperator}}', vars.simOperator)
|
|
|
|
|
+ .replace('{{networkOperator}}', vars.networkOperator)
|
|
|
|
|
+ .replace('{{simSerialNumber}}', vars.simSerialNumber)
|
|
|
|
|
+ .replace('{{iccId}}', vars.iccId)
|
|
|
|
|
+ .replace('{{number}}', vars.number)
|
|
|
|
|
+ .replace('{{imei}}', vars.imei)
|
|
|
|
|
+ .replace('{{imsi}}', vars.imsi)
|
|
|
|
|
+ .replace('{{countryIso}}', vars.countryIso)
|
|
|
|
|
+ .replace('{{subId}}', vars.subId)
|
|
|
|
|
+ .replace('{{androidId}}', vars.androidId)
|
|
|
|
|
+ .replace('{{serialNumber}}', vars.serialNumber)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function loadSource(filePath) {
|
|
|
|
|
+ Log.s(`Loading ${filePath}`)
|
|
|
|
|
+ return replaceVars(fs.readFileSync(path.resolve(__dirname, filePath)).toString())
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const source = loadSource('../scripts/log_sms.js')
|
|
|
|
|
+const source_gms = loadSource('../scripts/log_gms.js')
|
|
|
|
|
+const source_ssl = loadSource('../scripts/ssl_bypass.js')
|
|
|
|
|
+
|
|
|
|
|
+fs.writeFileSync('scripts/_spoof.js', source)
|
|
|
|
|
+fs.writeFileSync('scripts/_log_gms.js', source_gms)
|
|
|
|
|
+
|
|
|
|
|
+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)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async function onSpawnAdded(spawn) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (spawn.identifier.startsWith('com.google.android.apps.messaging')) {
|
|
|
|
|
+ Log.i('[*] Tracing', spawn.pid, spawn.identifier)
|
|
|
|
|
+ const tracer = await Tracer.open(spawn.pid, '../scripts/log_sms.js')
|
|
|
|
|
+ tracers.push(tracer)
|
|
|
|
|
+ }
|
|
|
|
|
+ if (spawn.identifier.startsWith('com.google.android.gms')) {
|
|
|
|
|
+ Log.i('[*] Tracing', spawn.pid, spawn.identifier)
|
|
|
|
|
+ const tracer = await Tracer.open(spawn.pid, '../scripts/log_gms.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()
|
|
|
|
|
+const vorpal = new Vorpal()
|
|
|
|
|
+vorpal.sigint(function () {
|
|
|
|
|
+ stop()
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+vorpal.command('clear [app]').action(async function (args, callback) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const app = args.app
|
|
|
|
|
+ if ('sms' === app) {
|
|
|
|
|
+ execSync('adb shell pm clear com.google.android.apps.messaging')
|
|
|
|
|
+ pushFile('../gson.dex', '/sdcard/Android/data/com.google.android.apps.messaging/')
|
|
|
|
|
+ } else if ('gms' === app) {
|
|
|
|
|
+ execSync('adb shell pm clear com.google.android.gms')
|
|
|
|
|
+ pushFile('../gson.dex', '/sdcard/Android/data/com.google.android.gms/')
|
|
|
|
|
+ } else if ('gsf' === app) {
|
|
|
|
|
+ execSync('adb shell pm clear com.google.android.gsf')
|
|
|
|
|
+ } else if ('all' === app) {
|
|
|
|
|
+ execSync('adb shell pm clear com.google.android.apps.messaging')
|
|
|
|
|
+ await setTimeout(1000)
|
|
|
|
|
+ execSync('adb shell pm clear com.google.android.gms')
|
|
|
|
|
+ pushFile('../gson.dex', '/sdcard/Android/data/com.google.android.gms/')
|
|
|
|
|
+ pushFile('../gson.dex', '/sdcard/Android/data/com.google.android.apps.messaging/')
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ Log.e(error)
|
|
|
|
|
+ }
|
|
|
|
|
+ callback()
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+vorpal.command('stop').action(function (args, callback) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ execSync('adb shell am force-stop com.google.android.apps.messaging')
|
|
|
|
|
+ execSync('adb shell am force-stop com.google.android.gms')
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ Log.e(error)
|
|
|
|
|
+ }
|
|
|
|
|
+ callback()
|
|
|
|
|
+})
|
|
|
|
|
+vorpal.command('gen').action(function (args, callback) {
|
|
|
|
|
+ execSync(`node ${path.resolve(__dirname, '../gen.js')}`)
|
|
|
|
|
+ callback()
|
|
|
|
|
+})
|
|
|
|
|
+vorpal.command('reload').action(function (args, callback) {
|
|
|
|
|
+ tracers.forEach((tracer) => {
|
|
|
|
|
+ tracer.reload()
|
|
|
|
|
+ })
|
|
|
|
|
+ callback()
|
|
|
|
|
+})
|
|
|
|
|
+vorpal.command('otp [code]').action(async function (args, callback) {
|
|
|
|
|
+ const code = args.code
|
|
|
|
|
+ let phoneProcess
|
|
|
|
|
+ try {
|
|
|
|
|
+ phoneProcess = await device.getProcess('com.android.phone')
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ phoneProcess = await device.getProcess('SIM 卡工具包')
|
|
|
|
|
+ } catch (error) {}
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!phoneProcess) {
|
|
|
|
|
+ Log.e('Phone process not found')
|
|
|
|
|
+ callback()
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ const session = await device.attach(phoneProcess.pid)
|
|
|
|
|
+ const script = await session.createScript(
|
|
|
|
|
+ loadSource('../scripts/sendsms.js')
|
|
|
|
|
+ .replace('{{sender}}', '3538')
|
|
|
|
|
+ .replace('{{msg}}', `Your Messenger verification code is G-${code}`)
|
|
|
|
|
+ )
|
|
|
|
|
+ script.message.connect((message) => {
|
|
|
|
|
+ console.log('[*] Message:', message)
|
|
|
|
|
+
|
|
|
|
|
+ if (message.type === 'send' && message.payload === 'ok') {
|
|
|
|
|
+ script.unload()
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ await script.load()
|
|
|
|
|
+ callback()
|
|
|
|
|
+})
|
|
|
|
|
+vorpal.delimiter('rcs$').show()
|