UNPKG

bagbak

Version:

Dump iOS app from a jailbroken device, based on frida.re

125 lines (103 loc) 2.97 kB
import { Client } from 'ssh2'; /** * @type {Map<string, number>} */ const __port_cache = new Map(); /** * * @param {import("frida").Device} device * @returns {Promise<number>} */ export async function scan(device) { if (process.env['SSH_PORT']) return parseInt(process.env['SSH_PORT']); const cached = __port_cache.get(device.id); if (cached) return cached; const canidates = [22, 44] for (const port of canidates) { const ok = await device.openChannel(`tcp:${port}`) .then((channel) => new Promise((resolve) => { channel .once('data', data => { resolve(data.readUInt32BE() === 0x5353482d); // SSH- channel.destroy(); }) .once('error', () => { resolve(false); }); })) .catch(() => false); if (ok) { __port_cache.set(device.id, port); return port; } } throw Error('Port not found. Target device must be jailbroken and with sshd running.'); } /** * * @param {import("frida").Device} device * @param {import('ssh2').ConnectConfig} config * @returns {Promise<Client>} */ export async function connect(device, config) { const port = await scan(device); const channel = await device.openChannel(`tcp:${port}`); const client = new Client(); return new Promise((resolve, reject) => { client .on('error', reject) .once('ready', () => resolve(client)) .connect(Object.assign({ sock: channel }, config)); }); } /** * * @param {Client} client * @param {string} [initialCommand] * @returns {Promise<void>} */ export async function interactive(client, initialCommand) { const { stdin, stdout, stderr } = process; const { isTTY } = stdout; return new Promise((resolve, reject) => { client.shell({ term: process.env.TERM || 'vt100' }, (err, stream) => { if (err) { return reject(err); } if (isTTY && stdin.setRawMode) { stdin.setRawMode(true); } stream.pipe(stdout); stream.stderr.pipe(stderr); stdin.pipe(stream); if (initialCommand) stream.write(initialCommand + '\n'); const onResize = () => { const [w, h] = process.stdout.getWindowSize(); stream.setWindow(`${stdout.rows}`, `${stdout.columns}`, `${w}`, `${h}`) }; const cleanup = () => { if (isTTY) { stdout.removeListener('resize', onResize); if (stdin.setRawMode) stdin.setRawMode(false); } stream.unpipe(); stream.stderr.unpipe(); stdin.unpipe(); } const onError = (err) => { cleanup(); reject(err); } if (isTTY) { stream.once('data', onResize); process.stdout.on('resize', onResize); } client.once('end', () => onError(new Error('Connection closed'))); stream.on('error', onError).on('end', () => { resolve(); cleanup(); }) }); }); }