UNPKG

frida-js

Version:

Pure-JS bindings to control Frida from node.js & browsers.

103 lines 4.96 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.downloadFridaServer = downloadFridaServer; exports.getFridaReleaseDetails = getFridaReleaseDetails; exports.calculateFridaSRI = calculateFridaSRI; const os = require("os"); const stream_1 = require("stream"); async function downloadFridaServer(options) { const { version, platform, arch, sri, ghToken } = options; if (sri && (!version || version === 'latest' || !platform || !arch)) { throw new Error('SRI cannot be used to download Frida unless fixed version, platform & arch values are provided'); } const releaseDetails = await getFridaReleaseDetails(version, ghToken || process.env.GITHUB_TOKEN); const downloadUrl = findFridaDownloadUrl(releaseDetails.assets, { version, platform: platform || os.platform(), arch: arch || os.arch() }); return fetchFridaServer(downloadUrl, sri); } async function getFridaReleaseDetails(version, ghToken) { const headers = !!ghToken ? { Authorization: `token ${ghToken}` } : {}; const response = await fetch(`https://api.github.com/repos/frida/frida/releases/${version && version !== 'latest' ? `tags/${version}` : 'latest'}`, { headers }); if (!response.ok) { console.warn(`Frida releases ${response.status} response, body: `, await response.text(), '\n'); throw new Error(`Frida releases request rejected with ${response.status}`); } return response.json(); } /** * Calculates an SRI value for the given parameters, on a trust-on-first-download basis. * * This fetches and extracts the Frida server for the given parameters, and calculates the * SRI details for the given content. Optionally, specific preferred hash algorithms can * be provided if required. * * This returns a promise, resolving to an array of strings, one for each algorithm used. * Any of these values can be used as an SRI argument for `downloadFridaServer`. */ async function calculateFridaSRI(options, hashOptions = {}) { if (!options.version || options.version === 'latest' || !options.arch || !options.platform) { throw new Error('Cannot calculate SRI without fixed version, platform & arch values'); } // We import SSRI on demand, just when it's required, to avoid bundling in browsers etc. const SSRI = await Promise.resolve().then(() => require('ssri')); const fridaStream = await downloadFridaServer(options); const results = await SSRI.fromStream(fridaStream, hashOptions); return Object.values(results).flatMap((hash) => hash.toString()); } function findFridaDownloadUrl(assets, releaseOptions) { let { version, arch, platform } = releaseOptions; // Map some os.platform()/arch() results into Frida format: if (platform === 'darwin') platform = 'macos'; if (platform === 'win32') platform = 'windows'; if (arch === 'x64') { arch = 'x86_64'; } ; let extension = platform === 'windows' ? 'exe\\.xz' : 'xz'; const assetRegex = new RegExp(`frida-server-[\\d.\\.]+-${platform}-${arch}\\.${extension}`); const asset = assets.find((asset) => asset.name.match(assetRegex)); if (!asset) { console.warn(`No Frida release asset found matching ${assetRegex.toString()}`); throw new Error(`No ${version} frida-server download available for ${platform} ${arch}`); } return asset.browser_download_url; } async function fetchFridaServer(downloadUrl, sri) { // We delay these imports until here, since they're not needed in most cases (where // you're not downloading) and they can be heavy, so good to avoid in browser bundles: const [{ XzReadableStream }, SSRI] = await Promise.all([ Promise.resolve().then(() => require('xz-decompress')), Promise.resolve().then(() => require('ssri')) ]); const resultStream = sri ? SSRI.integrityStream({ integrity: sri }) : new stream_1.PassThrough(); const assetDownload = await fetch(downloadUrl); if (!assetDownload.ok) { throw new Error(`Frida server download was unsuccessful, returned ${assetDownload.status}`); } if (!assetDownload.body) { throw new Error('No body available for Frida server download'); } // Actually start streaming the body next tick, to ensure there's time to set up // any error handlers required on the returned stream before it begins. setTimeout(() => { // Decompress the .xz file to a raw file stream (no tar - it's a single file) const decodeStream = stream_1.Readable.fromWeb( // Node web stream & DOM web stream types aren't a perfect match so we have to cast: new XzReadableStream(assetDownload.body)); decodeStream.pipe(resultStream); decodeStream.on('error', (e) => resultStream.emit('error', e)); }, 0); return resultStream; } //# sourceMappingURL=download-frida.js.map