frida
Version:
Inject JavaScript to explore native apps on Windows, Mac, Linux, iOS and Android
981 lines (980 loc) • 37.9 kB
JavaScript
import bindings from "bindings";
import util from "util";
import { Minimatch } from "minimatch";
import { Duplex } from "stream";
const { inspect } = util;
const binding = bindings({
bindings: "frida_binding",
try: [
["module_root", "build", "bindings"],
[process.cwd(), "bindings"],
]
});
var MessageTypeImpl;
(function (MessageTypeImpl) {
MessageTypeImpl["Send"] = "send";
MessageTypeImpl["Error"] = "error";
})(MessageTypeImpl || (MessageTypeImpl = {}));
binding.MessageType = MessageTypeImpl;
export const MessageType = binding.MessageType;
var LogLevelImpl;
(function (LogLevelImpl) {
LogLevelImpl["Info"] = "info";
LogLevelImpl["Warning"] = "warning";
LogLevelImpl["Error"] = "error";
})(LogLevelImpl || (LogLevelImpl = {}));
binding.LogLevel = LogLevelImpl;
export const LogLevel = binding.LogLevel;
{
const STANDARD_SPAWN_OPTION_NAMES = new Set([
"argv",
"envp",
"env",
"cwd",
"stdio",
]);
class ScriptServices {
exportsProxy = new ScriptExportsProxy(this);
#script;
#pendingRequests = new Map();
#nextRequestId = 1;
constructor(script) {
this.#script = script;
process.nextTick(() => {
script.message.connect(() => { });
});
}
handleMessageIntercept = (message, data) => {
if (message.type === MessageType.Send && isRpcSendMessage(message)) {
const [, id, operation, ...params] = message.payload;
this.#onRpcMessage(id, operation, params, data);
return false;
}
else if (isLogMessage(message)) {
const opaqueMessage = message;
const logMessage = opaqueMessage;
this.#script.logHandler(logMessage.level, logMessage.payload);
return false;
}
return true;
};
request(operation, params, data, cancellable = null) {
return new Promise((resolve, reject) => {
const id = this.#nextRequestId++;
const complete = (error, result) => {
if (cancellable !== null) {
cancellable.cancelled.disconnect(onOperationCancelled);
}
this.#script.destroyed.disconnect(onScriptDestroyed);
this.#pendingRequests.delete(id);
if (error === null) {
resolve(result);
}
else {
reject(error);
}
};
function onScriptDestroyed() {
complete(new Error("Script is destroyed"));
}
function onOperationCancelled() {
complete(new Error("Operation was cancelled"));
}
this.#pendingRequests.set(id, complete);
this.#script.post(["frida:rpc", id, operation, ...params], data);
this.#script.destroyed.connect(onScriptDestroyed);
if (cancellable !== null) {
cancellable.cancelled.connect(onOperationCancelled);
if (cancellable.isCancelled) {
onOperationCancelled();
return;
}
}
if (this.#script.isDestroyed) {
onScriptDestroyed();
}
});
}
#onRpcMessage(id, operation, params, data) {
if (operation === RpcOperation.Ok || operation === RpcOperation.Error) {
const callback = this.#pendingRequests.get(id);
if (callback === undefined) {
return;
}
let value = null;
let error = null;
if (operation === RpcOperation.Ok) {
if (data !== null) {
value = (params.length > 1) ? [params[1], data] : data;
}
else {
value = params[0];
}
}
else {
const [message, name, stack, rawErr] = params;
error = new Error(message);
error.name = name;
error.stack = stack;
Object.assign(error, rawErr);
}
callback(error, value);
}
}
}
class ScriptExportsProxy {
constructor(rpcController) {
return new Proxy(this, {
has(target, property) {
return !isReservedMethodName(property);
},
get(target, property, receiver) {
if (typeof property === "symbol") {
if (property === inspect.custom) {
return inspectProxy;
}
return undefined;
}
if (property in target) {
return target[property];
}
if (isReservedMethodName(property)) {
return undefined;
}
return (...args) => {
let cancellable = null;
if (args[args.length - 1] instanceof Cancellable) {
cancellable = args.pop();
}
let data = null;
if (Buffer.isBuffer(args[args.length - 1])) {
data = args.pop();
}
return rpcController.request("call", [property, args], data, cancellable);
};
},
set(target, property, value, receiver) {
if (typeof property === "symbol") {
return false;
}
target[property] = value;
return true;
},
ownKeys(target) {
return Object.getOwnPropertyNames(target);
},
getOwnPropertyDescriptor(target, property) {
if (property in target) {
return Object.getOwnPropertyDescriptor(target, property);
}
if (isReservedMethodName(property)) {
return undefined;
}
return {
writable: true,
configurable: true,
enumerable: true
};
},
});
}
}
function inspectProxy() {
return "ScriptExportsProxy {}";
}
let RpcOperation;
(function (RpcOperation) {
RpcOperation["Ok"] = "ok";
RpcOperation["Error"] = "error";
})(RpcOperation || (RpcOperation = {}));
function isInternalMessage(message) {
return isRpcMessage(message) || isLogMessage(message);
}
function isRpcMessage(message) {
return message.type === MessageType.Send && isRpcSendMessage(message);
}
function isRpcSendMessage(message) {
const payload = message.payload;
if (!Array.isArray(payload)) {
return false;
}
return payload[0] === "frida:rpc";
}
function isLogMessage(message) {
return message.type === "log";
}
function log(level, text) {
switch (level) {
case LogLevel.Info:
console.log(text);
break;
case LogLevel.Warning:
console.warn(text);
break;
case LogLevel.Error:
console.error(text);
break;
}
}
const reservedMethodNames = new Set([
"then",
"catch",
"finally",
]);
function isReservedMethodName(name) {
return reservedMethodNames.has(name.toString());
}
const IO_PRIORITY_DEFAULT = 0;
class IOStreamAdapter extends Duplex {
#impl;
#input;
#output;
#pending = new Set();
#cancellable = new Cancellable();
constructor(impl) {
super({});
this.#impl = impl;
this.#input = impl.inputStream;
this.#output = impl.outputStream;
}
async _destroy(error, callback) {
this.#cancellable.cancel();
for (const operation of this.#pending) {
try {
await operation;
}
catch (e) {
}
}
try {
await this.#impl.close(IO_PRIORITY_DEFAULT);
}
catch (e) {
}
callback(error);
}
_read(size) {
const operation = this.#input.read(size, IO_PRIORITY_DEFAULT, this.#cancellable)
.then((data) => {
const isEof = data.length === 0;
if (isEof) {
this.push(null);
return;
}
this.push(data);
})
.catch((error) => {
if (this.#impl.closed) {
this.push(null);
}
this.emit("error", error);
});
this.#track(operation);
}
_write(chunk, encoding, callback) {
let data;
if (Buffer.isBuffer(chunk)) {
data = chunk;
}
else {
data = Buffer.from(chunk, encoding);
}
const operation = this.#writeAll(data)
.then(() => {
callback(null);
})
.catch((error) => {
callback(error);
});
this.#track(operation);
}
async #writeAll(data) {
let offset = 0;
do {
const n = await this.#output.write(data.slice(offset), IO_PRIORITY_DEFAULT, this.#cancellable);
offset += n;
} while (offset !== data.length);
}
#track(operation) {
this.#pending.add(operation);
operation
.catch(_ => { })
.finally(() => {
this.#pending.delete(operation);
});
}
}
class CallbackAuthenticationService extends binding.AbstractAuthenticationService {
#callback;
constructor(callback) {
super();
this.#callback = callback;
}
async authenticate(token, cancellable) {
const info = await this.#callback(token);
return JSON.stringify(info);
}
}
function parseSocketAddress(address) {
const family = address.family;
switch (family) {
case SocketFamily.Unix: {
const addr = address;
switch (addr.addressType) {
case UnixSocketAddressType.Anonymous:
return {
family: "unix:anonymous",
};
case UnixSocketAddressType.Path:
return {
family: "unix:path",
path: addr.path.toString(),
};
case UnixSocketAddressType.Abstract:
case UnixSocketAddressType.AbstractPadded:
return {
family: "unix:abstract",
path: addr.path,
};
}
break;
}
case SocketFamily.Ipv4: {
const addr = address;
return {
family: "ipv4",
address: addr.address.toString(),
port: addr.port,
};
}
case SocketFamily.Ipv6: {
const addr = address;
return {
family: "ipv6",
address: addr.address.toString(),
port: addr.port,
flowlabel: addr.flowinfo,
scopeid: addr.scopeId,
};
}
}
throw new Error("invalid BaseSocketAddress");
}
function objectToStrv(object) {
return Object.entries(object).map(([k, v]) => `${k}=${v}`);
}
class SignalWrapper {
#source;
#transform;
#intercept;
#handlers = new Set();
constructor(source, options) {
this.#source = source;
if (options === undefined || options.transform === undefined) {
this.#intercept = options?.intercept;
}
else {
this.#transform = options.transform;
this.#intercept = options.intercept;
}
}
connect(handler) {
this.#handlers.add(handler);
if (this.#handlers.size === 1) {
this.#source.connect(this.#wrappedHandler);
}
}
disconnect(handler) {
this.#handlers.delete(handler);
if (this.#handlers.size === 0) {
this.#source.disconnect(this.#wrappedHandler);
}
}
#wrappedHandler = ((...sourceArgs) => {
let targetArgs;
const transform = this.#transform;
if (transform === undefined) {
targetArgs = sourceArgs;
}
else {
targetArgs = transform(...sourceArgs);
}
const intercept = this.#intercept;
if (intercept !== undefined) {
if (!intercept(...targetArgs)) {
return;
}
}
for (const handler of this.#handlers) {
handler(...targetArgs);
}
});
}
function inspectWrapper(object, name, properties, depth, options) {
if (depth < 0) {
return options.stylize(`[${name}]`, "special");
}
const summary = Object.fromEntries(properties.map(name => [name, object[name]]));
const nextOptions = Object.assign({}, options, {
depth: (options.depth === null) ? null : depth - 1
});
return name + " " + inspect(summary, nextOptions);
}
binding.DeviceManager.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "DeviceManager", [], depth, options);
};
binding._Device.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "Device", ["id", "name", "icon", "type", "bus"], depth, options);
};
class Device extends binding._Device {
async getProcess(name, options = {}, cancellable) {
const { scope = Scope.Minimal, } = options;
const processes = await this.enumerateProcesses({ scope }, cancellable);
const mm = new Minimatch(name.toLowerCase());
const matching = processes.filter(process => mm.match(process.name.toLowerCase()));
if (matching.length === 1) {
return matching[0];
}
else if (matching.length > 1) {
throw new Error("Ambiguous name; it matches: " + matching.map(process => `${process.name} (pid: ${process.pid})`).join(", "));
}
else {
throw new Error("Process not found");
}
}
async #getPid(target, cancellable) {
if (typeof target === "number") {
return target;
}
const process = await this.getProcess(target, {}, cancellable);
return process.pid;
}
async querySystemParameters(cancellable) {
const result = await this._querySystemParameters(cancellable);
return result;
}
async spawn(programOrArgv, opts, cancellable) {
const options = {};
let program;
let argv;
if (typeof programOrArgv === "string") {
program = programOrArgv;
argv = opts?.argv;
}
else {
program = programOrArgv[0];
argv = programOrArgv;
if (argv.length === 1) {
argv = undefined;
}
}
if (argv !== undefined) {
options.argv = argv;
}
if (opts !== undefined) {
const envp = opts.envp;
if (envp !== undefined) {
options.envp = objectToStrv(envp);
}
const env = opts.env;
if (env !== undefined) {
options.env = objectToStrv(env);
}
const cwd = opts.cwd;
if (cwd !== undefined) {
options.cwd = cwd;
}
options.aux = Object.fromEntries(Object.entries(opts).filter(([k, v]) => !STANDARD_SPAWN_OPTION_NAMES.has(k)));
}
const result = await this._spawn(program, options, cancellable);
return result;
}
async input(target, data, cancellable) {
const pid = await this.#getPid(target, cancellable);
const result = await this._input(pid, data, cancellable);
return result;
}
async resume(target, cancellable) {
const pid = await this.#getPid(target, cancellable);
const result = await this._resume(pid, cancellable);
return result;
}
async kill(target, cancellable) {
const pid = await this.#getPid(target, cancellable);
const result = await this._kill(pid, cancellable);
return result;
}
async attach(target, options, cancellable) {
const pid = await this.#getPid(target, cancellable);
const result = await this._attach(pid, options, cancellable);
return result;
}
async injectLibraryFile(target, path, entrypoint, data, cancellable) {
const pid = await this.#getPid(target, cancellable);
const result = await this._injectLibraryFile(pid, path, entrypoint, data, cancellable);
return result;
}
async injectLibraryBlob(target, blob, entrypoint, data, cancellable) {
const pid = await this.#getPid(target, cancellable);
const result = await this._injectLibraryBlob(pid, blob, entrypoint, data, cancellable);
return result;
}
async openChannel(address, cancellable) {
const result = await this._openChannel(address, cancellable);
return new IOStreamAdapter(result);
}
output = new SignalWrapper(this._output, {
transform(pid, fd, data) {
return [pid, fd, data];
},
});
uninjected = new SignalWrapper(this._uninjected, {
transform(id) {
return [id];
},
});
}
binding.Device = Device;
binding.Application.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "Application", ["identifier", "name", "pid", "parameters"], depth, options);
};
binding.Process.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "Process", ["pid", "name", "parameters"], depth, options);
};
binding.Spawn.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "Spawn", ["pid", "identifier"], depth, options);
};
binding.Child.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "Child", ["pid", "parentPid", "origin", "identifier", "path", "argv", "envp"], depth, options);
};
binding.Crash.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "Crash", ["pid", "processName", "summary", "report", "parameters"], depth, options);
};
binding._Bus.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "Bus", [], depth, options);
};
class Bus extends binding._Bus {
post(message, data) {
const json = JSON.stringify(message);
this._post(json, data);
}
message = new SignalWrapper(this._message, {
transform(json, data) {
return [JSON.parse(json), data];
},
});
}
binding.Bus = Bus;
binding.Service.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "Service", [], depth, options);
};
binding.Session.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "Session", ["pid", "persistTimeout"], depth, options);
};
binding._Script.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "Script", [], depth, options);
};
class Script extends binding._Script {
#services = new ScriptServices(this);
logHandler = log;
get isDestroyed() {
return this._isDestroyed();
}
get exports() {
return this.#services.exportsProxy;
}
get defaultLogHandler() {
return log;
}
post(message, data) {
const json = JSON.stringify(message);
this._post(json, data);
}
async enableDebugger(options, cancellable) {
const port = options?.port ?? 0;
const result = await this._enableDebugger(port, cancellable);
return result;
}
message = new SignalWrapper(this._message, {
transform(json, data) {
return [JSON.parse(json), data];
},
intercept: this.#services.handleMessageIntercept,
});
}
binding.Script = Script;
binding.PortalMembership.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "PortalMembership", [], depth, options);
};
binding.PackageManager.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "PackageManager", ["registry"], depth, options);
};
binding.Package.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "Package", ["name", "version", "description", "url"], depth, options);
};
binding.PackageSearchResult.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "PackageSearchResult", ["packages", "total"], depth, options);
};
binding.PackageInstallResult.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "PackageInstallResult", ["packages"], depth, options);
};
binding.ControlService.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "ControlService", [], depth, options);
};
binding._PortalService.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "PortalService", ["device", "clusterParams", "controlParams"], depth, options);
};
class PortalService extends binding._PortalService {
constructor(options) {
const clusterParams = options?.clusterParams ?? new EndpointParameters();
const controlParams = options?.controlParams ?? null;
super(clusterParams, controlParams);
}
post(connectionId, message, data) {
const json = JSON.stringify(message);
this._post(connectionId, json, data);
}
narrowcast(tag, message, data) {
const json = JSON.stringify(message);
this._narrowcast(tag, json, data);
}
broadcast(message, data) {
const json = JSON.stringify(message);
this._broadcast(json, data);
}
nodeConnected = new SignalWrapper(this._nodeConnected, {
transform(connectionId, remoteAddress) {
return [connectionId, parseSocketAddress(remoteAddress)];
},
});
nodeJoined = new SignalWrapper(this._nodeJoined, {
transform(connectionId, application) {
return [connectionId, application];
},
});
nodeLeft = new SignalWrapper(this._nodeLeft, {
transform(connectionId, application) {
return [connectionId, application];
},
});
nodeDisconnected = new SignalWrapper(this._nodeDisconnected, {
transform(connectionId, remoteAddress) {
return [connectionId, parseSocketAddress(remoteAddress)];
},
});
controllerConnected = new SignalWrapper(this._controllerConnected, {
transform(connectionId, remoteAddress) {
return [connectionId, parseSocketAddress(remoteAddress)];
},
});
controllerDisconnected = new SignalWrapper(this._controllerDisconnected, {
transform(connectionId, remoteAddress) {
return [connectionId, parseSocketAddress(remoteAddress)];
},
});
authenticated = new SignalWrapper(this._authenticated, {
transform(connectionId, sessionInfo) {
return [connectionId, JSON.parse(sessionInfo)];
},
});
subscribe = new SignalWrapper(this._subscribe, {
transform(connectionId) {
return [connectionId];
},
});
message = new SignalWrapper(this._message, {
transform(connectionId, json, data) {
return [connectionId, JSON.parse(json), data];
},
});
}
binding.PortalService = PortalService;
binding.FileMonitor.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "FileMonitor", ["path"], depth, options);
};
binding.Compiler.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "Compiler", [], depth, options);
};
binding.StaticAuthenticationService.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "StaticAuthenticationService", ["tokenHash"], depth, options);
};
binding._Relay.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "Relay", ["address", "username", "password", "kind"], depth, options);
};
class Relay extends binding._Relay {
constructor(properties) {
const { address, username, password, kind } = properties;
super(address, username, password, kind);
}
}
binding.Relay = Relay;
binding._EndpointParameters.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "EndpointParameters", ["address", "port", "certificate", "origin", "authService", "assetRoot"], depth, options);
};
class EndpointParameters extends binding._EndpointParameters {
constructor(params) {
const address = params?.address ?? null;
const port = params?.port ?? 0;
const certificate = params?.certificate ?? null;
const origin = params?.origin ?? null;
let authService = null;
const auth = params?.authentication;
if (auth !== undefined) {
if (auth.scheme === "token") {
authService = new StaticAuthenticationService(auth.token);
}
else {
authService = new CallbackAuthenticationService(auth.callback);
}
}
const assetRoot = params?.assetRoot ?? null;
super(address, port, certificate, origin, authService, assetRoot);
}
}
binding.EndpointParameters = EndpointParameters;
binding.AuthenticationService.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "AuthenticationService", [], depth, options);
};
binding.BaseObject.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "BaseObject", [], depth, options);
};
binding._Cancellable.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "Cancellable", [], depth, options);
};
class Cancellable extends binding._Cancellable {
get isCancelled() {
return this._isCancelled();
}
}
binding.Cancellable = Cancellable;
binding.IOStream.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "IOStream", ["closed", "inputStream", "outputStream"], depth, options);
};
binding.InputStream.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "InputStream", [], depth, options);
};
binding.OutputStream.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "OutputStream", [], depth, options);
};
binding.InetSocketAddress.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "InetSocketAddress", ["address", "flowinfo", "port", "scopeId"], depth, options);
};
binding.InetAddress.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "InetAddress", ["family", "isAny", "isLinkLocal", "isLoopback", "isMcGlobal", "isMcLinkLocal", "isMcNodeLocal", "isMcOrgLocal", "isMcSiteLocal", "isMulticast", "isSiteLocal"], depth, options);
};
binding.UnixSocketAddress.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "UnixSocketAddress", ["addressType", "path"], depth, options);
};
binding.BaseSocketAddress.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "BaseSocketAddress", ["family"], depth, options);
};
binding.SocketAddressEnumerator.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "SocketAddressEnumerator", [], depth, options);
};
binding.SocketConnectable.prototype[inspect.custom] = function (depth, options) {
return inspectWrapper(this, "SocketConnectable", [], depth, options);
};
}
binding.commitConstructors();
export const { DeviceManager, Device, RemoteDeviceOptions, Application, Process, ProcessMatchOptions, RawSpawnOptions, Spawn, Child, Crash, Bus, Service, Session, Script, PortalMembership, PackageManager, Package, PackageSearchOptions, PackageSearchResult, PackageInstallOptions, PackageInstallResult, ControlService, ControlServiceOptions, PortalService, FileMonitor, Compiler, CompilerOptions, BuildOptions, WatchOptions, StaticAuthenticationService, FrontmostQueryOptions, ApplicationQueryOptions, ProcessQueryOptions, SessionOptions, ScriptOptions, SnapshotOptions, PortalOptions, PeerOptions, Relay, EndpointParameters, AbstractAuthenticationService, BaseObject, Cancellable, IOStream, InputStream, OutputStream, InetSocketAddress, InetAddress, UnixSocketAddress, BaseSocketAddress, SocketAddressEnumerator, Runtime, DeviceType, PackageInstallPhase, OutputFormat, BundleFormat, TypeCheckMode, SourceMaps, JsCompression, Realm, SessionDetachReason, Scope, Stdio, ChildOrigin, SnapshotTransport, ScriptRuntime, RelayKind, FileMonitorEvent, SocketFamily, UnixSocketAddressType, } = binding;
const frida = {
DeviceManager,
Device,
RemoteDeviceOptions,
Application,
Process,
ProcessMatchOptions,
RawSpawnOptions,
Spawn,
Child,
Crash,
Bus,
Service,
Session,
Script,
PortalMembership,
PackageManager,
Package,
PackageSearchOptions,
PackageSearchResult,
PackageInstallOptions,
PackageInstallResult,
ControlService,
ControlServiceOptions,
PortalService,
FileMonitor,
Compiler,
CompilerOptions,
BuildOptions,
WatchOptions,
StaticAuthenticationService,
FrontmostQueryOptions,
ApplicationQueryOptions,
ProcessQueryOptions,
SessionOptions,
ScriptOptions,
SnapshotOptions,
PortalOptions,
PeerOptions,
Relay,
EndpointParameters,
AbstractAuthenticationService,
BaseObject,
Cancellable,
IOStream,
InputStream,
OutputStream,
InetSocketAddress,
InetAddress,
UnixSocketAddress,
BaseSocketAddress,
SocketAddressEnumerator,
Runtime,
DeviceType,
PackageInstallPhase,
OutputFormat,
BundleFormat,
TypeCheckMode,
SourceMaps,
JsCompression,
Realm,
SessionDetachReason,
Scope,
Stdio,
ChildOrigin,
SnapshotTransport,
ScriptRuntime,
RelayKind,
FileMonitorEvent,
SocketFamily,
UnixSocketAddressType,
MessageType,
LogLevel,
querySystemParameters,
spawn,
resume,
kill,
attach,
injectLibraryFile,
injectLibraryBlob,
enumerateDevices,
getDeviceManager,
getLocalDevice,
getRemoteDevice,
getUsbDevice,
getDevice,
};
export default frida;
let sharedDeviceManager = null;
export async function querySystemParameters(cancellable) {
const device = await getLocalDevice(cancellable);
return await device.querySystemParameters(cancellable);
}
export async function spawn(program, options, cancellable) {
const device = await getLocalDevice(cancellable);
return await device.spawn(program, options, cancellable);
}
export async function resume(target, cancellable) {
const device = await getLocalDevice(cancellable);
await device.resume(target, cancellable);
}
export async function kill(target, cancellable) {
const device = await getLocalDevice(cancellable);
await device.kill(target, cancellable);
}
export async function attach(target, options, cancellable) {
const device = await getLocalDevice(cancellable);
return await device.attach(target, options, cancellable);
}
export async function injectLibraryFile(target, path, entrypoint, data, cancellable) {
const device = await getLocalDevice(cancellable);
return await device.injectLibraryFile(target, path, entrypoint, data, cancellable);
}
export async function injectLibraryBlob(target, blob, entrypoint, data, cancellable) {
const device = await getLocalDevice(cancellable);
return await device.injectLibraryBlob(target, blob, entrypoint, data, cancellable);
}
export async function enumerateDevices(cancellable) {
const deviceManager = getDeviceManager();
return await deviceManager.enumerateDevices(cancellable);
}
;
export function getDeviceManager() {
if (sharedDeviceManager === null) {
sharedDeviceManager = new DeviceManager();
}
return sharedDeviceManager;
}
export function getLocalDevice(cancellable) {
return getMatchingDevice(device => device.type === DeviceType.Local, {}, cancellable);
}
export function getRemoteDevice(cancellable) {
return getMatchingDevice(device => device.type === DeviceType.Remote, {}, cancellable);
}
export function getUsbDevice(options, cancellable) {
return getMatchingDevice(device => device.type === DeviceType.Usb, options, cancellable);
}
export function getDevice(id, options, cancellable) {
return getMatchingDevice(device => device.id === id, options, cancellable);
}
async function getMatchingDevice(predicate, options = {}, cancellable = null) {
const device = await findMatchingDevice(predicate, cancellable);
if (device !== null) {
return device;
}
const { timeout = 0 } = options;
if (timeout === 0) {
throw new Error("Device not found");
}
const getDeviceEventually = new Promise((resolve, reject) => {
const deviceManager = getDeviceManager();
deviceManager.added.connect(onDeviceAdded);
const timer = (timeout !== null) ? setTimeout(onTimeout, timeout) : null;
if (cancellable !== null) {
cancellable.cancelled.connect(onCancel);
if (cancellable.isCancelled) {
onCancel();
return;
}
}
findMatchingDevice(predicate, cancellable)
.then(device => {
if (device !== null) {
onSuccess(device);
}
})
.catch(onError);
function onDeviceAdded(device) {
if (predicate(device)) {
onSuccess(device);
}
}
function onSuccess(device) {
stopMonitoring();
resolve(device);
}
function onError(error) {
stopMonitoring();
reject(error);
}
function onTimeout() {
onError(new Error("Timed out while waiting for device to appear"));
}
function onCancel() {
onError(new Error("Operation was cancelled"));
}
function stopMonitoring() {
cancellable?.cancelled.disconnect(onCancel);
if (timer !== null) {
clearTimeout(timer);
}
deviceManager.added.disconnect(onDeviceAdded);
}
});
return await getDeviceEventually;
}
async function findMatchingDevice(predicate, cancellable) {
const deviceManager = getDeviceManager();
const devices = await deviceManager.enumerateDevices(cancellable);
return devices.find(predicate) ?? null;
}