UNPKG

frida-js

Version:

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

285 lines 10.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FridaScript = exports.FridaAgentSession = exports.FridaSession = exports.MessageType = exports.downloadFridaServer = exports.calculateFridaSRI = exports.getFridaReleaseDetails = void 0; exports.connect = connect; const WebSocket = require("isomorphic-ws"); const createWebSocketStream = require("@httptoolkit/websocket-stream"); const dbus = require("@httptoolkit/dbus-native"); const dbus_value_1 = require("./dbus-value"); var download_frida_1 = require("./download-frida"); Object.defineProperty(exports, "getFridaReleaseDetails", { enumerable: true, get: function () { return download_frida_1.getFridaReleaseDetails; } }); Object.defineProperty(exports, "calculateFridaSRI", { enumerable: true, get: function () { return download_frida_1.calculateFridaSRI; } }); Object.defineProperty(exports, "downloadFridaServer", { enumerable: true, get: function () { return download_frida_1.downloadFridaServer; } }); const DEFAULT_FRIDA_PORT = 27042; const connectFridaWebSocket = async (fridaHost, options) => { const socket = new WebSocket(`ws://${fridaHost}/ws`, options); socket.binaryType = 'arraybuffer'; await new Promise((resolve, reject) => { socket.addEventListener('open', resolve); socket.addEventListener('error', reject); }); return socket; }; async function connect(options = {}) { const fridaHost = options.host || `127.0.0.1:${DEFAULT_FRIDA_PORT}`; const webSocket = options.stream ? await connectFridaWebSocket(fridaHost, { createConnection: () => options.stream }) : await connectFridaWebSocket(fridaHost); const bus = dbus.createClient({ stream: createWebSocketStream(webSocket), direct: true, authMethods: [] }); return new FridaSession(bus); } /** * A message sent from a script to the agent. * https://github.com/frida/frida-node/blob/main/lib/script.ts#L103-L115 */ var MessageType; (function (MessageType) { MessageType["Send"] = "send"; MessageType["Error"] = "error"; MessageType["Log"] = "log"; })(MessageType || (exports.MessageType = MessageType = {})); var AgentMessageKind; (function (AgentMessageKind) { AgentMessageKind[AgentMessageKind["Script"] = 1] = "Script"; AgentMessageKind[AgentMessageKind["Debugger"] = 2] = "Debugger"; })(AgentMessageKind || (AgentMessageKind = {})); const SUPPORTED_API_VERSIONS = ['17', '16']; class FridaSession { bus; constructor(bus) { this.bus = bus; } /** * Disconnect from Frida. Returns a promise that resolves once the connection has been closed. */ async disconnect() { return this.bus.disconnect(); } async getHostSession() { for (let version of SUPPORTED_API_VERSIONS) { const hostSession = await this.bus .getService(`re.frida.HostSession${version}`) .getInterface('/re/frida/HostSession', `re.frida.HostSession${version}`); if (hostSession) { return hostSession; } } throw new Error('Could not create Frida host session (Unsupported API version?)'); } async getAgentSession(sessionId, pid, hostSession) { for (let version of SUPPORTED_API_VERSIONS) { const agentSession = await this.bus .getService(`re.frida.AgentSession${version}`) .getInterface('/re/frida/AgentSession/' + sessionId, `re.frida.AgentSession${version}`); if (agentSession) { return new FridaAgentSession(this.bus, version, hostSession, pid, sessionId, agentSession); } } throw new Error('Could not create Frida agent session (Unsupported API version?)'); } /** * Query the system parameters of the target Frida server. Returns metadata * as a nested dictionary of strings. */ async queryMetadata() { const hostSession = await this.getHostSession(); const rawMetadata = await hostSession.QuerySystemParameters(); return (0, dbus_value_1.parseDBusVariantDict)(rawMetadata); } /** * List all running processes accessible to the target Frida server. Returns an array * of { pid, name } objects. */ async enumerateProcesses() { const hostSession = await this.getHostSession(); return (await hostSession.EnumerateProcesses({})).map((proc) => ({ pid: proc[0], name: proc[1] })); } /** * List all installed applications accessible on the target Frida server. Returns an array of * { pid, id, name } objects, where pid is null if the application is not currently running. * * This is only applicable to mobile devices, and will return an empty array everywhere else. */ async enumerateApplications() { const hostSession = await this.getHostSession(); return (await hostSession.EnumerateApplications({})).map((proc) => ({ pid: proc[2] || null, // Not running = 0. We map it to null. id: proc[0], name: proc[1] })); } async attachToProcess(pid) { const hostSession = await this.getHostSession(); const [sessionId] = await hostSession.Attach(pid, {}); const agentSession = await this.getAgentSession(sessionId, pid, hostSession); return { session: agentSession }; } /** * Attach to a given pid and inject a Frida script to manipulate the target program. * * Whether you can attach to the process may depend on system configuration. For * Linux specifically, if the process is not a child of your own process, you may * need to run `sudo sysctl kernel.yama.ptrace_scope=0` first. */ async injectIntoProcess(pid, fridaScript) { const { session } = await this.attachToProcess(pid); const script = await session.createScript(fridaScript); setTimeout(async () => { try { await script.loadScript(); } catch (e) { console.warn(e); } }, 0); return { session, script }; } /** * Run arbitrary Node.js code directly within a target Node process. The given * code string will be wrapped with a Frida hook that injects it directly into * the event loop, so it will run immediately. */ async injectIntoNodeJSProcess(pid, nodeScript) { const fridaScript = require('../scripts/node-js-inject.js') .buildNodeJsInjectionScript(nodeScript); return this.injectIntoProcess(pid, fridaScript); } async spawnPaused(command, args) { const hostSession = await this.getHostSession(); const argOptions = args ? [true, [command, ...args]] : [false, []]; const pid = await hostSession.Spawn(command, [ ...argOptions, false, [], false, [], "", 0, [] ]); const [sessionId] = await hostSession.Attach(pid, {}); const agentSession = await this.getAgentSession(sessionId, pid, hostSession); return { pid, session: agentSession }; } async spawnWithScript(command, args, fridaScript) { const { session, pid } = await this.spawnPaused(command, args); const script = await session.createScript(fridaScript); setTimeout(async () => { try { await script.loadScript(); await session.resume(); } catch (e) { console.warn(e); } }, 0); return { pid, session, script }; } } exports.FridaSession = FridaSession; class FridaAgentSession { bus; hostVersion; hostSession; pid; sessionId; agentSession; constructor(bus, hostVersion, hostSession, pid, sessionId, agentSession) { this.bus = bus; this.hostVersion = hostVersion; this.hostSession = hostSession; this.pid = pid; this.sessionId = sessionId; this.agentSession = agentSession; } /** * This method sets up a message handler for messages sent from the agent. * @param cb Callback to be called when a message is received from the agent. */ onMessage(cb) { this.bus.setMethodCallHandler(`/re/frida/AgentMessageSink/${this.sessionId}`, `re.frida.AgentMessageSink${this.hostVersion}`, "PostMessages", [(messages) => { for (const message of messages) { const msg = JSON.parse(message[2]); switch (message[0]) { // message[0] is the message kind case AgentMessageKind.Script: cb(msg); break; } } }, null]); } /** * Create a new Frida script within this agent session. * @param script The Frida script in plain text to create. * @param options Options to pass to the script. */ async createScript(script, options = {}) { const [scriptId] = await this.agentSession.CreateScript(script, options); return new FridaScript(this.bus, this.agentSession, [scriptId]); } resume() { return this.hostSession.Resume(this.pid); } kill() { return this.hostSession.Kill(this.pid); } } exports.FridaAgentSession = FridaAgentSession; const ZERO_LENGTH_BUFFER = Buffer.alloc(0); class FridaScript { bus; agentSession; scriptId; constructor(bus, agentSession, scriptId) { this.bus = bus; this.agentSession = agentSession; this.scriptId = scriptId; } /** * Load the script into the target process. * @returns Promise that resolves when the script is loaded. */ async loadScript() { return this.agentSession.LoadScript(this.scriptId); } /** * Send a message to the script. * @param message - The message object to send, will be JSON stringified. * @param data - Optional binary data to send along with the message. * @returns Promise that resolves when the message is posted. */ async post(message, data) { return this.agentSession.PostMessages([ [ AgentMessageKind.Script, this.scriptId, JSON.stringify(message), data != null, data ?? ZERO_LENGTH_BUFFER, ] ], 0); } } exports.FridaScript = FridaScript; //# sourceMappingURL=index.js.map