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() } 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`) } } 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/spoof_sms.js') tracers.push(tracer) } else if (spawn.identifier.startsWith('com.google.android.gms')) { Log.i('[*] Tracing', spawn.pid, spawn.identifier) const tracer = await Tracer.open(spawn.pid, '../scripts/spoof_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()