Auto-generate Node-API bindings from .gir

Which means API changes in frida-core are instantly reflected, with only
customizations needing tweaking, if applicable.

And, since we now target Node-API there will now only be one prebuilt
binary per OS/ABI.

Co-authored-by: Håvard Sørbø <[email protected]>
This commit is contained in:
Ole André Vadla RavnåsandHåvard Sørbø committed 2025-05-08 10:05:20 +02:00
1 parent 4675cf8471
commit e2ffd0aa6c
109 files changed
+8346 -9964

No files matched your search

+1 -2
View File
@@ -1,7 +1,6 @@
/build/
/deps/
/dist/
/node_modules/
/npm-debug.log
/prebuilds/
/src/frida_bindgen/__pycache__/
/subprojects/*
-98
View File
@@ -1,98 +0,0 @@
import { Icon } from "./icon";
import { SystemParameters } from "./system_parameters";
export interface Application {
identifier: string;
name: string;
pid: number;
parameters: ApplicationParameters;
}
/**
* Application parameters typically obtained through `enumerateApplications({ scope })`,
* where `scope` is either `Scope.Metadata` or `Scope.Full`. The default of `Scope.Minimal`
* means no parameters will be included.
*/
export interface ApplicationParameters {
/**
* Version. Exposes `CFBundleShortVersionString` on Apple OSes, `versionName` on Android.
*/
version?: string;
/**
* Build stamp. Exposes `CFBundleVersion` on Apple OSes, `versionCode` on Android.
*/
build?: string;
/**
* Filesystem path of application. On Apple OSes this is where the bundle is.
*/
path?: string;
/**
* Apple storage container locations.
*/
containers?: {
/**
* Data container location.
*/
data?: string;
/**
* App group container locations.
*/
[name: string]: string | undefined;
}
/**
* Android source APKs. Exposes `publicSourceDir` followed by `splitPublicSourceDirs`.
*/
sources?: string[];
/**
* Android data directory location.
*/
dataDir?: string;
/**
* Android target SDK.
*/
targetSdk?: number;
/**
* Whether the application is debuggable. Applicable on e.g. iOS and Android.
*/
debuggable?: boolean;
/**
* If application is running, whether it is currently frontmost.
*/
frontmost?: boolean;
/**
* If application is running, name of user that it is running as.
*/
user?: string;
/**
* If application is running, PID of its parent process.
*/
ppid?: number;
/**
* If application is running, when it was started.
*/
started?: Date;
/**
* One or more icons. Only included when `scope` is set to `Scope.Full`.
*/
icons?: Icon[];
/**
* System parameters, available for applications accessed through a portal.
*/
system?: SystemParameters;
[name: string]: any;
}
-3
View File
@@ -1,3 +0,0 @@
export interface AuthenticatedSessionInfo {
[key: string]: any;
}
-30
View File
@@ -1,30 +0,0 @@
import { Cancellable } from "./cancellable";
import { Signals, Signal } from "./signals";
import { inspect } from "util";
export class Bus {
detached: Signal<BusDetachedHandler>;
message: Signal<BusMessageHandler>;
constructor(private impl: any) {
const signals: Signals = impl.signals;
this.detached = new Signal<BusDetachedHandler>(signals, "detached");
this.message = new Signal<BusMessageHandler>(signals, "message");
}
attach(cancellable?: Cancellable): Promise<void> {
return this.impl.attach(cancellable);
}
post(message: any, data: Buffer | null = null): void {
this.impl.post(message, data);
}
[inspect.custom](depth, options) {
return "Bus {}";
}
}
export type BusDetachedHandler = () => void;
export type BusMessageHandler = (message: any, data: Buffer | null) => void;
-48
View File
@@ -1,48 +0,0 @@
import { binding } from "./native";
import { Signal } from "./signals";
import { inspect } from "util";
export class Cancellable {
private impl = new binding.Cancellable();
cancelled: Signal<CancelledHandler>;
constructor() {
const { signals } = this.impl;
this.cancelled = new Signal<CancelledHandler>(signals, "cancelled");
}
get isCancelled(): boolean {
return this.impl.isCancelled;
}
throwIfCancelled(): void {
this.impl.throwIfCancelled();
}
cancel(): void {
return this.impl.cancel();
}
[inspect.custom]() {
return "Cancellable {}";
}
combine(other: Cancellable): Cancellable {
const cancel = new Cancellable();
this.cancelled.connect(() => cancel.cancel());
other.cancelled.connect(() => cancel.cancel());
if (this.isCancelled) cancel.cancel();
if (other.isCancelled) cancel.cancel();
return cancel;
}
public static withTimeout(ms): Cancellable {
const cancel = new Cancellable();
setTimeout(() => cancel.cancel(), ms).unref();
return cancel;
}
}
export type CancelledHandler = () => void;
-15
View File
@@ -1,15 +0,0 @@
export interface Child {
pid: number;
parentPid: number;
origin: ChildOrigin;
identifier: string | null;
path: string | null;
argv: string[] | null;
envp: { [name: string]: string } | null;
}
export enum ChildOrigin {
Fork = "fork",
Exec = "exec",
Spawn = "spawn"
}
-11
View File
@@ -1,11 +0,0 @@
export interface Crash {
pid: number;
processName: string;
summary: string;
report: string;
parameters: CrashParameters;
}
export interface CrashParameters {
[name: string]: string | number | boolean;
}
-331
View File
@@ -1,331 +0,0 @@
import { Application } from "./application";
import { Bus } from "./bus";
import { Cancellable } from "./cancellable";
import { Child } from "./child";
import { Crash } from "./crash";
import { Icon } from "./icon";
import { IOStream } from "./iostream";
import { Process } from "./process";
import { Service } from "./service";
import { Session } from "./session";
import { Signal } from "./signals";
import { Spawn } from "./spawn";
import { SystemParameters } from "./system_parameters";
import { Minimatch } from "minimatch";
import { inspect } from "util";
export class Device {
spawnAdded: Signal<SpawnAddedHandler>;
spawnRemoved: Signal<SpawnRemovedHandler>;
childAdded: Signal<ChildAddedHandler>;
childRemoved: Signal<ChildRemovedHandler>;
processCrashed: Signal<ProcessCrashedHandler>;
output: Signal<OutputHandler>;
uninjected: Signal<UninjectedHandler>;
lost: Signal<DeviceLostHandler>;
bus: Bus;
constructor(private impl: any) {
this.bus = new Bus(impl.bus);
const { signals } = impl;
this.spawnAdded = new Signal<SpawnAddedHandler>(signals, "spawn-added");
this.spawnRemoved = new Signal<SpawnRemovedHandler>(signals, "spawn-removed");
this.childAdded = new Signal<ChildAddedHandler>(signals, "child-added");
this.childRemoved = new Signal<ChildRemovedHandler>(signals, "child-removed");
this.processCrashed = new Signal<ProcessCrashedHandler>(signals, "process-crashed");
this.output = new Signal<OutputHandler>(signals, "output");
this.uninjected = new Signal<UninjectedHandler>(signals, "uninjected");
this.lost = new Signal<DeviceLostHandler>(signals, "lost");
}
get id(): string {
return this.impl.id;
}
get name(): string {
return this.impl.name;
}
get icon(): Icon | null {
return this.impl.icon;
}
get type(): DeviceType {
return this.impl.type;
}
get isLost(): boolean {
return this.impl.isLost;
}
querySystemParameters(cancellable?: Cancellable): Promise<SystemParameters> {
return this.impl.querySystemParameters(cancellable);
}
getFrontmostApplication(options: FrontmostQueryOptions = {}, cancellable?: Cancellable): Promise<Application | null> {
const {
scope = null,
} = options;
return this.impl.getFrontmostApplication(scope, cancellable);
}
enumerateApplications(options: ApplicationQueryOptions = {}, cancellable?: Cancellable): Promise<Application[]> {
const {
identifiers = [],
scope = null,
} = options;
return this.impl.enumerateApplications(identifiers, scope, cancellable);
}
enumerateProcesses(options: ProcessQueryOptions = {}, cancellable?: Cancellable): Promise<Process[]> {
const {
pids = [],
scope = null,
} = options;
return this.impl.enumerateProcesses(pids, scope, cancellable);
}
async getProcess(name: string, options: ProcessMatchOptions = {}, cancellable?: Cancellable): Promise<Process> {
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");
}
}
enableSpawnGating(cancellable?: Cancellable): Promise<void> {
return this.impl.enableSpawnGating(cancellable);
}
disableSpawnGating(cancellable?: Cancellable): Promise<void> {
return this.impl.disableSpawnGating(cancellable);
}
enumeratePendingSpawn(cancellable?: Cancellable): Promise<Spawn[]> {
return this.impl.enumeratePendingSpawn(cancellable);
}
enumeratePendingChildren(cancellable?: Cancellable): Promise<Child[]> {
return this.impl.enumeratePendingChildren(cancellable);
}
spawn(program: string | string[], options: SpawnOptions = {}, cancellable?: Cancellable): Promise<ProcessID> {
const pendingOptions = Object.assign({}, options);
let argv = consumeOption("argv");
if (typeof program !== "string") {
argv = program;
program = argv[0];
if (argv.length === 1) {
argv = null;
}
}
const envp = consumeOption("envp");
const env = consumeOption("env");
const cwd = consumeOption("cwd");
const stdio = consumeOption("stdio");
const aux = pendingOptions;
return this.impl.spawn(program, argv, envp, env, cwd, stdio, aux, cancellable);
function consumeOption(name) {
const value = pendingOptions[name];
delete pendingOptions[name];
return value ?? null;
}
}
async input(target: TargetProcess, data: Buffer, cancellable?: Cancellable): Promise<void> {
const pid = await this.getPid(target, cancellable);
return this.impl.input(pid, data, cancellable);
}
async resume(target: TargetProcess, cancellable?: Cancellable): Promise<void> {
const pid = await this.getPid(target, cancellable);
return this.impl.resume(pid, cancellable);
}
async kill(target: TargetProcess, cancellable?: Cancellable): Promise<void> {
const pid = await this.getPid(target, cancellable);
return this.impl.kill(pid, cancellable);
}
async attach(target: TargetProcess, options: SessionOptions = {}, cancellable?: Cancellable): Promise<Session> {
const {
realm = null,
persistTimeout = null,
} = options;
const pid = await this.getPid(target, cancellable);
return new Session(await this.impl.attach(pid, realm, persistTimeout, cancellable));
}
async injectLibraryFile(target: TargetProcess, path: string, entrypoint: string, data: string,
cancellable?: Cancellable): Promise<InjecteeID> {
const pid = await this.getPid(target, cancellable);
return this.impl.injectLibraryFile(pid, path, entrypoint, data, cancellable);
}
async injectLibraryBlob(target: TargetProcess, blob: Buffer, entrypoint: string, data: string,
cancellable?: Cancellable): Promise<InjecteeID> {
const pid = await this.getPid(target, cancellable);
return this.impl.injectLibraryBlob(pid, blob, entrypoint, data, cancellable);
}
async openChannel(address: string, cancellable?: Cancellable): Promise<IOStream> {
return new IOStream(await this.impl.openChannel(address, cancellable));
}
async openService(address: string, cancellable?: Cancellable): Promise<Service> {
return new Service(await this.impl.openService(address, cancellable));
}
async unpair(cancellable?: Cancellable): Promise<void> {
await this.impl.unpair(cancellable);
}
private async getPid(target: TargetProcess, cancellable?: Cancellable): Promise<ProcessID> {
if (typeof target === "number") {
return target;
}
const process = await this.getProcess(target, {}, cancellable);
return process.pid;
}
[inspect.custom](depth, options) {
return "Device " + inspect({
id: this.id,
name: this.name,
icon: this.icon,
type: this.type
}, options);
}
}
export type ProcessID = number;
export type ProcessName = string;
export type InjecteeID = number;
export type FileDescriptor = number;
export type SpawnAddedHandler = (spawn: Spawn) => void;
export type SpawnRemovedHandler = (spawn: Spawn) => void;
export type ChildAddedHandler = (child: Child) => void;
export type ChildRemovedHandler = (child: Child) => void;
export type ProcessCrashedHandler = (crash: Crash) => void;
export type OutputHandler = (pid: ProcessID, fd: FileDescriptor, data: Buffer) => void;
export type UninjectedHandler = (id: InjecteeID) => void;
export type DeviceLostHandler = () => void;
export enum DeviceType {
Local = "local",
Remote = "remote",
Usb = "usb"
}
export interface FrontmostQueryOptions {
/**
* How much data to collect about the frontmost application. The default is `Scope.Minimal`,
* which means no parameters will be collected. Specify `Scope.Metadata` to collect all
* parameters except icons, which can be included by specifying `Scope.Full`.
*/
scope?: Scope;
}
export interface ApplicationQueryOptions {
/**
* Limit enumeration to one or more application IDs only. Typically used to fetch additional
* details about a subset, e.g. based on user interaction.
*/
identifiers?: string[];
/**
* How much data to collect about each application. The default is `Scope.Minimal`, which
* means no parameters will be collected. Specify `Scope.Metadata` to collect all parameters
* except icons, which can be included by specifying `Scope.Full`.
*/
scope?: Scope;
}
export interface ProcessQueryOptions {
/**
* Limit enumeration to one or more process IDs only. Typically used to fetch additional
* details about a subset, e.g. based on user interaction.
*/
pids?: number[];
/**
* How much data to collect about each process. The default is `Scope.Minimal`, which
* means no parameters will be collected. Specify `Scope.Metadata` to collect all
* parameters except icons, which can be included by specifying `Scope.Full`.
*/
scope?: Scope;
}
export interface ProcessMatchOptions {
/**
* How much data to collect about the matching process. The default is `Scope.Minimal`,
* which means no parameters will be collected. Specify `Scope.Metadata` to collect all
* parameters except icons, which can be included by specifying `Scope.Full`.
*/
scope?: Scope;
}
/**
* How much data to collect about a given application or process.
*/
export enum Scope {
/**
* Don't collect any parameters. This is the default.
*/
Minimal = "minimal",
/**
* Collect all parameters except icons.
*/
Metadata = "metadata",
/**
* Collect all parameters, including icons.
*/
Full = "full"
}
export interface SpawnOptions {
argv?: string[];
envp?: { [name: string]: string };
env?: { [name: string]: string };
cwd?: string;
stdio?: Stdio;
[name: string]: any;
}
export enum Stdio {
Inherit = "inherit",
Pipe = "pipe"
}
export type TargetProcess = ProcessID | ProcessName;
export interface SessionOptions {
realm?: Realm;
persistTimeout?: number;
}
export enum Realm {
Native = "native",
Emulated = "emulated",
}
-69
View File
@@ -1,69 +0,0 @@
import { Cancellable } from "./cancellable";
import { Device } from "./device";
import { binding } from "./native";
import { Signals, Signal, SignalHandler, SignalAdapter } from "./signals";
import { inspect } from "util";
export class DeviceManager {
private impl = new binding.DeviceManager();
added: Signal<DeviceAddedHandler>;
removed: Signal<DeviceRemovedHandler>;
changed: Signal<DevicesChangedHandler>;
constructor() {
const signals = new DeviceManagerSignals(this.impl.signals);
this.added = new Signal<DeviceAddedHandler>(signals, "added");
this.removed = new Signal<DeviceRemovedHandler>(signals, "removed");
this.changed = new Signal<DevicesChangedHandler>(signals, "changed");
}
async enumerateDevices(cancellable?: Cancellable): Promise<Device[]> {
const devices: any[] = await this.impl.enumerateDevices(cancellable);
return devices.map(impl => new Device(impl));
}
async addRemoteDevice(address: string, options: RemoteDeviceOptions = {}, cancellable?: Cancellable): Promise<Device> {
const {
certificate = null,
origin = null,
token = null,
keepaliveInterval = null,
} = options;
return new Device(await this.impl.addRemoteDevice(address, certificate, origin, token, keepaliveInterval, cancellable));
}
removeRemoteDevice(address: string, cancellable?: Cancellable): Promise<void> {
return this.impl.removeRemoteDevice(address, cancellable);
}
[inspect.custom]() {
return "DeviceManager {}";
}
}
export interface RemoteDeviceOptions {
certificate?: string;
origin?: string;
token?: string;
keepaliveInterval?: number;
}
export type DeviceAddedHandler = (device: Device) => void;
export type DeviceRemovedHandler = (device: Device) => void;
export type DevicesChangedHandler = () => void;
class DeviceManagerSignals extends SignalAdapter {
constructor(signals: Signals) {
super(signals);
}
protected getProxy(name: string, userHandler: SignalHandler): SignalHandler | null {
if (name === "added" || name === "removed") {
return impl => userHandler(new Device(impl));
}
return null;
}
}
-56
View File
@@ -1,56 +0,0 @@
import { AuthenticatedSessionInfo } from "./authentication";
import { binding } from "./native";
import { inspect } from "util";
export class EndpointParameters {
private impl: any;
constructor({
address = null,
port = null,
certificate = null,
origin = null,
authentication = null,
assetRoot = null,
}: EndpointParametersSubset = {}) {
let authToken: string | null = null;
let authCallback: AuthenticationCallback | null = null;
if (authentication !== null) {
if (authentication.scheme === "token") {
authToken = authentication.token;
} else {
authCallback = authentication.callback;
}
}
this.impl = new binding.EndpointParameters(address, port, certificate, origin, authToken, authCallback, assetRoot);
}
[inspect.custom]() {
return "EndpointParameters {}";
}
}
export interface EndpointParametersSubset {
address?: string;
port?: number;
certificate?: string;
origin?: string;
authentication?: AuthenticationScheme;
assetRoot?: string;
}
export type AuthenticationScheme = TokenAuthenticationScheme | CallbackAuthenticationScheme;
export interface TokenAuthenticationScheme {
scheme: "token";
token: string;
}
export interface CallbackAuthenticationScheme {
scheme: "callback";
callback: AuthenticationCallback;
}
export type AuthenticationCallback = (token: string) => Promise<AuthenticatedSessionInfo>;
-15
View File
@@ -1,15 +0,0 @@
export type Icon = RGBAIcon | PNGIcon;
export interface RGBAIcon {
format: "rgba";
width: number;
height: number;
image: Buffer;
}
export interface PNGIcon {
format: "png";
width?: number;
height?: number;
image: Buffer;
}
-316
View File
@@ -1,316 +0,0 @@
import * as applicationModule from "./application";
import * as authenticationModule from "./authentication";
import * as busModule from "./bus";
import * as cancellableModule from "./cancellable";
import * as childModule from "./child";
import * as crashModule from "./crash";
import * as deviceManagerModule from "./device_manager";
import * as deviceModule from "./device";
import * as endpointParametersModule from "./endpoint_parameters";
import * as iconModule from "./icon";
import * as iostreamModule from "./iostream";
import * as portalMembershipModule from "./portal_membership";
import * as portalServiceModule from "./portal_service";
import * as processModule from "./process";
import * as relayModule from "./relay";
import * as scriptModule from "./script";
import * as serviceModule from "./service";
import * as sessionModule from "./session";
import * as socketAddressModule from "./socket_address";
import * as spawnModule from "./spawn";
import * as systemParametersModule from "./system_parameters";
export type DeviceManager = deviceManagerModule.DeviceManager;
export const DeviceManager = deviceManagerModule.DeviceManager;
export type RemoteDeviceOptions = deviceManagerModule.RemoteDeviceOptions;
export type DeviceAddedHandler = deviceManagerModule.DeviceAddedHandler;
export type DeviceRemovedHandler = deviceManagerModule.DeviceRemovedHandler;
export type DevicesChangedHandler = deviceManagerModule.DevicesChangedHandler;
export type Device = deviceModule.Device;
export const Device = deviceModule.Device;
export type ProcessID = deviceModule.ProcessID;
export type ProcessName = deviceModule.ProcessName;
export type InjecteeID = deviceModule.InjecteeID;
export type FileDescriptor = deviceModule.FileDescriptor;
export type SpawnAddedHandler = deviceModule.SpawnAddedHandler;
export type SpawnRemovedHandler = deviceModule.SpawnRemovedHandler;
export type ChildAddedHandler = deviceModule.ChildAddedHandler;
export type ChildRemovedHandler = deviceModule.ChildRemovedHandler;
export type OutputHandler = deviceModule.OutputHandler;
export type UninjectedHandler = deviceModule.UninjectedHandler;
export type DeviceLostHandler = deviceModule.DeviceLostHandler;
export type DeviceType = deviceModule.DeviceType;
export const DeviceType = deviceModule.DeviceType;
export type FrontmostQueryOptions = deviceModule.FrontmostQueryOptions;
export type ApplicationQueryOptions = deviceModule.ApplicationQueryOptions;
export type ProcessQueryOptions = deviceModule.ProcessQueryOptions;
export type ProcessMatchOptions = deviceModule.ProcessMatchOptions;
export type Scope = deviceModule.Scope;
export const Scope = deviceModule.Scope;
export type SpawnOptions = deviceModule.SpawnOptions;
export type Stdio = deviceModule.Stdio;
export const Stdio = deviceModule.Stdio;
export type TargetProcess = deviceModule.TargetProcess;
export type SessionOptions = deviceModule.SessionOptions;
export type Realm = deviceModule.Realm;
export const Realm = deviceModule.Realm;
export type SystemParameters = systemParametersModule.SystemParameters;
export type SystemInterface = systemParametersModule.SystemInterface;
export type NetworkInterface = systemParametersModule.NetworkInterface;
export type CellularInterface = systemParametersModule.CellularInterface;
export type Session = sessionModule.Session;
export const Session = sessionModule.Session;
export type SessionDetachedHandler = sessionModule.SessionDetachedHandler;
export type SessionDetachReason = sessionModule.SessionDetachReason;
export const SessionDetachReason = sessionModule.SessionDetachReason;
export type PeerOptions = sessionModule.PeerOptions;
export type PortalOptions = sessionModule.PortalOptions;
export type Script = scriptModule.Script;
export const Script = scriptModule.Script;
export type ScriptOptions = scriptModule.ScriptOptions;
export type SnapshotOptions = scriptModule.SnapshotOptions;
export type ScriptRuntime = scriptModule.ScriptRuntime;
export const ScriptRuntime = scriptModule.ScriptRuntime;
export type ScriptDestroyedHandler = scriptModule.ScriptDestroyedHandler;
export type ScriptMessageHandler = scriptModule.ScriptMessageHandler;
export type ScriptLogHandler = scriptModule.ScriptLogHandler;
export type Message = scriptModule.Message;
export type MessageType = scriptModule.MessageType;
export const MessageType = scriptModule.MessageType;
export type SendMessage = scriptModule.SendMessage;
export type ErrorMessage = scriptModule.ErrorMessage;
export type ScriptExports = scriptModule.ScriptExports;
export type LogLevel = scriptModule.LogLevel;
export const LogLevel = scriptModule.LogLevel;
export type EnableDebuggerOptions = scriptModule.EnableDebuggerOptions;
export type Relay = relayModule.Relay;
export const Relay = relayModule.Relay;
export type RelayProperties = relayModule.RelayProperties;
export type RelayKind = relayModule.RelayKind;
export const RelayKind = relayModule.RelayKind;
export type PortalMembership = portalMembershipModule.PortalMembership;
export const PortalMembership = portalMembershipModule.PortalMembership;
export type PortalService = portalServiceModule.PortalService;
export const PortalService = portalServiceModule.PortalService;
export type PortalServiceOptions = portalServiceModule.PortalServiceOptions;
export type PortalConnectionId = portalServiceModule.PortalConnectionId;
export type PortalConnectionTag = portalServiceModule.PortalConnectionTag;
export type PortalNodeConnectedHandler = portalServiceModule.PortalNodeConnectedHandler;
export type PortalNodeJoinedHandler = portalServiceModule.PortalNodeJoinedHandler;
export type PortalNodeLeftHandler = portalServiceModule.PortalNodeLeftHandler;
export type PortalNodeDisconnectedHandler = portalServiceModule.PortalNodeDisconnectedHandler;
export type PortalControllerConnectedHandler = portalServiceModule.PortalControllerConnectedHandler;
export type PortalControllerDisconnectedHandler = portalServiceModule.PortalControllerDisconnectedHandler;
export type PortalAuthenticatedHandler = portalServiceModule.PortalAuthenticatedHandler;
export type PortalSubscribeHandler = portalServiceModule.PortalSubscribeHandler;
export type PortalMessageHandler = portalServiceModule.PortalMessageHandler;
export type EndpointParameters = endpointParametersModule.EndpointParameters;
export const EndpointParameters = endpointParametersModule.EndpointParameters;
export type EndpointParametersSubset = endpointParametersModule.EndpointParametersSubset;
export type AuthenticationScheme = endpointParametersModule.AuthenticationScheme;
export type TokenAuthenticationScheme = endpointParametersModule.TokenAuthenticationScheme;
export type CallbackAuthenticationScheme = endpointParametersModule.CallbackAuthenticationScheme;
export type AuthenticationCallback = endpointParametersModule.AuthenticationCallback;
export type AuthenticatedSessionInfo = authenticationModule.AuthenticatedSessionInfo;
export type IOStream = iostreamModule.IOStream;
export const IOStream = iostreamModule.IOStream;
export type Cancellable = cancellableModule.Cancellable;
export const Cancellable = cancellableModule.Cancellable;
export type Application = applicationModule.Application;
export type ApplicationParameters = applicationModule.ApplicationParameters;
export type Process = processModule.Process;
export type ProcessParameters = processModule.ProcessParameters;
export type Spawn = spawnModule.Spawn;
export type Child = childModule.Child;
export type ChildOrigin = childModule.ChildOrigin;
export const ChildOrigin = childModule.ChildOrigin;
export type Crash = crashModule.Crash;
export type CrashParameters = crashModule.CrashParameters;
export type Icon = iconModule.Icon;
export type RGBAIcon = iconModule.RGBAIcon;
export type PNGIcon = iconModule.PNGIcon;
export type Bus = busModule.Bus;
export const Bus = busModule.Bus;
export type BusDetachedHandler = busModule.BusDetachedHandler;
export type BusMessageHandler = busModule.BusMessageHandler;
export type Service = serviceModule.Service;
export const Service = serviceModule.Service;
export type ServiceCloseHandler = serviceModule.ServiceCloseHandler;
export type ServiceMessageHandler = serviceModule.ServiceMessageHandler;
export type SocketAddress = socketAddressModule.SocketAddress;
export type IPV4SocketAddress = socketAddressModule.IPV4SocketAddress;
export type IPV6SocketAddress = socketAddressModule.IPV6SocketAddress;
export type AnonymousUnixSocketAddress = socketAddressModule.AnonymousUnixSocketAddress;
export type PathUnixSocketAddress = socketAddressModule.PathUnixSocketAddress;
export type AbstractUnixSocketAddress = socketAddressModule.AbstractUnixSocketAddress;
let sharedDeviceManager: DeviceManager = null;
export async function querySystemParameters(cancellable?: Cancellable): Promise<SystemParameters> {
const device = await getLocalDevice(cancellable);
return await device.querySystemParameters(cancellable);
}
export async function spawn(program: string | string[], options: SpawnOptions = {}, cancellable?: Cancellable): Promise<number> {
const device = await getLocalDevice(cancellable);
return await device.spawn(program, options, cancellable);
}
export async function resume(target: number | string, cancellable?: Cancellable): Promise<void> {
const device = await getLocalDevice(cancellable);
await device.resume(target, cancellable);
}
export async function kill(target: number | string, cancellable?: Cancellable): Promise<void> {
const device = await getLocalDevice(cancellable);
await device.kill(target, cancellable);
}
export async function attach(target: TargetProcess, options: SessionOptions = {}, cancellable?: Cancellable): Promise<Session> {
const device = await getLocalDevice(cancellable);
return await device.attach(target, options, cancellable);
}
export async function injectLibraryFile(target: number | string, path: string, entrypoint: string, data: string,
cancellable?: Cancellable): Promise<number> {
const device = await getLocalDevice(cancellable);
return await device.injectLibraryFile(target, path, entrypoint, data, cancellable);
}
export async function injectLibraryBlob(target: number | string, blob: Buffer, entrypoint: string, data: string,
cancellable?: Cancellable): Promise<number> {
const device = await getLocalDevice(cancellable);
return await device.injectLibraryBlob(target, blob, entrypoint, data, cancellable);
}
export async function enumerateDevices(cancellable?: Cancellable): Promise<Device[]> {
const deviceManager = getDeviceManager();
return await deviceManager.enumerateDevices(cancellable);
};
export function getDeviceManager(): DeviceManager {
if (sharedDeviceManager === null) {
sharedDeviceManager = new deviceManagerModule.DeviceManager();
}
return sharedDeviceManager;
}
export function getLocalDevice(cancellable?: Cancellable): Promise<Device> {
return getMatchingDevice(device => device.type === DeviceType.Local, {}, cancellable);
}
export function getRemoteDevice(cancellable?: Cancellable): Promise<Device> {
return getMatchingDevice(device => device.type === DeviceType.Remote, {}, cancellable);
}
export function getUsbDevice(options?: GetDeviceOptions, cancellable?: Cancellable): Promise<Device> {
return getMatchingDevice(device => device.type === DeviceType.Usb, options, cancellable);
}
export function getDevice(id: string, options?: GetDeviceOptions, cancellable?: Cancellable): Promise<Device> {
return getMatchingDevice(device => device.id === id, options, cancellable);
}
export interface GetDeviceOptions {
timeout?: number | null;
}
async function getMatchingDevice(predicate: DevicePredicate, options: GetDeviceOptions = {}, cancellable?: Cancellable): Promise<Device> {
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: (device: Device) => void, reject: (error: Error) => void) => {
const deviceManager = getDeviceManager();
deviceManager.added.connect(onDeviceAdded);
const timer = (timeout !== null) ? setTimeout(onTimeout, timeout) : null;
if (cancellable !== undefined) {
cancellable.cancelled.connect(onCancel);
if (cancellable.isCancelled) {
onCancel();
return;
}
}
findMatchingDevice(predicate, cancellable)
.then(device => {
if (device !== null) {
onSuccess(device);
}
})
.catch(onError);
function onDeviceAdded(device: Device): void {
if (predicate(device)) {
onSuccess(device);
}
}
function onSuccess(device: Device): void {
stopMonitoring();
resolve(device);
}
function onError(error: Error): void {
stopMonitoring();
reject(error);
}
function onTimeout(): void {
onError(new Error("Timed out while waiting for device to appear"));
}
function onCancel(): void {
onError(new Error("Operation was cancelled"));
}
function stopMonitoring(): void {
if (cancellable !== undefined) {
cancellable.cancelled.disconnect(onCancel);
}
if (timer !== null) {
clearTimeout(timer);
}
deviceManager.added.disconnect(onDeviceAdded);
}
});
return await getDeviceEventually;
}
async function findMatchingDevice(predicate: DevicePredicate, cancellable?: Cancellable): Promise<Device | null> {
const deviceManager = getDeviceManager();
const devices = await deviceManager.enumerateDevices(cancellable);
const matching = devices.filter(predicate);
if (matching.length === 0) {
return null;
}
return matching[0];
}
type DevicePredicate = (device: Device) => boolean;
-78
View File
@@ -1,78 +0,0 @@
import { Cancellable } from "./cancellable";
import { Duplex } from "stream";
export class IOStream extends Duplex {
private pending = new Set<Promise<void>>();
private cancellable = new Cancellable();
constructor(private impl: any) {
super({});
}
async _destroy(error: Error | null, callback: (error: Error | null) => void): Promise<void> {
this.cancellable.cancel();
for (const operation of this.pending) {
try {
await operation;
} catch (e) {
}
}
try {
await this.impl.close();
} catch (e) {
}
callback(error);
}
_read(size: number): void {
const operation = this.impl.read(size, this.cancellable)
.then((data: Buffer): void => {
const isEof = data.length === 0;
if (isEof) {
this.push(null);
return;
}
this.push(data);
})
.catch((error: Error): void => {
if (this.impl.isClosed) {
this.push(null);
}
this.emit("error", error);
});
this.track(operation);
}
_write(chunk: any, encoding: BufferEncoding, callback: (error?: Error | null) => void): void {
let data: Buffer;
if (Buffer.isBuffer(chunk)) {
data = chunk;
} else {
data = Buffer.from(chunk, encoding);
}
const operation = this.impl.write(data, this.cancellable)
.then((): void => {
callback(null);
})
.catch((error: Error): void => {
callback(error);
});
this.track(operation);
}
private track(operation: Promise<void>): void {
this.pending.add(operation);
operation
.catch(_ => {})
.finally(() => {
this.pending.delete(operation);
});
}
}
-53
View File
@@ -1,53 +0,0 @@
sources = [
'index.ts',
'application.ts',
'authentication.ts',
'bus.ts',
'cancellable.ts',
'child.ts',
'crash.ts',
'device.ts',
'device_manager.ts',
'endpoint_parameters.ts',
'icon.ts',
'iostream.ts',
'native.ts',
'portal_membership.ts',
'portal_service.ts',
'process.ts',
'relay.ts',
'script.ts',
'service.ts',
'session.ts',
'signals.ts',
'socket_address.ts',
'spawn.ts',
'system_parameters.ts',
]
outputs = []
foreach s : sources
stem = s.split('.')[0]
outputs += [
stem + '.js',
stem + '.d.ts',
]
endforeach
custom_target('js',
input: sources,
output: outputs,
command: [
python,
files('build.py'),
'@OUTDIR@',
'@PRIVATE_DIR@',
npm,
package_json,
tsconfig_json,
'@INPUT@',
],
install: true,
install_dir: pkg_install_dir / 'dist',
)
-9
View File
@@ -1,9 +0,0 @@
import * as bindings from "bindings";
export const binding = bindings({
bindings: "frida_binding",
try: [
["module_root", "build", "bindings"],
[process.cwd(), "bindings"],
]
});
-10
View File
@@ -1,10 +0,0 @@
import { Cancellable } from "./cancellable";
export class PortalMembership {
constructor(private impl: any) {
}
terminate(cancellable?: Cancellable): Promise<void> {
return this.impl.terminate(cancellable);
}
}
-105
View File
@@ -1,105 +0,0 @@
import { Application } from "./application";
import { AuthenticatedSessionInfo } from "./authentication";
import { Cancellable } from "./cancellable";
import { Device } from "./device";
import { EndpointParameters } from "./endpoint_parameters";
import { binding } from "./native";
import { Signal } from "./signals";
import { SocketAddress } from "./socket_address";
import { inspect } from "util";
export class PortalService {
nodeConnected: Signal<PortalNodeConnectedHandler>;
nodeJoined: Signal<PortalNodeJoinedHandler>;
nodeLeft: Signal<PortalNodeLeftHandler>;
nodeDisconnected: Signal<PortalNodeDisconnectedHandler>;
controllerConnected: Signal<PortalControllerConnectedHandler>;
controllerDisconnected: Signal<PortalControllerDisconnectedHandler>;
authenticated: Signal<PortalAuthenticatedHandler>;
subscribe: Signal<PortalSubscribeHandler>;
message: Signal<PortalMessageHandler>;
device: Device;
private impl: any;
constructor({ clusterParams = null, controlParams = null }: PortalServiceOptions = {}) {
const impl = new binding.PortalService(clusterParams, controlParams);
this.impl = impl;
this.device = new Device(impl.device);
const { signals } = impl;
this.nodeConnected = new Signal<PortalNodeConnectedHandler>(signals, "node-connected");
this.nodeJoined = new Signal<PortalNodeJoinedHandler>(signals, "node-joined");
this.nodeLeft = new Signal<PortalNodeLeftHandler>(signals, "node-left");
this.nodeDisconnected = new Signal<PortalNodeDisconnectedHandler>(signals, "node-disconnected");
this.controllerConnected = new Signal<PortalControllerConnectedHandler>(signals, "controller-connected");
this.controllerDisconnected = new Signal<PortalControllerDisconnectedHandler>(signals, "controller-disconnected");
this.authenticated = new Signal<PortalAuthenticatedHandler>(signals, "authenticated");
this.subscribe = new Signal<PortalSubscribeHandler>(signals, "subscribe");
this.message = new Signal<PortalMessageHandler>(signals, "message");
}
start(cancellable?: Cancellable): Promise<void> {
return this.impl.start(cancellable);
}
stop(cancellable?: Cancellable): Promise<void> {
return this.impl.stop(cancellable);
}
post(connectionId: PortalConnectionId, message: any, data: Buffer | null = null): void {
this.impl.post(connectionId, message, data);
}
narrowcast(tag: PortalConnectionTag, message: any, data: Buffer | null = null): void {
this.impl.narrowcast(tag, message, data);
}
broadcast(message: any, data: Buffer | null = null): void {
this.impl.broadcast(message, data);
}
enumerateTags(connectionId: PortalConnectionId): Promise<PortalConnectionTag[]> {
return this.impl.enumerateTags(connectionId);
}
tag(connectionId: PortalConnectionId, tag: PortalConnectionTag): void {
this.impl.tag(connectionId, tag);
}
untag(connectionId: PortalConnectionId, tag: PortalConnectionTag): void {
this.impl.untag(connectionId, tag);
}
[inspect.custom]() {
return "PortalService {}";
}
}
export interface PortalServiceOptions {
clusterParams?: EndpointParameters;
controlParams?: EndpointParameters;
}
export type PortalConnectionId = number;
export type PortalConnectionTag = string;
export type PortalNodeConnectedHandler = (connectionId: PortalConnectionId, remoteAddress: SocketAddress) => void;
export type PortalNodeJoinedHandler = (connectionId: PortalConnectionId, application: Application) => void;
export type PortalNodeLeftHandler = (connectionId: PortalConnectionId, application: Application) => void;
export type PortalNodeDisconnectedHandler = (connectionId: PortalConnectionId, remoteAddress: SocketAddress) => void;
export type PortalControllerConnectedHandler = (connectionId: PortalConnectionId, remoteAddress: SocketAddress) => void;
export type PortalControllerDisconnectedHandler = (connectionId: PortalConnectionId, remoteAddress: SocketAddress) => void;
export type PortalAuthenticatedHandler = (connectionId: PortalConnectionId, sessionInfo: AuthenticatedSessionInfo) => void;
export type PortalSubscribeHandler = (connectionId: PortalConnectionId) => void;
export type PortalMessageHandler = (connectionId: PortalConnectionId, message: any, data: Buffer | null) => void;
-57
View File
@@ -1,57 +0,0 @@
import { Icon } from "./icon";
import { SystemParameters } from "./system_parameters";
export interface Process {
pid: number;
name: string;
parameters: ProcessParameters;
}
/**
* Process parameters typically obtained through `enumerateProcesses({ scope })`, where
* `scope` is either `Scope.Metadata` or `Scope.Full`. The default of `Scope.Minimal`
* means no parameters will be included.
*/
export interface ProcessParameters {
/**
* Filesystem path of program.
*/
path?: string;
/**
* Name of user that the process is running as.
*/
user?: string;
/**
* Parent process ID.
*/
ppid?: number;
/**
* When the process was started.
*/
started?: Date;
/**
* Application IDs that have code running inside this process.
*/
applications?: string[];
/**
* Whether the process is currently frontmost.
*/
frontmost?: boolean;
/**
* One or more icons. Only included when `scope` is set to `Scope.Full`.
*/
icons?: Icon[];
/**
* System parameters, available for processes accessed through a portal.
*/
system?: SystemParameters;
[name: string]: any;
}
-44
View File
@@ -1,44 +0,0 @@
import { binding } from "./native";
import { inspect } from "util";
export class Relay {
private impl: RelayProperties;
constructor({ address, username, password, kind }: RelayProperties) {
this.impl = new binding.Relay(address, username, password, kind);
}
get address(): string {
return this.impl.address;
}
get username(): string {
return this.impl.username;
}
get password(): string {
return this.impl.password;
}
get kind(): RelayKind {
return this.impl.kind;
}
[inspect.custom](depth, options) {
return inspect(this.impl, { ...options, depth });
}
}
export interface RelayProperties {
address: string;
username: string;
password: string;
kind: RelayKind;
}
export enum RelayKind {
TurnUDP = "turn-udp",
TurnTCP = "turn-tcp",
TurnTLS = "turn-tls"
}
-361
View File
@@ -1,361 +0,0 @@
import { Cancellable } from "./cancellable";
import { Signals, Signal, SignalHandler, SignalAdapter } from "./signals";
import { inspect } from "util";
export class Script {
destroyed: Signal<ScriptDestroyedHandler>;
message: Signal<ScriptMessageHandler>;
private exportsProxy: any;
private logHandlerImpl: ScriptLogHandler = log;
constructor(private impl: any) {
const services = new ScriptServices(this, impl.signals);
const rpcController: RpcController = services;
this.exportsProxy = new ScriptExportsProxy(rpcController);
const signals: Signals = services;
this.destroyed = new Signal<ScriptDestroyedHandler>(signals, "destroyed");
this.message = new Signal<ScriptMessageHandler>(signals, "message");
}
get isDestroyed(): boolean {
return this.impl.isDestroyed;
}
get exports(): ScriptExports {
return this.exportsProxy;
}
get logHandler(): ScriptLogHandler {
return this.logHandlerImpl;
}
set logHandler(handler: ScriptLogHandler) {
this.logHandlerImpl = handler;
}
get defaultLogHandler(): ScriptLogHandler {
return log;
}
load(cancellable?: Cancellable): Promise<void> {
return this.impl.load(cancellable);
}
unload(cancellable?: Cancellable): Promise<void> {
return this.impl.unload(cancellable);
}
eternalize(cancellable?: Cancellable): Promise<void> {
return this.impl.eternalize(cancellable);
}
post(message: any, data: Buffer | null = null): void {
this.impl.post(message, data);
}
enableDebugger(options: EnableDebuggerOptions = {}, cancellable?: Cancellable): Promise<void> {
const { port = 0 } = options;
return this.impl.enableDebugger(port, cancellable);
}
disableDebugger(cancellable?: Cancellable): Promise<void> {
return this.impl.disableDebugger(cancellable);
}
[inspect.custom](depth, options) {
return "Script {}";
}
}
export interface ScriptOptions {
name?: string;
snapshot?: Buffer;
runtime?: ScriptRuntime;
}
export interface SnapshotOptions {
warmupScript?: string;
runtime?: ScriptRuntime;
}
export enum ScriptRuntime {
Default = "default",
QJS = "qjs",
V8 = "v8"
}
export type ScriptDestroyedHandler = () => void;
export type ScriptMessageHandler = (message: Message, data: Buffer | null) => void;
export type ScriptLogHandler = (level: LogLevel, text: string) => void;
export type Message = SendMessage | ErrorMessage;
export enum MessageType {
Send = "send",
Error = "error"
}
export interface SendMessage {
type: MessageType.Send;
payload: any;
}
export interface ErrorMessage {
type: MessageType.Error;
description: string;
stack?: string;
fileName?: string;
lineNumber?: number;
columnNumber?: number;
}
interface LogMessage {
type: "log";
level: LogLevel;
payload: string;
}
export interface ScriptExports {
[name: string]: (...args: any[]) => Promise<any>;
}
export enum LogLevel {
Info = "info",
Warning = "warning",
Error = "error"
}
export interface EnableDebuggerOptions {
port?: number;
}
class ScriptServices extends SignalAdapter implements RpcController {
private pendingRequests: { [id: string]: (error: Error | null, result?: any) => void } = {};
private nextRequestId: number = 1;
constructor(private script: Script, signals: Signals) {
super(signals);
this.signals.connect("destroyed", this.onDestroyed);
this.signals.connect("message", this.onMessage);
}
protected getProxy(name: string, userHandler: SignalHandler): SignalHandler | null {
if (name === "message") {
return (message, data) => {
if (!isInternalMessage(message)) {
userHandler(message, data);
}
};
}
return null;
}
private onDestroyed = () => {
this.signals.disconnect("destroyed", this.onDestroyed);
this.signals.disconnect("message", this.onMessage);
}
private onMessage = (message: Message, data: Buffer | null) => {
if (message.type === MessageType.Send && isRpcSendMessage(message)) {
const [ , id, operation, ...params ] = message.payload;
this.onRpcMessage(id, operation, params, data);
} else if (isLogMessage(message)) {
const opaqueMessage: any = message;
const logMessage: LogMessage = opaqueMessage;
this.script.logHandler(logMessage.level, logMessage.payload);
}
}
request(operation: string, params: any[], data: Buffer | null, cancellable?: Cancellable): Promise<any> {
return new Promise((resolve, reject) => {
const id = this.nextRequestId++;
const complete = (error: Error | null, result?: any): void => {
if (cancellable !== undefined) {
cancellable.cancelled.disconnect(onOperationCancelled);
}
this.signals.disconnect("destroyed", onScriptDestroyed);
delete this.pendingRequests[id];
if (error === null) {
resolve(result);
} else {
reject(error);
}
};
function onScriptDestroyed(): void {
complete(new Error("Script is destroyed"));
}
function onOperationCancelled(): void {
complete(new Error("Operation was cancelled"));
}
this.pendingRequests[id] = complete;
this.script.post(["frida:rpc", id, operation, ...params], data);
this.signals.connect("destroyed", onScriptDestroyed);
if (cancellable !== undefined) {
cancellable.cancelled.connect(onOperationCancelled);
if (cancellable.isCancelled) {
onOperationCancelled();
return;
}
}
if (this.script.isDestroyed) {
onScriptDestroyed();
}
});
}
onRpcMessage(id: number, operation: RpcOperation, params: any[], data: Buffer | null) {
if (operation === RpcOperation.Ok || operation === RpcOperation.Error) {
const callback = this.pendingRequests[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);
}
}
}
function ScriptExportsProxy(rpcController: RpcController): void {
return new Proxy(this, {
has(target, property) {
return !isReservedMethodName(property);;
},
get(target, property, receiver) {
if (property in target) {
return target[property];
}
if (property === inspect.custom) {
return inspectProxy;
}
if (isReservedMethodName(property)) {
return undefined;
}
return (...args: any[]): Promise<any> => {
let cancellable: Cancellable | undefined;
if (args[args.length - 1] instanceof Cancellable) {
cancellable = args.pop();
}
let data: Buffer | null = null;
if (Buffer.isBuffer(args[args.length - 1])) {
data = args.pop();
}
return rpcController.request("call", [property, args], data, cancellable);
};
},
set(target, property, value, receiver) {
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 {}";
}
interface RpcController {
request(operation: string, params: any[], data: ArrayBuffer | null, cancellable?: Cancellable): Promise<any>;
}
enum RpcOperation {
Ok = "ok",
Error = "error"
}
function isInternalMessage(message: Message): boolean {
return isRpcMessage(message) || isLogMessage(message);
}
function isRpcMessage(message: Message): boolean {
return message.type === MessageType.Send && isRpcSendMessage(message);
}
function isRpcSendMessage(message: SendMessage): boolean {
const payload = message.payload;
if (!Array.isArray(payload)) {
return false;
}
return payload[0] === "frida:rpc";
}
function isLogMessage(message: Message): boolean {
return message.type as string === "log";
}
function log(level: LogLevel, text: string): void {
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<string>([
"then",
"catch",
"finally",
]);
function isReservedMethodName(name: string | number | symbol): boolean {
return reservedMethodNames.has(name.toString());
}
-34
View File
@@ -1,34 +0,0 @@
import { Cancellable } from "./cancellable";
import { Signals, Signal } from "./signals";
import { inspect } from "util";
export class Service {
close: Signal<ServiceCloseHandler>;
message: Signal<ServiceMessageHandler>;
constructor(private impl: any) {
const signals: Signals = impl.signals;
this.close = new Signal<ServiceCloseHandler>(signals, "close");
this.message = new Signal<ServiceMessageHandler>(signals, "message");
}
activate(cancellable?: Cancellable): Promise<void> {
return this.impl.activate(cancellable);
}
cancel(cancellable?: Cancellable): Promise<void> {
return this.impl.cancel(cancellable);
}
request(parameters: any, cancellable?: Cancellable): Promise<any> {
return this.impl.request(parameters, cancellable);
}
[inspect.custom](depth, options) {
return "Service {}";
}
}
export type ServiceCloseHandler = () => void;
export type ServiceMessageHandler = (message: any) => void;
-113
View File
@@ -1,113 +0,0 @@
import { Cancellable } from "./cancellable";
import { Crash } from "./crash";
import { PortalMembership } from "./portal_membership";
import { Relay } from "./relay";
import { Script, ScriptOptions, SnapshotOptions } from "./script";
import { Signal } from "./signals";
import { inspect } from "util";
export class Session {
detached: Signal<SessionDetachedHandler>;
constructor(private impl: any) {
const { signals } = impl;
this.detached = new Signal<SessionDetachedHandler>(signals, "detached");
}
get pid(): number {
return this.impl.pid;
}
get persistTimeout(): number {
return this.impl.persistTimeout;
}
get isDetached(): boolean {
return this.impl.isDetached;
}
detach(cancellable?: Cancellable): Promise<void> {
return this.impl.detach(cancellable);
}
resume(cancellable?: Cancellable): Promise<void> {
return this.impl.resume(cancellable);
}
enableChildGating(cancellable?: Cancellable): Promise<void> {
return this.impl.enableChildGating(cancellable);
}
disableChildGating(cancellable?: Cancellable): Promise<void> {
return this.impl.disableChildGating(cancellable);
}
async createScript(source: string, options: ScriptOptions = {}, cancellable?: Cancellable): Promise<Script> {
const { name = null, snapshot = null, runtime = null } = options;
return new Script(await this.impl.createScript(source, name, snapshot, runtime, cancellable));
}
async createScriptFromBytes(bytes: Buffer, options: ScriptOptions = {}, cancellable?: Cancellable): Promise<Script> {
const { name = null, snapshot = null, runtime = null } = options;
return new Script(await this.impl.createScriptFromBytes(bytes, name, snapshot, runtime, cancellable));
}
compileScript(source: string, options: ScriptOptions = {}, cancellable?: Cancellable): Promise<Buffer> {
const { name = null, runtime = null } = options;
return this.impl.compileScript(source, name, runtime, cancellable);
}
snapshotScript(embedScript: string, options: SnapshotOptions = {}, cancellable?: Cancellable): Promise<Buffer> {
const { warmupScript = null, runtime = null } = options;
return this.impl.snapshotScript(embedScript, warmupScript, runtime, cancellable);
}
setupPeerConnection(options: PeerOptions = {}, cancellable?: Cancellable): Promise<void> {
const {
stunServer = null,
relays = [],
} = options;
return this.impl.setupPeerConnection(stunServer, relays, cancellable);
}
async joinPortal(address: string, options: PortalOptions = {}, cancellable?: Cancellable): Promise<PortalMembership> {
const {
certificate = null,
token = null,
acl = null,
} = options;
return new PortalMembership(await this.impl.joinPortal(address, certificate, token, acl, cancellable));
}
[inspect.custom](depth, options) {
return "Session " + inspect({
pid: this.pid
}, options);
}
}
export type SessionDetachedHandler = (reason: SessionDetachReason, crash: Crash | null) => void;
export enum SessionDetachReason {
ApplicationRequested = "application-requested",
ProcessReplaced = "process-replaced",
ProcessTerminated = "process-terminated",
ConnectionTerminated = "connection-terminated",
DeviceLost = "device-lost"
}
export interface PeerOptions {
stunServer?: string;
relays?: Relay[];
}
export interface PortalOptions {
certificate?: string;
token?: string;
acl?: string[];
}
-45
View File
@@ -1,45 +0,0 @@
export interface Signals {
connect(name: string, handler: SignalHandler): void;
disconnect(name: string, handler: SignalHandler): void;
}
export class Signal<T extends SignalHandler> {
constructor(private signals: Signals, private name: string) {
}
connect(handler: T): void {
this.signals.connect(this.name, handler);
}
disconnect(handler: T): void {
this.signals.disconnect(this.name, handler);
}
}
export type SignalHandler = (...args: any[]) => void;
export class SignalAdapter implements Signals {
private proxyHandlers: Map<SignalHandler, SignalHandler> = new Map();
constructor(protected signals: Signals) {
}
connect(name: string, handler: SignalHandler): void {
const proxyHandler = this.getProxy(name, handler);
if (proxyHandler !== null) {
this.proxyHandlers.set(handler, proxyHandler);
this.signals.connect(name, proxyHandler);
} else {
this.signals.connect(name, handler);
}
}
disconnect(name: string, handler: SignalHandler): void {
const proxyHandler = this.proxyHandlers.get(handler);
this.signals.disconnect(name, (proxyHandler !== undefined) ? proxyHandler : handler);
}
protected getProxy(name: string, userHandler: SignalHandler): SignalHandler | null {
return null;
}
}
-35
View File
@@ -1,35 +0,0 @@
export type SocketAddress =
| IPV4SocketAddress
| IPV6SocketAddress
| AnonymousUnixSocketAddress
| PathUnixSocketAddress
| AbstractUnixSocketAddress
;
export interface IPV4SocketAddress {
family: "ipv4";
address: string;
port: number;
}
export interface IPV6SocketAddress {
family: "ipv6";
address: string;
port: number;
flowlabel: number;
scopeid: number;
}
export interface AnonymousUnixSocketAddress {
family: "unix:anonymous";
}
export interface PathUnixSocketAddress {
family: "unix:path";
path: string;
}
export interface AbstractUnixSocketAddress {
family: "unix:abstract";
path: Buffer;
}
-4
View File
@@ -1,4 +0,0 @@
export interface Spawn {
pid: number;
identifier: string | null;
}
-103
View File
@@ -1,103 +0,0 @@
export interface SystemParameters {
/**
* Operating System details.
*/
os: {
/**
* ID, e.g.: windows, macos, linux, ios, android, qnx, fedora, ubuntu, etc.
*/
id: string;
/**
* Human-readable name, e.g. `"macOS"`.
*/
name: string;
/**
* Human-readable version string, e.g. `"11.2.2"`.
*/
version?: string;
/**
* Build version, e.g. `"21B91"`.
*/
build?: string;
}
/**
* Platform, same as `Process.platform` in GumJS.
*/
platform: "windows" | "darwin" | "linux" | "qnx";
/**
* Architecture, same as `Process.arch` in GumJS.
*/
arch: "ia32" | "x64" | "arm" | "arm64" | "mips";
/**
* Hardware details.
*/
hardware?: {
/**
* Product type, e.g. `"iPad6,3"`.
*/
product?: string;
/**
* Hardware platform, e.g. `"t8010"`.
*/
platform?: string;
/**
* Hardware model, e.g. `"J71bAP"`.
*/
model?: string;
}
/**
* Level of access.
*/
access: "full" | "jailed";
/**
* System name, e.g. `"Ole André’s iPhone"`.
*/
name?: string;
/**
* iOS UDID (Unique Device ID).
*/
udid?: string;
/**
* Details about cellular and networking interfaces.
*/
interfaces?: SystemInterface[];
/**
* Android API level, e.g.: `30`.
*/
apiLevel?: number;
[name: string]: any;
}
export type SystemInterface = NetworkInterface | CellularInterface;
export interface NetworkInterface {
type: "ethernet" | "wifi" | "bluetooth";
/**
* MAC address, e.g.: `"aa:bb:cc:dd:ee:ff"`.
*/
address: string;
}
export interface CellularInterface {
type: "cellular";
/**
* Phone number, e.g. `"+47 123 45 678"`.
*/
phoneNumber: string;
}
+40 -47
View File
@@ -1,12 +1,12 @@
project('frida-node', 'cpp',
project('frida-node', 'c',
version: run_command(find_program('python3'), files('scripts' / 'detect-version.py'),
capture: true,
check: true).stdout().strip(),
meson_version: '>=1.1.0',
)
cpp = meson.get_compiler('cpp')
strip = (cpp.get_argument_syntax() == 'msvc') ? '' : find_program('strip')
cc = meson.get_compiler('c')
strip = (cc.get_argument_syntax() == 'msvc') ? '' : find_program('strip')
node = find_program('node')
npm = find_program('npm')
@@ -76,16 +76,16 @@ foreach item : metadata
endif
endforeach
extra_cpp_args = []
extra_c_args = []
extra_link_args = []
extra_link_depends = []
foreach d : node_defines
extra_cpp_args += '-D' + d
extra_c_args += '-D' + d
endforeach
if host_machine.system() == 'windows'
extra_cpp_args += [
extra_c_args += [
'/Zc:__cplusplus',
'/Zc:inline',
'/Zc:threadSafeInit-',
@@ -101,53 +101,13 @@ if host_machine.system() == 'windows'
]
endif
nan_dep = dependency('nan')
frida_core_dep = dependency('frida-core-1.0', default_options: [
f'frida_version=@version@',
'compiler_backend=disabled',
])
subdir('src')
pkg_install_dir = get_option('libdir') / 'node_modules' / 'frida'
binding = shared_module('frida_binding', frida_binding_sources,
name_prefix: '',
name_suffix: 'node',
include_directories: include_directories(node_incdirs),
implicit_include_directories: false,
override_options: [
'cpp_std=c++17',
'cpp_eh=none',
'cpp_rtti=false',
],
cpp_args: [
'-DBUILDING_NODE_EXTENSION',
'-DNODE_GYP_MODULE_NAME=frida_binding',
f'-DHOST_BINARY="@host_binary@"',
'-DFRIDA_' + runtime.underscorify().to_upper(),
] + extra_cpp_args,
link_args: node_libs + extra_link_args,
link_depends: extra_link_depends,
dependencies: [frida_core_dep, nan_dep],
install: true,
install_dir: pkg_install_dir / 'build',
)
custom_target('prebuild',
input: [binding],
output: [f'frida-v@version@-@runtime@-v@abi@-@node_platform@-@[email protected]'],
command: [
python,
files('scripts' / 'package.py'),
'>>>', strip, '<<<',
get_option('strip').to_string(),
'@INPUT@',
'@OUTPUT@',
],
)
package_json = custom_target('package.json',
input: 'package.json',
output: 'package.json',
@@ -163,4 +123,37 @@ package_json = custom_target('package.json',
)
tsconfig_json = files('tsconfig.json')
subdir('lib')
subdir('src')
binding = shared_module('frida_binding', frida_binding_sources,
name_prefix: '',
name_suffix: 'node',
include_directories: include_directories(node_incdirs),
implicit_include_directories: false,
c_args: [
'-DBUILDING_NODE_EXTENSION',
'-DNODE_GYP_MODULE_NAME=frida_binding',
'-DNAPI_VERSION=8',
f'-DHOST_BINARY="@host_binary@"',
'-DFRIDA_' + runtime.underscorify().to_upper(),
] + extra_c_args,
link_args: node_libs + extra_link_args,
link_depends: extra_link_depends,
dependencies: frida_core_dep,
install: true,
install_dir: pkg_install_dir / 'build',
vs_module_defs: 'src' / 'addon.def',
)
custom_target('prebuild',
input: [binding],
output: [f'frida-v@version@-@runtime@-v@abi@-@node_platform@-@[email protected]'],
command: [
python,
files('scripts' / 'package.py'),
'>>>', strip, '<<<',
get_option('strip').to_string(),
'@INPUT@',
'@OUTPUT@',
],
)
+681 -270
View File
File diff suppressed because it is too large. Load diff
+21 -18
View File
@@ -21,20 +21,24 @@
],
"homepage": "https://frida.re",
"engines": {
"node": ">=16 || 14 >=14.17"
"node": ">=16"
},
"main": "./dist",
"main": "build/src/frida.js",
"exports": {
".": "./build/src/frida.js"
},
"type": "module",
"files": [
"BSDmakefile",
"Makefile",
"build/src/*.d.ts",
"build/src/*.js",
"configure",
"configure.bat",
"dist/",
"make.bat",
"meson.build",
"meson.options",
"tsconfig.json",
"lib/",
"releng/*.py",
"releng/*.toml",
"releng/devkit-assets/",
@@ -51,25 +55,24 @@
],
"dependencies": {
"bindings": "^1.5.0",
"minimatch": "^9.0.3",
"nan": "^2.18.0",
"prebuild-install": "^7.1.1"
"minimatch": "^10.0.1",
"prebuild-install": "^7.1.3"
},
"devDependencies": {
"@types/chai": "^4.2.18",
"@types/mocha": "^10.0.3",
"@types/node": "^20.8.8",
"chai": "^4.3.4",
"mocha": "^10.0.0",
"ts-node": "^10.0.0",
"typescript": "^5.2.2"
"@types/bindings": "^1.5.5",
"@types/chai": "^5.2.1",
"@types/mocha": "^10.0.10",
"@types/node": "^22.13.16",
"chai": "^5.2.0",
"mocha": "^11.1.0",
"ts-node": "^10.9.2",
"typescript": "^5.8.2"
},
"scripts": {
"prepare": "npm run build",
"build": "tsc",
"watch": "tsc -w",
"install": "node scripts/install.js",
"test": "node --expose-gc node_modules/mocha/bin/_mocha -r ts-node/register test/*.ts"
"test": "node --loader ts-node/esm --expose-gc node_modules/mocha/bin/_mocha --extension ts test/*.ts",
"examples:build": "tsc -p tsconfig.examples.json",
"examples:watch": "tsc -p tsconfig.examples.json -w"
},
"binary": {
"host": "https://github.com",
+5 -4
View File
@@ -1,8 +1,9 @@
const { execSync } = require('child_process');
const fs = require('fs');
const path = require('path');
import { execSync } from 'child_process';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const pkgRoot = path.dirname(path.dirname(__filename));
const pkgRoot = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
const binding = path.join(pkgRoot, 'build', 'frida_binding.node');
if (fs.existsSync(binding)) {
process.exit(0);
-78
View File
@@ -1,78 +0,0 @@
#include "application.h"
#include "bus.h"
#include "cancellable.h"
#include "child.h"
#include "crash.h"
#include "device.h"
#include "device_manager.h"
#include "endpoint_parameters.h"
#include "glib_context.h"
#include "iostream.h"
#include "portal_membership.h"
#include "portal_service.h"
#include "process.h"
#include "relay.h"
#include "runtime.h"
#include "script.h"
#include "service.h"
#include "session.h"
#include "signals.h"
#include "spawn.h"
#include "uv_context.h"
using v8::Context;
using v8::Local;
using v8::Object;
using v8::Value;
namespace frida {
static void DisposeAll(void* data);
static void InitAll(Local<Object> exports,
Local<Value> module,
Local<Context> context) {
frida_init();
auto uv_context = new UVContext(uv_default_loop());
auto glib_context = new GLibContext(frida_get_main_context());
auto runtime = new Runtime(uv_context, glib_context);
Signals::Init(exports, runtime);
DeviceManager::Init(exports, runtime);
Device::Init(exports, runtime);
Application::Init(exports, runtime);
Process::Init(exports, runtime);
Spawn::Init(exports, runtime);
Child::Init(exports, runtime);
Crash::Init(exports, runtime);
Bus::Init(exports, runtime);
Service::Init(exports, runtime);
Session::Init(exports, runtime);
Script::Init(exports, runtime);
Relay::Init(exports, runtime);
PortalMembership::Init(exports, runtime);
PortalService::Init(exports, runtime);
EndpointParameters::Init(exports, runtime);
IOStream::Init(exports, runtime);
Cancellable::Init(exports, runtime);
#if NODE_VERSION_AT_LEAST(11, 0, 0)
node::AddEnvironmentCleanupHook(context->GetIsolate(), DisposeAll, runtime);
#else
node::AtExit(DisposeAll, runtime);
#endif
}
static void DisposeAll(void* data) {
auto runtime = static_cast<Runtime*>(data);
DeviceManager::Dispose(runtime);
delete runtime;
}
}
NODE_MODULE_CONTEXT_AWARE(frida_binding, frida::InitAll)
+3
View File
@@ -0,0 +1,3 @@
EXPORTS
node_api_module_get_api_version_v1
napi_register_module_v1
+2 -1
View File
@@ -1 +1,2 @@
# No exported symbols.
_node_api_module_get_api_version_v1
_napi_register_module_v1
+4
View File
@@ -1,4 +1,8 @@
{
global:
node_api_module_get_api_version_v1;
napi_register_module_v1;
local:
*;
};
-148
View File
@@ -1,148 +0,0 @@
#include "application.h"
#include <cstring>
#define APPLICATION_DATA_CONSTRUCTOR "application:ctor"
using std::strcmp;
using v8::DEFAULT;
using v8::External;
using v8::Function;
using v8::Integer;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::Persistent;
using v8::ReadOnly;
using v8::String;
using v8::Value;
namespace frida {
Application::Application(FridaApplication* handle, Runtime* runtime)
: GLibObject(handle, runtime) {
g_object_ref(handle_);
}
Application::~Application() {
g_object_unref(handle_);
}
void Application::Init(Local<Object> exports, Runtime* runtime) {
auto isolate = Isolate::GetCurrent();
auto name = Nan::New("Application").ToLocalChecked();
auto tpl = CreateTemplate(name, Application::New, runtime);
auto instance_tpl = tpl->InstanceTemplate();
auto data = Local<Value>();
Nan::SetAccessor(instance_tpl, Nan::New("parameters").ToLocalChecked(),
GetParameters, 0, data, DEFAULT, ReadOnly);
Nan::SetAccessor(instance_tpl, Nan::New("pid").ToLocalChecked(),
GetPid, 0, data, DEFAULT, ReadOnly);
Nan::SetAccessor(instance_tpl, Nan::New("name").ToLocalChecked(),
GetName, 0, data, DEFAULT, ReadOnly);
Nan::SetAccessor(instance_tpl, Nan::New("identifier").ToLocalChecked(),
GetIdentifier, 0, data, DEFAULT, ReadOnly);
auto ctor = Nan::GetFunction(tpl).ToLocalChecked();
Nan::Set(exports, name, ctor);
runtime->SetDataPointer(APPLICATION_DATA_CONSTRUCTOR,
new Persistent<Function>(isolate, ctor));
}
Local<Object> Application::New(gpointer handle, Runtime* runtime) {
auto ctor = Nan::New<Function>(
*static_cast<Persistent<Function>*>(
runtime->GetDataPointer(APPLICATION_DATA_CONSTRUCTOR)));
const int argc = 1;
Local<Value> argv[argc] = { Nan::New<External>(handle) };
return Nan::NewInstance(ctor, argc, argv).ToLocalChecked();
}
NAN_METHOD(Application::New) {
if (!info.IsConstructCall()) {
Nan::ThrowError("Use the `new` keyword to create a new instance");
return;
}
if (info.Length() != 1 || !info[0]->IsExternal()) {
Nan::ThrowTypeError("Bad argument, expected raw handle");
return;
}
auto runtime = GetRuntimeFromConstructorArgs(info);
auto handle = static_cast<FridaApplication*>(
Local<External>::Cast(info[0])->Value());
auto wrapper = new Application(handle, runtime);
auto obj = info.This();
wrapper->Wrap(obj);
info.GetReturnValue().Set(obj);
}
NAN_PROPERTY_GETTER(Application::GetIdentifier) {
auto handle = ObjectWrap::Unwrap<Application>(
info.Holder())->GetHandle<FridaApplication>();
info.GetReturnValue().Set(
Nan::New(frida_application_get_identifier(handle)).ToLocalChecked());
}
NAN_PROPERTY_GETTER(Application::GetName) {
auto handle = ObjectWrap::Unwrap<Application>(
info.Holder())->GetHandle<FridaApplication>();
info.GetReturnValue().Set(
Nan::New(frida_application_get_name(handle)).ToLocalChecked());
}
NAN_PROPERTY_GETTER(Application::GetPid) {
auto isolate = info.GetIsolate();
auto handle = ObjectWrap::Unwrap<Application>(
info.Holder())->GetHandle<FridaApplication>();
info.GetReturnValue().Set(
Integer::NewFromUnsigned(isolate, frida_application_get_pid(handle)));
}
NAN_PROPERTY_GETTER(Application::GetParameters) {
auto wrapper = ObjectWrap::Unwrap<Application>(info.Holder());
auto handle = wrapper->GetHandle<FridaApplication>();
GHashTable* parameters = frida_application_get_parameters(handle);
info.GetReturnValue().Set(ParseParameters(parameters));
}
Local<Value> Application::ParseParameters(GHashTable* dict) {
auto result = Nan::New<Object>();
GHashTableIter iter;
gpointer raw_key, raw_value;
g_hash_table_iter_init(&iter, dict);
while (g_hash_table_iter_next(&iter, &raw_key, &raw_value)) {
char* canonicalized_key =
Runtime::ParameterNameFromC(static_cast<char*>(raw_key));
GVariant* var_value = static_cast<GVariant*>(raw_value);
Local<String> key = Nan::New(canonicalized_key).ToLocalChecked();
Local<Value> value;
if (strcmp(canonicalized_key, "started") == 0 &&
g_variant_is_of_type(var_value, G_VARIANT_TYPE_STRING)) {
value = Runtime::ValueFromDatetime(g_variant_get_string(var_value, NULL));
} else {
value = Runtime::ValueFromVariant(var_value);
}
Nan::Set(result, key, value);
g_free(canonicalized_key);
}
return result;
}
}
-31
View File
@@ -1,31 +0,0 @@
#ifndef FRIDANODE_APPLICATION_H
#define FRIDANODE_APPLICATION_H
#include "glib_object.h"
#include <frida-core.h>
namespace frida {
class Application : public GLibObject {
public:
static void Init(v8::Local<v8::Object> exports, Runtime* runtime);
static v8::Local<v8::Object> New(gpointer handle, Runtime* runtime);
private:
explicit Application(FridaApplication* handle, Runtime* runtime);
~Application();
static NAN_METHOD(New);
static NAN_PROPERTY_GETTER(GetIdentifier);
static NAN_PROPERTY_GETTER(GetName);
static NAN_PROPERTY_GETTER(GetPid);
static NAN_PROPERTY_GETTER(GetParameters);
static v8::Local<v8::Value> ParseParameters(GHashTable* dict);
};
}
#endif
-174
View File
@@ -1,174 +0,0 @@
#include "authentication.h"
using frida::Runtime;
using v8::External;
using v8::Function;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::Persistent;
using v8::String;
using v8::Value;
struct _FridaNodeAuthenticationService {
GObject parent;
Persistent<Function>* callback;
Runtime* runtime;
};
static void frida_node_authentication_service_iface_init(gpointer g_iface,
gpointer iface_data);
static void frida_node_authentication_service_dispose(GObject* object);
static void frida_node_authentication_service_authenticate(
FridaAuthenticationService* service, const gchar* token,
GCancellable* cancellable, GAsyncReadyCallback callback,
gpointer user_data);
static gchar* frida_node_authentication_service_authenticate_finish(
FridaAuthenticationService* service, GAsyncResult* result, GError** error);
G_DEFINE_TYPE_EXTENDED(
FridaNodeAuthenticationService,
frida_node_authentication_service,
G_TYPE_OBJECT,
0,
G_IMPLEMENT_INTERFACE(FRIDA_TYPE_AUTHENTICATION_SERVICE,
frida_node_authentication_service_iface_init))
FridaAuthenticationService* frida_node_authentication_service_new(
Local<Function> callback, Runtime* runtime) {
auto service = static_cast<FridaNodeAuthenticationService*>(
g_object_new(FRIDA_TYPE_NODE_AUTHENTICATION_SERVICE, NULL));
service->callback = new Persistent<Function>(Isolate::GetCurrent(), callback);
service->runtime = runtime;
return FRIDA_AUTHENTICATION_SERVICE(service);
}
static void frida_node_authentication_service_class_init(
FridaNodeAuthenticationServiceClass* klass)
{
GObjectClass* object_class = G_OBJECT_CLASS(klass);
object_class->dispose = frida_node_authentication_service_dispose;
}
static void frida_node_authentication_service_iface_init(gpointer g_iface,
gpointer iface_data) {
auto iface = static_cast<FridaAuthenticationServiceIface*>(g_iface);
iface->authenticate =
frida_node_authentication_service_authenticate;
iface->authenticate_finish =
frida_node_authentication_service_authenticate_finish;
}
static void frida_node_authentication_service_init(
FridaNodeAuthenticationService* self) {
}
static void frida_node_authentication_service_dispose(GObject* object) {
auto self = FRIDA_NODE_AUTHENTICATION_SERVICE(object);
Persistent<Function>* callback = self->callback;
if (callback != NULL) {
self->callback = NULL;
self->runtime->GetUVContext()->Schedule([=]() {
delete callback;
});
}
G_OBJECT_CLASS(frida_node_authentication_service_parent_class)->dispose(
object);
}
static NAN_METHOD(OnAuthenticationSuccess) {
auto task = static_cast<GTask*>(info.Data().As<External>()->Value ());
auto self = static_cast<FridaNodeAuthenticationService*>(
g_task_get_source_object(task));
gchar* session_info = NULL;
if (info.Length() >= 1) {
auto val = info[0];
if (val->IsObject() && !val->IsNull()) {
Local<String> json = self->runtime->ValueToJson(val);
Nan::Utf8String str(json);
session_info = g_strdup(*str);
}
}
if (session_info != NULL) {
g_task_return_pointer(task, session_info, g_free);
} else {
g_task_return_new_error(task, FRIDA_ERROR, FRIDA_ERROR_INVALID_ARGUMENT,
"Internal error");
}
g_object_unref(task);
}
static NAN_METHOD(OnAuthenticationFailure) {
auto task = static_cast<GTask*>(info.Data().As<External>()->Value ());
Local<Value> fallback_message = Nan::New("Internal error").ToLocalChecked();
Local<Value> message = fallback_message;
if (info.Length() >= 1) {
auto error_value = info[0];
if (error_value->IsObject()) {
message = Nan::Get(error_value.As<Object>(),
Nan::New("message").ToLocalChecked()).FromMaybe(fallback_message);
}
}
Nan::Utf8String str(message);
g_task_return_new_error(task, FRIDA_ERROR, FRIDA_ERROR_INVALID_ARGUMENT,
"%s", *str);
g_object_unref(task);
}
static void frida_node_authentication_service_authenticate(
FridaAuthenticationService* service, const gchar* token,
GCancellable* cancellable, GAsyncReadyCallback callback,
gpointer user_data) {
auto self = FRIDA_NODE_AUTHENTICATION_SERVICE(service);
auto task = g_task_new(self, cancellable, callback, user_data);
g_task_set_task_data(task, g_strdup(token), g_free);
self->runtime->GetUVContext()->Schedule([=]() {
auto context = Isolate::GetCurrent()->GetCurrentContext();
bool scheduled = false;
Local<Value> no_result = Nan::Undefined();
auto callback = Nan::New<Function>(*self->callback);
Local<Value> token_value = Nan::New(static_cast<gchar*>(
g_task_get_task_data(task))).ToLocalChecked();
Local<Value> promise_value = callback->Call(context, Nan::Undefined(), 1,
&token_value).FromMaybe(no_result);
if (promise_value->IsObject()) {
auto promise = promise_value.As<Object>();
auto then = Nan::Get(promise, Nan::New("then").ToLocalChecked())
.FromMaybe(no_result);
if (then->IsFunction()) {
Local<Value> argv[] = {
Nan::New<Function>(OnAuthenticationSuccess, Nan::New<External>(task)),
Nan::New<Function>(OnAuthenticationFailure, Nan::New<External>(task)),
};
scheduled = !then.As<Function>()->Call(context, promise_value,
G_N_ELEMENTS(argv), argv).IsEmpty();
}
}
if (!scheduled) {
g_task_return_new_error(task, FRIDA_ERROR, FRIDA_ERROR_INVALID_ARGUMENT,
"Internal error");
g_object_unref(task);
}
});
}
static gchar* frida_node_authentication_service_authenticate_finish(
FridaAuthenticationService* service, GAsyncResult* result, GError** error) {
return static_cast<gchar*>(g_task_propagate_pointer(G_TASK(result), error));
}
-24
View File
@@ -1,24 +0,0 @@
#ifndef FRIDANODE_AUTHENTICATION_H
#define FRIDANODE_AUTHENTICATION_H
#include "runtime.h"
#include <frida-core.h>
#include <node.h>
G_BEGIN_DECLS
#define FRIDA_TYPE_NODE_AUTHENTICATION_SERVICE (frida_node_authentication_service_get_type())
G_DECLARE_FINAL_TYPE(
FridaNodeAuthenticationService,
frida_node_authentication_service,
FRIDA,
NODE_AUTHENTICATION_SERVICE,
GObject)
FridaAuthenticationService* frida_node_authentication_service_new(
v8::Local<v8::Function> callback, frida::Runtime* runtime);
G_END_DECLS
#endif
-167
View File
@@ -1,167 +0,0 @@
#include "bus.h"
#include "operation.h"
#include "signals.h"
#include "usage_monitor.h"
#include <cstring>
#define BUS_DATA_CONSTRUCTOR "bus:ctor"
using std::strcmp;
using v8::External;
using v8::Function;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::Persistent;
using v8::Value;
namespace frida {
Bus::Bus(FridaBus* handle, Runtime* runtime)
: GLibObject(handle, runtime) {
g_object_ref(handle_);
}
Bus::~Bus() {
g_object_unref(handle_);
}
void Bus::Init(Local<Object> exports, Runtime* runtime) {
auto isolate = Isolate::GetCurrent();
auto name = Nan::New("Bus").ToLocalChecked();
auto tpl = CreateTemplate(name, New, runtime);
Nan::SetPrototypeMethod(tpl, "attach", Attach);
Nan::SetPrototypeMethod(tpl, "post", Post);
auto ctor = Nan::GetFunction(tpl).ToLocalChecked();
Nan::Set(exports, name, ctor);
runtime->SetDataPointer(BUS_DATA_CONSTRUCTOR,
new Persistent<Function>(isolate, ctor));
}
Local<Object> Bus::New(gpointer handle, Runtime* runtime) {
auto ctor = Nan::New<Function>(
*static_cast<Persistent<Function>*>(
runtime->GetDataPointer(BUS_DATA_CONSTRUCTOR)));
const int argc = 1;
Local<Value> argv[argc] = { Nan::New<External>(handle) };
return Nan::NewInstance(ctor, argc, argv).ToLocalChecked();
}
NAN_METHOD(Bus::New) {
if (!info.IsConstructCall()) {
Nan::ThrowError("Use the `new` keyword to create a new instance");
return;
}
if (info.Length() != 1 || !info[0]->IsExternal()) {
Nan::ThrowTypeError("Bad argument, expected raw handle");
return;
}
auto runtime = GetRuntimeFromConstructorArgs(info);
auto handle = static_cast<FridaBus*>(
Local<External>::Cast(info[0])->Value());
auto wrapper = new Bus(handle, runtime);
auto obj = info.This();
wrapper->Wrap(obj);
auto signals_obj = Signals::New(handle, runtime, TransformSignal, runtime);
Nan::Set(obj, Nan::New("signals").ToLocalChecked(), signals_obj);
auto signals_wrapper = ObjectWrap::Unwrap<Signals>(signals_obj);
signals_wrapper->SetConnectCallback(OnConnect, runtime);
signals_wrapper->SetDisconnectCallback(OnDisconnect, runtime);
info.GetReturnValue().Set(obj);
}
namespace {
class AttachOperation : public Operation<FridaBus> {
protected:
void Begin() {
frida_bus_attach(handle_, cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
frida_bus_attach_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
return Nan::Undefined();
}
};
}
NAN_METHOD(Bus::Attach) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<Bus>(info.Holder());
auto operation = new AttachOperation();
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
NAN_METHOD(Bus::Post) {
auto wrapper = ObjectWrap::Unwrap<Bus>(info.Holder());
auto num_args = info.Length();
if (num_args < 2) {
Nan::ThrowTypeError("Expected message and data");
return;
}
Nan::Utf8String message(wrapper->runtime_->ValueToJson(info[0]));
GBytes* data = NULL;
auto buffer = info[1];
if (!buffer->IsNull()) {
if (!node::Buffer::HasInstance(buffer)) {
Nan::ThrowTypeError("Expected a buffer");
return;
}
data = g_bytes_new(node::Buffer::Data(buffer),
node::Buffer::Length(buffer));
}
frida_bus_post(wrapper->GetHandle<FridaBus>(), *message, data);
g_bytes_unref(data);
}
Local<Value> Bus::TransformSignal(const gchar* name, guint index,
const GValue* value, gpointer user_data) {
if (index != 0 || strcmp(name, "message") != 0)
return Local<Value>();
auto runtime = static_cast<Runtime*>(user_data);
auto json = Nan::New(g_value_get_string(value)).ToLocalChecked();
return runtime->ValueFromJson(json);
}
void Bus::OnConnect(const gchar* name, gpointer user_data) {
auto runtime = static_cast<Runtime*>(user_data);
if (ShouldStayAliveToEmit(name))
runtime->GetUVContext()->IncreaseUsage();
}
void Bus::OnDisconnect(const gchar* name, gpointer user_data) {
auto runtime = static_cast<Runtime*>(user_data);
if (ShouldStayAliveToEmit(name))
runtime->GetUVContext()->DecreaseUsage();
}
bool Bus::ShouldStayAliveToEmit(const gchar* name) {
return strcmp(name, "detached") == 0 || strcmp(name, "message") == 0;
}
}
-33
View File
@@ -1,33 +0,0 @@
#ifndef FRIDANODE_BUS_H
#define FRIDANODE_BUS_H
#include "glib_object.h"
#include <frida-core.h>
namespace frida {
class Bus : public GLibObject {
public:
static void Init(v8::Local<v8::Object> exports, Runtime* runtime);
static v8::Local<v8::Object> New(gpointer handle, Runtime* runtime);
private:
explicit Bus(FridaBus* handle, Runtime* runtime);
~Bus();
static NAN_METHOD(New);
static NAN_METHOD(Attach);
static NAN_METHOD(Post);
static v8::Local<v8::Value> TransformSignal(const gchar* name, guint index,
const GValue* value, gpointer user_data);
static void OnConnect(const gchar* signal, gpointer user_data);
static void OnDisconnect(const gchar* signal, gpointer user_data);
static bool ShouldStayAliveToEmit(const gchar* signal);
};
}
#endif
-117
View File
@@ -1,117 +0,0 @@
#include "cancellable.h"
#include "signals.h"
#define CANCELLABLE_DATA_TEMPLATE "cancellable:tpl"
using v8::DEFAULT;
using v8::External;
using v8::Function;
using v8::FunctionTemplate;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::Persistent;
using v8::ReadOnly;
using v8::Value;
namespace frida {
Cancellable::Cancellable(GCancellable* handle, Runtime* runtime)
: GLibObject(handle, runtime) {
g_object_ref(handle_);
}
Cancellable::~Cancellable() {
g_object_unref(handle_);
}
void Cancellable::Init(Local<Object> exports, Runtime* runtime) {
auto isolate = Isolate::GetCurrent();
auto name = Nan::New("Cancellable").ToLocalChecked();
auto tpl = CreateTemplate(name, Cancellable::New, runtime);
auto instance_tpl = tpl->InstanceTemplate();
auto data = Local<Value>();
Nan::SetAccessor(instance_tpl, Nan::New("isCancelled").ToLocalChecked(),
IsCancelled, 0, data, DEFAULT, ReadOnly);
Nan::SetPrototypeMethod(tpl, "throwIfCancelled", ThrowIfCancelled);
Nan::SetPrototypeMethod(tpl, "cancel", Cancel);
auto ctor = Nan::GetFunction(tpl).ToLocalChecked();
Nan::Set(exports, name, ctor);
runtime->SetDataPointer(CANCELLABLE_DATA_TEMPLATE,
new Persistent<FunctionTemplate>(isolate, tpl));
}
GCancellable* Cancellable::TryParse(Local<Value> value, Runtime* runtime) {
if (!value->IsObject())
return NULL;
auto impl = Nan::Get(value.As<Object>(), Nan::New("impl").ToLocalChecked())
.ToLocalChecked();
if (!HasInstance(impl, runtime))
return NULL;
return ObjectWrap::Unwrap<Cancellable>(impl.As<Object>())
->GetHandle<GCancellable>();
}
bool Cancellable::HasInstance(Local<Value> value, Runtime* runtime) {
auto tpl = Nan::New<FunctionTemplate>(
*static_cast<Persistent<FunctionTemplate>*>(
runtime->GetDataPointer(CANCELLABLE_DATA_TEMPLATE)));
return tpl->HasInstance(value);
}
NAN_METHOD(Cancellable::New) {
if (!info.IsConstructCall()) {
Nan::ThrowError("Use the `new` keyword to create a new instance");
return;
}
auto runtime = GetRuntimeFromConstructorArgs(info);
auto handle = g_cancellable_new();
auto wrapper = new Cancellable(handle, runtime);
auto obj = info.This();
wrapper->Wrap(obj);
auto signals_obj = Signals::New(handle, runtime);
Nan::Set(obj, Nan::New("signals").ToLocalChecked(), signals_obj);
g_object_unref(handle);
info.GetReturnValue().Set(obj);
}
NAN_PROPERTY_GETTER(Cancellable::IsCancelled) {
auto handle = ObjectWrap::Unwrap<Cancellable>(
info.Holder())->GetHandle<GCancellable>();
info.GetReturnValue().Set(
static_cast<bool>(g_cancellable_is_cancelled(handle)));
}
NAN_METHOD(Cancellable::ThrowIfCancelled) {
auto handle = ObjectWrap::Unwrap<Cancellable>(
info.Holder())->GetHandle<GCancellable>();
GError* error = NULL;
if (g_cancellable_set_error_if_cancelled(handle, &error)) {
Nan::ThrowError(Nan::Error(error->message));
g_error_free(error);
}
}
NAN_METHOD(Cancellable::Cancel) {
auto handle = ObjectWrap::Unwrap<Cancellable>(
info.Holder())->GetHandle<GCancellable>();
g_cancellable_cancel(handle);
}
}
-31
View File
@@ -1,31 +0,0 @@
#ifndef FRIDANODE_CANCELLABLE_H
#define FRIDANODE_CANCELLABLE_H
#include "glib_object.h"
#include <gio/gio.h>
namespace frida {
class Cancellable : public GLibObject {
public:
static void Init(v8::Local<v8::Object> exports, Runtime* runtime);
static GCancellable* TryParse(v8::Local<v8::Value> value, Runtime* runtime);
static bool HasInstance(v8::Local<v8::Value> value, Runtime* runtime);
private:
explicit Cancellable(GCancellable* handle, Runtime* runtime);
~Cancellable();
static NAN_METHOD(New);
static NAN_PROPERTY_GETTER(IsCancelled);
static NAN_METHOD(ThrowIfCancelled);
static NAN_METHOD(Cancel);
};
}
#endif
-150
View File
@@ -1,150 +0,0 @@
#include "child.h"
#define CHILD_DATA_CONSTRUCTOR "child:ctor"
using v8::DEFAULT;
using v8::External;
using v8::Function;
using v8::Integer;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::Persistent;
using v8::ReadOnly;
using v8::Value;
namespace frida {
Child::Child(FridaChild* handle, Runtime* runtime)
: GLibObject(handle, runtime) {
g_object_ref(handle_);
}
Child::~Child() {
g_object_unref(handle_);
}
void Child::Init(Local<Object> exports, Runtime* runtime) {
auto isolate = Isolate::GetCurrent();
auto name = Nan::New("Child").ToLocalChecked();
auto tpl = CreateTemplate(name, Child::New, runtime);
auto instance_tpl = tpl->InstanceTemplate();
auto data = Local<Value>();
Nan::SetAccessor(instance_tpl, Nan::New("envp").ToLocalChecked(),
GetEnvp, 0, data, DEFAULT, ReadOnly);
Nan::SetAccessor(instance_tpl, Nan::New("argv").ToLocalChecked(),
GetArgv, 0, data, DEFAULT, ReadOnly);
Nan::SetAccessor(instance_tpl, Nan::New("path").ToLocalChecked(),
GetPath, 0, data, DEFAULT, ReadOnly);
Nan::SetAccessor(instance_tpl, Nan::New("identifier").ToLocalChecked(),
GetIdentifier, 0, data, DEFAULT, ReadOnly);
Nan::SetAccessor(instance_tpl, Nan::New("origin").ToLocalChecked(),
GetOrigin, 0, data, DEFAULT, ReadOnly);
Nan::SetAccessor(instance_tpl, Nan::New("parentPid").ToLocalChecked(),
GetParentPid, 0, data, DEFAULT, ReadOnly);
Nan::SetAccessor(instance_tpl, Nan::New("pid").ToLocalChecked(),
GetPid, 0, data, DEFAULT, ReadOnly);
auto ctor = Nan::GetFunction(tpl).ToLocalChecked();
Nan::Set(exports, name, ctor);
runtime->SetDataPointer(CHILD_DATA_CONSTRUCTOR,
new Persistent<Function>(isolate, ctor));
}
Local<Object> Child::New(gpointer handle, Runtime* runtime) {
auto ctor = Nan::New<Function>(
*static_cast<Persistent<Function>*>(
runtime->GetDataPointer(CHILD_DATA_CONSTRUCTOR)));
const int argc = 1;
Local<Value> argv[argc] = { Nan::New<External>(handle) };
return Nan::NewInstance(ctor, argc, argv).ToLocalChecked();
}
NAN_METHOD(Child::New) {
if (!info.IsConstructCall()) {
Nan::ThrowError("Use the `new` keyword to create a new instance");
return;
}
if (info.Length() != 1 || !info[0]->IsExternal()) {
Nan::ThrowTypeError("Bad argument, expected raw handle");
return;
}
auto runtime = GetRuntimeFromConstructorArgs(info);
auto handle = static_cast<FridaChild*>(
Local<External>::Cast(info[0])->Value());
auto wrapper = new Child(handle, runtime);
auto obj = info.This();
wrapper->Wrap(obj);
info.GetReturnValue().Set(obj);
}
NAN_PROPERTY_GETTER(Child::GetPid) {
auto handle = ObjectWrap::Unwrap<Child>(
info.Holder())->GetHandle<FridaChild>();
info.GetReturnValue().Set(Nan::New<Integer>(frida_child_get_pid(handle)));
}
NAN_PROPERTY_GETTER(Child::GetParentPid) {
auto handle = ObjectWrap::Unwrap<Child>(
info.Holder())->GetHandle<FridaChild>();
info.GetReturnValue().Set(
Nan::New<Integer>(frida_child_get_parent_pid(handle)));
}
NAN_PROPERTY_GETTER(Child::GetOrigin) {
auto handle = ObjectWrap::Unwrap<Child>(
info.Holder())->GetHandle<FridaChild>();
info.GetReturnValue().Set(Runtime::ValueFromEnum(
frida_child_get_origin(handle), FRIDA_TYPE_CHILD_ORIGIN));
}
NAN_PROPERTY_GETTER(Child::GetIdentifier) {
auto handle = ObjectWrap::Unwrap<Child>(
info.Holder())->GetHandle<FridaChild>();
auto identifier = frida_child_get_identifier(handle);
if (identifier != NULL)
info.GetReturnValue().Set(Nan::New(identifier).ToLocalChecked());
else
info.GetReturnValue().Set(Nan::Null());
}
NAN_PROPERTY_GETTER(Child::GetPath) {
auto handle = ObjectWrap::Unwrap<Child>(
info.Holder())->GetHandle<FridaChild>();
auto path = frida_child_get_path(handle);
if (path != NULL)
info.GetReturnValue().Set(Nan::New(path).ToLocalChecked());
else
info.GetReturnValue().Set(Nan::Null());
}
NAN_PROPERTY_GETTER(Child::GetArgv) {
auto handle = ObjectWrap::Unwrap<Child>(
info.Holder())->GetHandle<FridaChild>();
gint length;
auto argv = frida_child_get_argv(handle, &length);
info.GetReturnValue().Set(Runtime::ValueFromStrv(argv, length));
}
NAN_PROPERTY_GETTER(Child::GetEnvp) {
auto handle = ObjectWrap::Unwrap<Child>(
info.Holder())->GetHandle<FridaChild>();
gint length;
auto envp = frida_child_get_envp(handle, &length);
info.GetReturnValue().Set(Runtime::ValueFromEnvp(envp, length));
}
}
-32
View File
@@ -1,32 +0,0 @@
#ifndef FRIDANODE_CHILD_H
#define FRIDANODE_CHILD_H
#include "glib_object.h"
#include <frida-core.h>
namespace frida {
class Child : public GLibObject {
public:
static void Init(v8::Local<v8::Object> exports, Runtime* runtime);
static v8::Local<v8::Object> New(gpointer handle, Runtime* runtime);
private:
explicit Child(FridaChild* handle, Runtime* runtime);
~Child();
static NAN_METHOD(New);
static NAN_PROPERTY_GETTER(GetPid);
static NAN_PROPERTY_GETTER(GetParentPid);
static NAN_PROPERTY_GETTER(GetOrigin);
static NAN_PROPERTY_GETTER(GetIdentifier);
static NAN_PROPERTY_GETTER(GetPath);
static NAN_PROPERTY_GETTER(GetArgv);
static NAN_PROPERTY_GETTER(GetEnvp);
};
}
#endif
-122
View File
@@ -1,122 +0,0 @@
#include "crash.h"
#define CRASH_DATA_CONSTRUCTOR "crash:ctor"
using v8::DEFAULT;
using v8::External;
using v8::Function;
using v8::Integer;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::Persistent;
using v8::ReadOnly;
using v8::Value;
namespace frida {
Crash::Crash(FridaCrash* handle, Runtime* runtime)
: GLibObject(handle, runtime) {
g_object_ref(handle_);
}
Crash::~Crash() {
g_object_unref(handle_);
}
void Crash::Init(Local<Object> exports, Runtime* runtime) {
auto isolate = Isolate::GetCurrent();
auto name = Nan::New("Crash").ToLocalChecked();
auto tpl = CreateTemplate(name, Crash::New, runtime);
auto instance_tpl = tpl->InstanceTemplate();
auto data = Local<Value>();
Nan::SetAccessor(instance_tpl, Nan::New("parameters").ToLocalChecked(),
GetParameters, 0, data, DEFAULT, ReadOnly);
Nan::SetAccessor(instance_tpl, Nan::New("report").ToLocalChecked(),
GetReport, 0, data, DEFAULT, ReadOnly);
Nan::SetAccessor(instance_tpl, Nan::New("summary").ToLocalChecked(),
GetSummary, 0, data, DEFAULT, ReadOnly);
Nan::SetAccessor(instance_tpl, Nan::New("processName").ToLocalChecked(),
GetProcessName, 0, data, DEFAULT, ReadOnly);
Nan::SetAccessor(instance_tpl, Nan::New("pid").ToLocalChecked(),
GetPid, 0, data, DEFAULT, ReadOnly);
auto ctor = Nan::GetFunction(tpl).ToLocalChecked();
Nan::Set(exports, name, ctor);
runtime->SetDataPointer(CRASH_DATA_CONSTRUCTOR,
new Persistent<Function>(isolate, ctor));
}
Local<Object> Crash::New(gpointer handle, Runtime* runtime) {
auto ctor = Nan::New<Function>(
*static_cast<Persistent<Function>*>(
runtime->GetDataPointer(CRASH_DATA_CONSTRUCTOR)));
const int argc = 1;
Local<Value> argv[argc] = { Nan::New<External>(handle) };
return Nan::NewInstance(ctor, argc, argv).ToLocalChecked();
}
NAN_METHOD(Crash::New) {
if (!info.IsConstructCall()) {
Nan::ThrowError("Use the `new` keyword to create a new instance");
return;
}
if (info.Length() != 1 || !info[0]->IsExternal()) {
Nan::ThrowTypeError("Bad argument, expected raw handle");
return;
}
auto runtime = GetRuntimeFromConstructorArgs(info);
auto handle = static_cast<FridaCrash*>(
Local<External>::Cast(info[0])->Value());
auto wrapper = new Crash(handle, runtime);
auto obj = info.This();
wrapper->Wrap(obj);
info.GetReturnValue().Set(obj);
}
NAN_PROPERTY_GETTER(Crash::GetPid) {
auto handle = ObjectWrap::Unwrap<Crash>(
info.Holder())->GetHandle<FridaCrash>();
info.GetReturnValue().Set(Nan::New<Integer>(frida_crash_get_pid(handle)));
}
NAN_PROPERTY_GETTER(Crash::GetProcessName) {
auto handle = ObjectWrap::Unwrap<Crash>(
info.Holder())->GetHandle<FridaCrash>();
info.GetReturnValue().Set(
Nan::New(frida_crash_get_process_name(handle)).ToLocalChecked());
}
NAN_PROPERTY_GETTER(Crash::GetSummary) {
auto handle = ObjectWrap::Unwrap<Crash>(
info.Holder())->GetHandle<FridaCrash>();
info.GetReturnValue().Set(
Nan::New(frida_crash_get_summary(handle)).ToLocalChecked());
}
NAN_PROPERTY_GETTER(Crash::GetReport) {
auto handle = ObjectWrap::Unwrap<Crash>(
info.Holder())->GetHandle<FridaCrash>();
info.GetReturnValue().Set(
Nan::New(frida_crash_get_report(handle)).ToLocalChecked());
}
NAN_PROPERTY_GETTER(Crash::GetParameters) {
auto handle = ObjectWrap::Unwrap<Crash>(
info.Holder())->GetHandle<FridaCrash>();
GHashTable* parameters = frida_crash_get_parameters(handle);
info.GetReturnValue().Set(Runtime::ValueFromParametersDict(parameters));
}
}
-30
View File
@@ -1,30 +0,0 @@
#ifndef FRIDANODE_CRASH_H
#define FRIDANODE_CRASH_H
#include "glib_object.h"
#include <frida-core.h>
namespace frida {
class Crash : public GLibObject {
public:
static void Init(v8::Local<v8::Object> exports, Runtime* runtime);
static v8::Local<v8::Object> New(gpointer handle, Runtime* runtime);
private:
explicit Crash(FridaCrash* handle, Runtime* runtime);
~Crash();
static NAN_METHOD(New);
static NAN_PROPERTY_GETTER(GetPid);
static NAN_PROPERTY_GETTER(GetProcessName);
static NAN_PROPERTY_GETTER(GetSummary);
static NAN_PROPERTY_GETTER(GetReport);
static NAN_PROPERTY_GETTER(GetParameters);
};
}
#endif
-1350
View File
File diff suppressed because it is too large. Load diff
-56
View File
@@ -1,56 +0,0 @@
#ifndef FRIDANODE_DEVICE_H
#define FRIDANODE_DEVICE_H
#include "glib_object.h"
#include <frida-core.h>
namespace frida {
class Device : public GLibObject {
public:
static void Init(v8::Local<v8::Object> exports, Runtime* runtime);
static v8::Local<v8::Object> New(gpointer handle, Runtime* runtime);
private:
Device(FridaDevice* handle, Runtime* runtime);
~Device();
static NAN_METHOD(New);
static NAN_PROPERTY_GETTER(GetId);
static NAN_PROPERTY_GETTER(GetName);
static NAN_PROPERTY_GETTER(GetIcon);
static NAN_PROPERTY_GETTER(GetType);
static NAN_PROPERTY_GETTER(GetBus);
static NAN_PROPERTY_GETTER(IsLost);
static NAN_METHOD(QuerySystemParameters);
static NAN_METHOD(GetFrontmostApplication);
static NAN_METHOD(EnumerateApplications);
static NAN_METHOD(EnumerateProcesses);
static NAN_METHOD(EnableSpawnGating);
static NAN_METHOD(DisableSpawnGating);
static NAN_METHOD(EnumeratePendingSpawn);
static NAN_METHOD(EnumeratePendingChildren);
static NAN_METHOD(Spawn);
static NAN_METHOD(Input);
static NAN_METHOD(Resume);
static NAN_METHOD(Kill);
static NAN_METHOD(Attach);
static NAN_METHOD(InjectLibraryFile);
static NAN_METHOD(InjectLibraryBlob);
static NAN_METHOD(OpenChannel);
static NAN_METHOD(OpenService);
static NAN_METHOD(Unpair);
static v8::Local<v8::Value> TransformSignal(const gchar* name, guint index,
const GValue* value, gpointer user_data);
static void OnConnect(const gchar* signal, gpointer user_data);
static void OnDisconnect(const gchar* signal, gpointer user_data);
static bool ShouldStayAliveToEmit(const gchar* signal);
};
}
#endif
-362
View File
@@ -1,362 +0,0 @@
#include "device_manager.h"
#include "device.h"
#include "operation.h"
#include "signals.h"
#include <cstring>
#define DEVICE_MANAGER_DATA_WRAPPERS "device_manager:wrappers"
using std::strcmp;
using v8::Array;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Value;
namespace frida {
DeviceManager::DeviceManager(FridaDeviceManager* handle, Runtime* runtime)
: GLibObject(handle, runtime) {
g_object_ref(handle_);
runtime_->SetDataPointer(DEVICE_MANAGER_DATA_WRAPPERS, g_slist_prepend(
static_cast<GSList*>(
runtime_->GetDataPointer(DEVICE_MANAGER_DATA_WRAPPERS)), this));
}
DeviceManager::~DeviceManager() {
runtime_->SetDataPointer(DEVICE_MANAGER_DATA_WRAPPERS, g_slist_remove(
static_cast<GSList*>(
runtime_->GetDataPointer(DEVICE_MANAGER_DATA_WRAPPERS)), this));
frida_unref(handle_);
}
void DeviceManager::Init(Local<Object> exports, Runtime* runtime) {
Local<String> name = Nan::New("DeviceManager").ToLocalChecked();
auto tpl = CreateTemplate(name, DeviceManager::New, runtime);
Nan::SetPrototypeMethod(tpl, "close", Close);
Nan::SetPrototypeMethod(tpl, "enumerateDevices", EnumerateDevices);
Nan::SetPrototypeMethod(tpl, "addRemoteDevice", AddRemoteDevice);
Nan::SetPrototypeMethod(tpl, "removeRemoteDevice", RemoveRemoteDevice);
Nan::Set(exports, name, Nan::GetFunction(tpl).ToLocalChecked());
}
void DeviceManager::Dispose(Runtime* runtime) {
auto wrappers = static_cast<GSList*>(
runtime->GetDataPointer(DEVICE_MANAGER_DATA_WRAPPERS));
while (wrappers != NULL) {
auto wrapper = static_cast<DeviceManager*>(wrappers->data);
frida_device_manager_close_sync(wrapper->GetHandle<FridaDeviceManager>(),
NULL, NULL);
wrappers = g_slist_delete_link(wrappers, wrappers);
}
runtime->SetDataPointer(DEVICE_MANAGER_DATA_WRAPPERS, NULL);
}
NAN_METHOD(DeviceManager::New) {
if (!info.IsConstructCall()) {
Nan::ThrowError("Use the `new` keyword to create a new instance");
return;
}
auto runtime = GetRuntimeFromConstructorArgs(info);
auto handle = frida_device_manager_new();
auto wrapper = new DeviceManager(handle, runtime);
auto obj = info.This();
wrapper->Wrap(obj);
auto signals_obj = Signals::New(handle, runtime, TransformDeviceSignals,
runtime);
Nan::Set(obj, Nan::New("signals").ToLocalChecked(), signals_obj);
g_object_unref(handle);
auto signals_wrapper = ObjectWrap::Unwrap<Signals>(signals_obj);
signals_wrapper->SetConnectCallback(OnConnect, runtime);
signals_wrapper->SetDisconnectCallback(OnDisconnect, runtime);
info.GetReturnValue().Set(obj);
}
namespace {
class CloseOperation : public Operation<FridaDeviceManager> {
protected:
void Begin() {
frida_device_manager_close(handle_, cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
frida_device_manager_close_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
return Nan::Undefined();
}
};
}
NAN_METHOD(DeviceManager::Close) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<DeviceManager>(info.Holder());
auto operation = new CloseOperation();
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
namespace {
class EnumerateDevicesOperation : public Operation<FridaDeviceManager> {
protected:
void Begin() {
frida_device_manager_enumerate_devices(handle_, cancellable_, OnReady,
this);
}
void End(GAsyncResult* result, GError** error) {
devices_ = frida_device_manager_enumerate_devices_finish(handle_, result,
error);
}
Local<Value> Result(Isolate* isolate) {
auto size = frida_device_list_size(devices_);
Local<Array> devices = Nan::New<Array>(size);
for (auto i = 0; i != size; i++) {
auto handle = frida_device_list_get(devices_, i);
auto device = Device::New(handle, runtime_);
Nan::Set(devices, i, device);
g_object_unref(handle);
}
frida_unref(devices_);
return devices;
}
private:
FridaDeviceList* devices_;
};
}
NAN_METHOD(DeviceManager::EnumerateDevices) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<DeviceManager>(info.Holder());
auto operation = new EnumerateDevicesOperation();
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
namespace {
class AddRemoteDeviceOperation : public Operation<FridaDeviceManager> {
public:
AddRemoteDeviceOperation(gchar* address, FridaRemoteDeviceOptions* options)
: address_(address),
options_(options) {
}
~AddRemoteDeviceOperation() {
g_object_unref(options_);
g_free(address_);
}
protected:
void Begin() {
frida_device_manager_add_remote_device(handle_, address_, options_,
cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
device_ = frida_device_manager_add_remote_device_finish(handle_, result,
error);
}
Local<Value> Result(Isolate* isolate) {
auto wrapper = Device::New(device_, runtime_);
g_object_unref(device_);
return wrapper;
}
private:
gchar* address_;
FridaRemoteDeviceOptions* options_;
FridaDevice* device_;
};
}
NAN_METHOD(DeviceManager::AddRemoteDevice) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<DeviceManager>(info.Holder());
if (info.Length() < 5) {
Nan::ThrowTypeError("Missing one or more arguments");
return;
}
auto address_value = info[0];
auto certificate_value = info[1];
auto origin_value = info[2];
auto token_value = info[3];
auto keepalive_interval_value = info[4];
if (!address_value->IsString()) {
Nan::ThrowTypeError("Bad argument, 'address' must be a string");
return;
}
Nan::Utf8String address(address_value);
auto options = frida_remote_device_options_new();
bool valid = true;
if (!certificate_value->IsNull()) {
GTlsCertificate* certificate;
valid = Runtime::ValueToCertificate(certificate_value, &certificate);
if (valid) {
frida_remote_device_options_set_certificate(options, certificate);
g_object_unref(certificate);
}
}
if (valid && !origin_value->IsNull()) {
if (origin_value->IsString()) {
Nan::Utf8String origin(origin_value);
frida_remote_device_options_set_origin(options, *origin);
} else {
Nan::ThrowTypeError("Bad argument, 'origin' must be a string");
valid = false;
}
}
if (valid && !token_value->IsNull()) {
if (token_value->IsString()) {
Nan::Utf8String token(token_value);
frida_remote_device_options_set_token(options, *token);
} else {
Nan::ThrowTypeError("Bad argument, 'token' must be a string");
valid = false;
}
}
if (valid && !keepalive_interval_value->IsNull()) {
if (keepalive_interval_value->IsNumber()) {
auto keepalive_interval =
Nan::To<int32_t>(keepalive_interval_value).FromMaybe(-1);
if (keepalive_interval >= -1) {
frida_remote_device_options_set_keepalive_interval(options,
keepalive_interval);
} else {
Nan::ThrowTypeError("Bad argument, invalid 'keepaliveInterval'");
valid = false;
}
} else {
Nan::ThrowTypeError("Bad argument, 'keepaliveInterval' must be a number");
valid = false;
}
}
if (!valid) {
g_object_unref(options);
return;
}
auto operation = new AddRemoteDeviceOperation(g_strdup(*address), options);
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
namespace {
class RemoveRemoteDeviceOperation : public Operation<FridaDeviceManager> {
public:
RemoveRemoteDeviceOperation(gchar* address) : address_(address) {
}
~RemoveRemoteDeviceOperation() {
g_free(address_);
}
protected:
void Begin() {
frida_device_manager_remove_remote_device(handle_, address_, cancellable_,
OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
frida_device_manager_remove_remote_device_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
return Nan::Undefined();
}
private:
gchar* address_;
};
}
NAN_METHOD(DeviceManager::RemoveRemoteDevice) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<DeviceManager>(info.Holder());
if (info.Length() < 1 || !info[0]->IsString()) {
Nan::ThrowTypeError("Expected an address");
return;
}
Nan::Utf8String address(info[0]);
auto operation = new RemoveRemoteDeviceOperation(g_strdup(*address));
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
Local<Value> DeviceManager::TransformDeviceSignals(const gchar* name,
guint index, const GValue* value, gpointer user_data) {
if (index == 0 && (strcmp(name, "added") == 0 ||
strcmp(name, "removed") == 0)) {
auto runtime = static_cast<Runtime*>(user_data);
return Device::New(g_value_get_object(value), runtime);
}
return Local<Value>();
}
void DeviceManager::OnConnect(const gchar* name, gpointer user_data) {
auto runtime = static_cast<Runtime*>(user_data);
if (ShouldStayAliveToEmit(name))
runtime->GetUVContext()->IncreaseUsage();
}
void DeviceManager::OnDisconnect(const gchar* name, gpointer user_data) {
auto runtime = static_cast<Runtime*>(user_data);
if (ShouldStayAliveToEmit(name))
runtime->GetUVContext()->DecreaseUsage();
}
bool DeviceManager::ShouldStayAliveToEmit(const gchar* name) {
return strcmp(name, "added") == 0 ||
strcmp(name, "removed") == 0 ||
strcmp(name, "changed") == 0;
}
}
-35
View File
@@ -1,35 +0,0 @@
#ifndef FRIDANODE_DEVICE_MANAGER_H
#define FRIDANODE_DEVICE_MANAGER_H
#include "glib_object.h"
#include <frida-core.h>
namespace frida {
class DeviceManager : public GLibObject {
public:
static void Init(v8::Local<v8::Object> exports, Runtime* runtime);
static void Dispose(Runtime* runtime);
private:
DeviceManager(FridaDeviceManager* handle, Runtime* runtime);
~DeviceManager();
static NAN_METHOD(New);
static NAN_METHOD(Close);
static NAN_METHOD(EnumerateDevices);
static NAN_METHOD(AddRemoteDevice);
static NAN_METHOD(RemoveRemoteDevice);
static v8::Local<v8::Value> TransformDeviceSignals(const gchar* name,
guint index, const GValue* value, gpointer user_data);
static void OnConnect(const gchar* signal, gpointer user_data);
static void OnDisconnect(const gchar* signal, gpointer user_data);
static bool ShouldStayAliveToEmit(const gchar* signal);
};
}
#endif
-171
View File
@@ -1,171 +0,0 @@
#include "endpoint_parameters.h"
#include "authentication.h"
#define ENDPOINT_PARAMETERS_DATA_TEMPLATE "endpoint_parameters:tpl"
using v8::Function;
using v8::FunctionTemplate;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::Persistent;
using v8::Value;
namespace frida {
EndpointParameters::EndpointParameters(FridaEndpointParameters* handle,
Runtime* runtime)
: GLibObject(handle, runtime) {
g_object_ref(handle_);
}
EndpointParameters::~EndpointParameters() {
g_object_unref(handle_);
}
void EndpointParameters::Init(Local<Object> exports, Runtime* runtime) {
auto isolate = Isolate::GetCurrent();
auto name = Nan::New("EndpointParameters").ToLocalChecked();
auto tpl = CreateTemplate(name, EndpointParameters::New, runtime);
auto ctor = Nan::GetFunction(tpl).ToLocalChecked();
Nan::Set(exports, name, ctor);
runtime->SetDataPointer(ENDPOINT_PARAMETERS_DATA_TEMPLATE,
new Persistent<FunctionTemplate>(isolate, tpl));
}
FridaEndpointParameters* EndpointParameters::TryParse(Local<Value> value,
Runtime* runtime) {
if (!value->IsObject())
return NULL;
auto impl = Nan::Get(value.As<Object>(), Nan::New("impl").ToLocalChecked())
.ToLocalChecked();
if (!HasInstance(impl, runtime))
return NULL;
return ObjectWrap::Unwrap<EndpointParameters>(
impl.As<Object>())->GetHandle<FridaEndpointParameters>();
}
bool EndpointParameters::HasInstance(Local<Value> value, Runtime* runtime) {
auto tpl = Nan::New<FunctionTemplate>(
*static_cast<Persistent<FunctionTemplate>*>(
runtime->GetDataPointer(ENDPOINT_PARAMETERS_DATA_TEMPLATE)));
return tpl->HasInstance(value);
}
NAN_METHOD(EndpointParameters::New) {
if (!info.IsConstructCall()) {
Nan::ThrowError("Use the `new` keyword to create a new instance");
return;
}
if (info.Length() < 7) {
Nan::ThrowTypeError("Missing one or more arguments");
return;
}
auto runtime = GetRuntimeFromConstructorArgs(info);
auto address_value = info[0];
auto port_value = info[1];
auto certificate_value = info[2];
auto origin_value = info[3];
auto auth_token_value = info[4];
auto auth_callback_value = info[5];
auto asset_root_value = info[6];
gchar* address = NULL;
guint16 port = 0;
GTlsCertificate* certificate = NULL;
gchar* origin = NULL;
FridaAuthenticationService* auth_service = NULL;
GFile* asset_root = NULL;
bool valid = true;
if (!address_value->IsNull()) {
if (address_value->IsString()) {
Nan::Utf8String str(address_value);
address = g_strdup(*str);
} else {
Nan::ThrowTypeError("Bad argument, 'address' must be a string");
valid = false;
}
}
if (valid && !port_value->IsNull()) {
auto val = Nan::To<int32_t>(port_value).FromMaybe(-1);
if (val >= 0 && val <= 65535) {
port = val;
} else {
Nan::ThrowTypeError("Bad argument, 'port' must be a valid port number");
valid = false;
}
}
if (valid && !certificate_value->IsNull()) {
valid = Runtime::ValueToCertificate(certificate_value, &certificate);
}
if (valid && !origin_value->IsNull()) {
if (origin_value->IsString()) {
Nan::Utf8String str(origin_value);
origin = g_strdup(*str);
} else {
Nan::ThrowTypeError("Bad argument, 'origin' must be a string");
valid = false;
}
}
if (valid && !auth_token_value->IsNull()) {
if (auth_token_value->IsString()) {
Nan::Utf8String auth_token(auth_token_value);
auth_service = FRIDA_AUTHENTICATION_SERVICE(
frida_static_authentication_service_new(*auth_token));
} else {
Nan::ThrowTypeError("Bad argument, 'authToken' must be a string");
valid = false;
}
} else if (valid && !auth_callback_value->IsNull()) {
if (auth_callback_value->IsFunction()) {
auto auth_callback = Local<Function>::Cast(auth_callback_value);
auth_service =
frida_node_authentication_service_new(auth_callback, runtime);
} else {
Nan::ThrowTypeError("Bad argument, 'authCallback' must be a function");
valid = false;
}
}
if (valid && !asset_root_value->IsNull()) {
if (asset_root_value->IsString()) {
Nan::Utf8String str(asset_root_value);
asset_root = g_file_new_for_path(*str);
} else {
Nan::ThrowTypeError("Bad argument, 'assetRoot' must be a string");
valid = false;
}
}
if (valid) {
auto handle = frida_endpoint_parameters_new(address, port, certificate,
origin, auth_service, asset_root);
auto wrapper = new EndpointParameters(handle, runtime);
g_object_unref(handle);
auto obj = info.This();
wrapper->Wrap(obj);
info.GetReturnValue().Set(obj);
}
g_clear_object(&asset_root);
g_clear_object(&auth_service);
g_free(origin);
g_clear_object(&certificate);
g_free(address);
}
}
-28
View File
@@ -1,28 +0,0 @@
#ifndef FRIDANODE_ENDPOINT_PARAMETERS_H
#define FRIDANODE_ENDPOINT_PARAMETERS_H
#include "glib_object.h"
#include <frida-core.h>
namespace frida {
class EndpointParameters : public GLibObject {
public:
static void Init(v8::Local<v8::Object> exports, Runtime* runtime);
static FridaEndpointParameters* TryParse(v8::Local<v8::Value> value,
Runtime* runtime);
static bool HasInstance(v8::Local<v8::Value> value, Runtime* runtime);
private:
explicit EndpointParameters(FridaEndpointParameters* handle,
Runtime* runtime);
~EndpointParameters();
static NAN_METHOD(New);
};
}
#endif
View File
Whitespace-only changes.
+4
View File
@@ -0,0 +1,4 @@
from .cli import main
if __name__ == "__main__":
main()
File diff suppressed because it is too large. Load diff
+100
View File
@@ -0,0 +1,100 @@
type SignalTransformer<
Source extends SignalHandler,
Target extends SignalHandler
> = (...args: Parameters<Source>) => Parameters<Target>;
type SignalInterceptor<H extends SignalHandler> = (...args: Parameters<H>) => boolean;
interface SignalWrapperOptionsNoTransform<H extends SignalHandler> {
transform?: undefined;
intercept?: SignalInterceptor<H>;
}
interface SignalWrapperOptionsTransform<
Source extends SignalHandler,
Target extends SignalHandler
> {
transform: SignalTransformer<Source, Target>;
intercept?: SignalInterceptor<Target>;
}
type SignalWrapperOptions<
Source extends SignalHandler,
Target extends SignalHandler
> =
| SignalWrapperOptionsNoTransform<Source & Target>
| SignalWrapperOptionsTransform<Source, Target>;
class SignalWrapper<
SourceHandler extends SignalHandler,
TargetHandler extends SignalHandler
> {
#source: Signal<SourceHandler>;
#transform?: SignalTransformer<SourceHandler, TargetHandler>;
#intercept?: SignalInterceptor<any>;
#handlers = new Set<TargetHandler>();
constructor(
source: Signal<SourceHandler>,
options?: SignalWrapperOptions<SourceHandler, TargetHandler>
) {
this.#source = source;
if (options === undefined || options.transform === undefined) {
this.#intercept = options?.intercept;
} else {
this.#transform = options.transform;
this.#intercept = options.intercept;
}
}
connect(handler: TargetHandler): void {
this.#handlers.add(handler);
if (this.#handlers.size === 1) {
this.#source.connect(this.#wrappedHandler);
}
}
disconnect(handler: TargetHandler): void {
this.#handlers.delete(handler);
if (this.#handlers.size === 0) {
this.#source.disconnect(this.#wrappedHandler);
}
}
#wrappedHandler = ((...sourceArgs: Parameters<SourceHandler>) => {
let targetArgs: Parameters<TargetHandler>;
const transform = this.#transform;
if (transform === undefined) {
targetArgs = sourceArgs as unknown as Parameters<TargetHandler>;
} else {
targetArgs = transform(...sourceArgs);
}
const intercept = this.#intercept;
if (intercept !== undefined) {
if (!intercept(...targetArgs)) {
return;
}
}
for (const handler of this.#handlers) {
handler(...targetArgs);
}
}) as SourceHandler;
}
function inspectWrapper(object: any, name: string, properties: string[], depth: number, options: util.InspectOptionsStylized): string {
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);
}
@@ -0,0 +1,78 @@
G_GNUC_UNUSED static gboolean fdn_is_null (napi_env env, napi_value value);
static gboolean fdn_is_undefined_or_null (napi_env env, napi_value value);
static gboolean fdn_is_function (napi_env env, napi_value value);
static gboolean fdn_boolean_from_value (napi_env env, napi_value value, gboolean * b);
static napi_value fdn_boolean_to_value (napi_env env, gboolean b);
static gboolean fdn_size_from_value (napi_env env, napi_value value, gsize * s);
static napi_value fdn_size_to_value (napi_env env, gsize s);
static napi_value fdn_ssize_to_value (napi_env env, gssize s);
static gboolean fdn_int_from_value (napi_env env, napi_value value, gint * i);
static napi_value fdn_int_to_value (napi_env env, gint i);
static gboolean fdn_uint_from_value (napi_env env, napi_value value, guint * u);
static napi_value fdn_uint_to_value (napi_env env, guint u);
static gboolean fdn_uint16_from_value (napi_env env, napi_value value, guint16 * u);
static napi_value fdn_uint16_to_value (napi_env env, guint16 u);
static napi_value fdn_uint32_to_value (napi_env env, guint32 u);
static gboolean fdn_int64_from_value (napi_env env, napi_value value, gint64 * i);
static napi_value fdn_int64_to_value (napi_env env, gint64 i);
static napi_value fdn_uint64_to_value (napi_env env, guint64 u);
static gboolean fdn_ulong_from_value (napi_env env, napi_value value, gulong * u);
static gboolean fdn_double_from_value (napi_env env, napi_value value, gdouble * d);
static napi_value fdn_double_to_value (napi_env env, gdouble d);
static gboolean fdn_enum_from_value (napi_env env, GType enum_type, napi_value value, gint * e);
static napi_value fdn_enum_to_value (napi_env env, GType enum_type, gint e);
static gboolean fdn_utf8_from_value (napi_env env, napi_value value, gchar ** str);
static napi_value fdn_utf8_to_value (napi_env env, const gchar * str);
static gboolean fdn_strv_from_value (napi_env env, napi_value value, gchar *** strv);
static napi_value fdn_strv_to_value (napi_env env, gchar ** strv);
static napi_value fdn_buffer_to_value (napi_env env, const guint8 * data, gsize size);
static gboolean fdn_bytes_from_value (napi_env env, napi_value value, GBytes ** bytes);
static napi_value fdn_bytes_to_value (napi_env env, GBytes * bytes);
static gboolean fdn_vardict_from_value (napi_env env, napi_value value, GHashTable ** vardict);
static napi_value fdn_vardict_to_value (napi_env env, GHashTable * vardict);
static gboolean fdn_variant_from_value (napi_env env, napi_value value, GVariant ** variant);
static napi_value fdn_variant_to_value (napi_env env, GVariant * variant);
static gboolean fdn_gvalue_from_value (napi_env env, GType type, napi_value js_value, GValue * value);
static napi_value fdn_gvalue_to_value (napi_env env, GValue * value);
static gboolean fdn_error_from_value (napi_env env, napi_value value, GError ** error);
static napi_value fdn_error_to_value (napi_env env, GError * error);
static gboolean fdn_file_from_value (napi_env env, napi_value value, GFile ** file);
static napi_value fdn_file_to_value (napi_env env, GFile * file);
static gboolean fdn_tls_certificate_from_value (napi_env env, napi_value value, GTlsCertificate ** certificate);
static napi_value fdn_tls_certificate_to_value (napi_env env, GTlsCertificate * certificate);
static gboolean fdn_options_from_value (napi_env env, GType object_type, napi_value value, gpointer * options);
static napi_value fdn_object_subclass_to_value (napi_env env, GObject * object);
static napi_value fdn_object_new (napi_env env, GObject * handle, napi_ref constructor);
static gboolean fdn_object_wrap (napi_env env, napi_value wrapper, GObject * handle, node_api_nogc_finalize finalizer);
static gboolean fdn_object_unwrap (napi_env env, napi_value wrapper, GType expected_type, GObject ** handle);
static void fdn_object_finalize (napi_env env, void * finalize_data, void * finalize_hint);
static napi_value fdn_object_get_signal (napi_env env, napi_callback_info info, const gchar * name, const gchar * js_storage_name, FdnSignalBehavior behavior);
static napi_value fdn_signal_new (napi_env env, GObject * handle, const gchar * name, FdnSignalBehavior behavior);
static void fdn_signal_register (napi_env env, napi_value exports);
static napi_value fdn_signal_construct (napi_env env, napi_callback_info info);
static void fdn_signal_finalize (napi_env env, void * finalize_data, void * finalize_hint);
static napi_value fdn_signal_connect (napi_env env, napi_callback_info info);
static napi_value fdn_signal_disconnect (napi_env env, napi_callback_info info);
static void fdn_signal_disconnect_closure (FdnSignal * self, FdnSignalClosure * closure);
static gboolean fdn_signal_parse_arguments (napi_env env, napi_callback_info info, FdnSignal ** self, napi_value * js_self, napi_value * handler);
static FdnSignalClosure * fdn_signal_closure_new (napi_env env, FdnSignal * sig, napi_value js_sig, napi_value handler);
static void fdn_signal_closure_finalize (gpointer data, GClosure * closure);
static void fdn_signal_closure_marshal (GClosure * closure, GValue * return_gvalue, guint n_param_values, const GValue * param_values, gpointer invocation_hint, gpointer marshal_data);
static void fdn_signal_closure_deliver (napi_env env, napi_value js_cb, void * context, void * data);
static void fdn_keep_alive_until (napi_env env, napi_value js_object, GObject * handle, FdnIsDestroyedFunc is_destroyed, const gchar * destroy_signal_name);
static void fdn_keep_alive_on_finalize (napi_env env, void * finalize_data, void * finalize_hint);
static void fdn_keep_alive_on_destroy_signal (GObject * handle, gpointer user_data);
static void fdn_keep_alive_on_destroy_signal_handler_detached (gpointer data, GClosure * closure);
static void fdn_keep_alive_schedule_cleanup (FdnKeepAliveContext * context);
static void fdn_keep_alive_on_tsfn_invoke (napi_env env, napi_value js_cb, void * context, void * data);
static void fdn_inherit_val_val (napi_env env, napi_value sub_ctor, napi_value super_ctor, napi_value object_ctor, napi_value set_proto);
G_GNUC_UNUSED static void fdn_inherit_val_ref (napi_env env, napi_value sub_ctor, napi_ref super_ctor, napi_value object_ctor, napi_value set_proto);
static void fdn_inherit_ref_val (napi_env env, napi_ref sub_ctor, napi_value super_ctor, napi_value object_ctor, napi_value set_proto);
static void fdn_inherit_ref_ref (napi_env env, napi_ref sub_ctor, napi_ref super_ctor, napi_value object_ctor, napi_value set_proto);
static gchar * fdn_camel_case_to_kebab_case (const gchar * name);
+57
View File
@@ -0,0 +1,57 @@
typedef enum {
FDN_SIGNAL_ALLOW_EXIT,
FDN_SIGNAL_KEEP_ALIVE
} FdnSignalBehavior;
typedef struct {
GObject * handle;
guint id;
FdnSignalBehavior behavior;
GSList * closures;
} FdnSignal;
typedef enum {
FDN_SIGNAL_CLOSURE_OPEN,
FDN_SIGNAL_CLOSURE_CLOSED,
} FdnSignalClosureState;
typedef struct {
GClosure closure;
FdnSignal * sig;
napi_ref js_sig;
FdnSignalClosureState state;
napi_threadsafe_function tsfn;
napi_ref handler;
gulong handler_id;
} FdnSignalClosure;
typedef enum {
FDN_SIGNAL_CLOSURE_MESSAGE_DESTROY,
FDN_SIGNAL_CLOSURE_MESSAGE_MARSHAL,
} FdnSignalClosureMessageType;
typedef struct {
napi_ref js_sig;
napi_threadsafe_function tsfn;
napi_ref handler;
} FdnSignalClosureMessageDestroy;
typedef struct {
GArray * args;
} FdnSignalClosureMessageMarshal;
typedef struct {
FdnSignalClosureMessageType type;
union {
FdnSignalClosureMessageDestroy destroy;
FdnSignalClosureMessageMarshal marshal;
} payload;
} FdnSignalClosureMessage;
typedef struct {
guint ref_count;
GObject * handle;
gulong signal_handler_id;
} FdnKeepAliveContext;
typedef gboolean (* FdnIsDestroyedFunc) (GObject * handle);
@@ -0,0 +1,13 @@
querySystemParameters
spawn
resume
kill
attach
injectLibraryFile
injectLibraryBlob
enumerateDevices
getDeviceManager
getLocalDevice
getRemoteDevice
getUsbDevice
getDevice
@@ -0,0 +1,157 @@
let sharedDeviceManager: DeviceManager | null = null;
export async function querySystemParameters(cancellable?: Cancellable | null): Promise<SystemParameters> {
const device = await getLocalDevice(cancellable);
return await device.querySystemParameters(cancellable);
}
export async function spawn(program: string | string[], options?: SpawnOptions, cancellable?: Cancellable | null): Promise<number> {
const device = await getLocalDevice(cancellable);
return await device.spawn(program, options, cancellable);
}
export async function resume(target: TargetProcess, cancellable?: Cancellable | null): Promise<void> {
const device = await getLocalDevice(cancellable);
await device.resume(target, cancellable);
}
export async function kill(target: TargetProcess, cancellable?: Cancellable | null): Promise<void> {
const device = await getLocalDevice(cancellable);
await device.kill(target, cancellable);
}
export async function attach(target: TargetProcess, options?: SessionOptions, cancellable?: Cancellable | null): Promise<Session> {
const device = await getLocalDevice(cancellable);
return await device.attach(target, options, cancellable);
}
export async function injectLibraryFile(target: TargetProcess, path: string, entrypoint: string, data: string,
cancellable?: Cancellable | null): Promise<number> {
const device = await getLocalDevice(cancellable);
return await device.injectLibraryFile(target, path, entrypoint, data, cancellable);
}
export async function injectLibraryBlob(target: TargetProcess, blob: Buffer, entrypoint: string, data: string,
cancellable?: Cancellable | null): Promise<number> {
const device = await getLocalDevice(cancellable);
return await device.injectLibraryBlob(target, blob, entrypoint, data, cancellable);
}
export async function enumerateDevices(cancellable?: Cancellable | null): Promise<Device[]> {
const deviceManager = getDeviceManager();
return await deviceManager.enumerateDevices(cancellable);
};
export function getDeviceManager(): DeviceManager {
if (sharedDeviceManager === null) {
sharedDeviceManager = new DeviceManager();
}
return sharedDeviceManager;
}
export function getLocalDevice(cancellable?: Cancellable | null): Promise<Device> {
return getMatchingDevice(device => device.type === DeviceType.Local, {}, cancellable);
}
export function getRemoteDevice(cancellable?: Cancellable | null): Promise<Device> {
return getMatchingDevice(device => device.type === DeviceType.Remote, {}, cancellable);
}
export function getUsbDevice(options?: GetDeviceOptions, cancellable?: Cancellable | null): Promise<Device> {
return getMatchingDevice(device => device.type === DeviceType.Usb, options, cancellable);
}
export function getDevice(id: string, options?: GetDeviceOptions, cancellable?: Cancellable | null): Promise<Device> {
return getMatchingDevice(device => device.id === id, options, cancellable);
}
export interface GetDeviceOptions {
timeout?: number | null;
}
async function getMatchingDevice(predicate: DevicePredicate, options: GetDeviceOptions = {}, cancellable: Cancellable | null = null): Promise<Device> {
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: (device: Device) => void, reject: (error: Error) => void) => {
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: Device): void {
if (predicate(device)) {
onSuccess(device);
}
}
function onSuccess(device: Device): void {
stopMonitoring();
resolve(device);
}
function onError(error: Error): void {
stopMonitoring();
reject(error);
}
function onTimeout(): void {
onError(new Error("Timed out while waiting for device to appear"));
}
function onCancel(): void {
onError(new Error("Operation was cancelled"));
}
function stopMonitoring(): void {
cancellable?.cancelled.disconnect(onCancel);
if (timer !== null) {
clearTimeout(timer);
}
deviceManager.added.disconnect(onDeviceAdded);
}
});
return await getDeviceEventually;
}
async function findMatchingDevice(predicate: DevicePredicate, cancellable?: Cancellable | null): Promise<Device | null> {
const deviceManager = getDeviceManager();
const devices = await deviceManager.enumerateDevices(cancellable);
const matching = devices.filter(predicate);
if (matching.length === 0) {
return null;
}
return matching[0];
}
type DevicePredicate = (device: Device) => boolean;
@@ -0,0 +1,2 @@
import { Minimatch } from "minimatch";
import { Duplex } from "stream";
@@ -0,0 +1,396 @@
const STANDARD_SPAWN_OPTION_NAMES = new Set<keyof SpawnOptions>([
"argv",
"envp",
"env",
"cwd",
"stdio",
]);
interface LogMessage {
type: "log";
level: LogLevel;
payload: string;
}
class ScriptServices implements RpcController {
exportsProxy = new ScriptExportsProxy(this);
#script: Script;
#pendingRequests = new Map<number, (error: Error | null, result?: any) => void>();
#nextRequestId = 1;
constructor(script: Script) {
this.#script = script;
process.nextTick(() => {
script.message.connect(() => {});
});
}
handleMessageIntercept = (message: Message, data: Buffer | null): boolean => {
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: any = message;
const logMessage: LogMessage = opaqueMessage;
this.#script.logHandler(logMessage.level, logMessage.payload);
return false;
}
return true;
};
request(operation: string, params: any[], data: Buffer | null, cancellable: Cancellable | null = null): Promise<any> {
return new Promise((resolve, reject) => {
const id = this.#nextRequestId++;
const complete = (error: Error | null, result?: any): void => {
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(): void {
complete(new Error("Script is destroyed"));
}
function onOperationCancelled(): void {
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: number, operation: RpcOperation, params: any[], data: Buffer | null) {
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 implements ScriptExports {
[name: string]: (...args: any[]) => Promise<any>;
constructor(rpcController: 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: any[]): Promise<any> => {
let cancellable: Cancellable | null = null;
if (args[args.length - 1] instanceof Cancellable) {
cancellable = args.pop();
}
let data: Buffer | null = 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 {}";
}
interface RpcController {
request(operation: string, params: any[], data: ArrayBuffer | null, cancellable: Cancellable | null): Promise<any>;
}
enum RpcOperation {
Ok = "ok",
Error = "error"
}
function isInternalMessage(message: Message): boolean {
return isRpcMessage(message) || isLogMessage(message);
}
function isRpcMessage(message: Message): boolean {
return message.type === MessageType.Send && isRpcSendMessage(message);
}
function isRpcSendMessage(message: SendMessage): boolean {
const payload = message.payload;
if (!Array.isArray(payload)) {
return false;
}
return payload[0] === "frida:rpc";
}
function isLogMessage(message: Message): boolean {
return message.type as string === "log";
}
function log(level: LogLevel, text: string): void {
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<string>([
"then",
"catch",
"finally",
]);
function isReservedMethodName(name: string | number | symbol): boolean {
return reservedMethodNames.has(name.toString());
}
const IO_PRIORITY_DEFAULT = 0;
class IOStreamAdapter extends Duplex {
#impl: IOStream;
#input: InputStream;
#output: OutputStream;
#pending = new Set<Promise<void>>();
#cancellable = new Cancellable();
constructor(impl: IOStream) {
super({});
this.#impl = impl;
this.#input = impl.inputStream;
this.#output = impl.outputStream;
}
async _destroy(error: Error | null, callback: (error: Error | null) => void): Promise<void> {
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: number): void {
const operation = this.#input.read(size, IO_PRIORITY_DEFAULT, this.#cancellable)
.then((data: Buffer): void => {
const isEof = data.length === 0;
if (isEof) {
this.push(null);
return;
}
this.push(data);
})
.catch((error: Error): void => {
if (this.#impl.closed) {
this.push(null);
}
this.emit("error", error);
});
this.#track(operation);
}
_write(chunk: any, encoding: BufferEncoding, callback: (error?: Error | null) => void): void {
let data: Buffer;
if (Buffer.isBuffer(chunk)) {
data = chunk;
} else {
data = Buffer.from(chunk, encoding);
}
const operation = this.#writeAll(data)
.then((): void => {
callback(null);
})
.catch((error: Error): void => {
callback(error);
});
this.#track(operation);
}
async #writeAll(data: Buffer): Promise<void> {
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: Promise<void>): void {
this.#pending.add(operation);
operation
.catch(_ => {})
.finally(() => {
this.#pending.delete(operation);
});
}
}
class CallbackAuthenticationService extends binding.AbstractAuthenticationService {
#callback: AuthenticationCallback;
constructor(callback: AuthenticationCallback) {
super();
this.#callback = callback;
}
async authenticate(token: string, cancellable: Cancellable | null): Promise<string> {
const info = await this.#callback(token);
return JSON.stringify(info);
}
}
function parseSocketAddress(address: BaseSocketAddress): SocketAddress {
const family = address.family;
switch (family) {
case SocketFamily.Unix: {
const addr = address as UnixSocketAddress;
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 as InetSocketAddress;
return {
family: "ipv4",
address: addr.address.toString(),
port: addr.port,
};
}
case SocketFamily.Ipv6: {
const addr = address as InetSocketAddress;
return {
family: "ipv6",
address: addr.address.toString(),
port: addr.port,
flowlabel: addr.flowinfo,
scopeid: addr.scopeId,
};
}
}
throw new Error("invalid BaseSocketAddress");
}
function objectToStrv(object: { [name: string]: string }): string[] {
return Object.entries(object).map(([k, v]) => `${k}=${v}`);
}
+96
View File
@@ -0,0 +1,96 @@
from __future__ import annotations
import argparse
from io import StringIO
from pathlib import Path
from . import codegen
from .customization import load_customizations
from .loader import compute_model
def main():
run(build_arguments())
def build_arguments() -> argparse.Namespace:
p = argparse.ArgumentParser(
description="Generate TypeScript and Node-API bindings for Frida."
)
p.add_argument(
"--frida-gir",
required=True,
type=Path,
help="Path to the Frida .gir file.",
)
p.add_argument(
"--glib-gir",
required=True,
type=Path,
help="Path to the GLib .gir file.",
)
p.add_argument(
"--gobject-gir",
required=True,
type=Path,
help="Path to the GObject .gir file.",
)
p.add_argument(
"--gio-gir",
required=True,
type=Path,
help="Path to the GIO .gir file.",
)
p.add_argument(
"--output-ts",
required=True,
type=Path,
help="Path to the output TypeScript file.",
)
p.add_argument(
"--output-dts",
required=True,
type=Path,
help="Path to the output TypeScript declaration file.",
)
p.add_argument(
"--output-c",
required=True,
type=Path,
help="Path to the output C file for N-API bindings.",
)
return p.parse_args()
def run(args: argparse.Namespace) -> None:
customizations = load_customizations()
model = compute_model(
args.frida_gir, args.glib_gir, args.gobject_gir, args.gio_gir, customizations
)
artefacts = codegen.generate_all(model)
with OutputFile(args.output_ts) as f:
f.write(artefacts["ts"])
with OutputFile(args.output_dts) as f:
f.write(artefacts["dts"])
with OutputFile(args.output_c) as f:
f.write(artefacts["c"])
class OutputFile:
def __init__(self, output_path):
self._output_path = output_path
self._io = StringIO()
def __enter__(self):
return self._io
def __exit__(self, *exc):
result = self._io.getvalue()
if self._output_path.exists():
existing_contents = self._output_path.read_text(encoding="utf-8")
if existing_contents == result:
return False
self._output_path.write_text(result, encoding="utf-8")
return False
File diff suppressed because it is too large. Load diff
+924
View File
@@ -0,0 +1,924 @@
from __future__ import annotations
from pathlib import Path
from typing import List, Mapping
from .model import (ConstructorCustomizations, CustomCode, CustomDeclaration,
Customizations, CustomMethod, CustomType, CustomTypeKind,
EnumerationCustomizations, EnumerationMemberCustomizations,
KeepAliveCustomization, MethodCustomizations,
ObjectTypeCustomizations, PropertyCustomizations,
SignalCustomizations, TypeCustomizations)
ASSETS_DIR = Path(__file__).resolve().parent / "assets"
CUSTOMIZATION_FACADE_EXPORTS = (
(ASSETS_DIR / "customization_facade.exports")
.read_text(encoding="utf-8")
.strip()
.split("\n")
)
CUSTOMIZATION_FACADE_TS = (ASSETS_DIR / "customization_facade.ts").read_text(
encoding="utf-8"
)
CUSTOMIZATION_HELPERS_IMPORTS = (
(ASSETS_DIR / "customization_helpers.imports")
.read_text(encoding="utf-8")
.strip()
.split("\n")
)
CUSTOMIZATION_HELPERS_TS = (ASSETS_DIR / "customization_helpers.ts").read_text(
encoding="utf-8"
)
def load_customizations() -> Customizations:
custom_types: List[CustomType] = {
"TargetProcess": CustomType(CustomTypeKind.TYPE, "ProcessID | ProcessName"),
"ProcessID": CustomType(CustomTypeKind.TYPE, "number"),
"InjecteeID": CustomType(CustomTypeKind.TYPE, "number"),
"FileDescriptor": CustomType(CustomTypeKind.TYPE, "number"),
"ProcessName": CustomType(CustomTypeKind.TYPE, "string"),
"SystemParameters": CustomType(
CustomTypeKind.INTERFACE,
"""
/**
* Operating System details.
*/
os: {
/**
* ID, e.g.: windows, macos, linux, ios, android, qnx, fedora, ubuntu, etc.
*/
id: string;
/**
* Human-readable name, e.g. `"macOS"`.
*/
name: string;
/**
* Human-readable version string, e.g. `"11.2.2"`.
*/
version?: string;
/**
* Build version, e.g. `"21B91"`.
*/
build?: string;
}
/**
* Platform, same as `Process.platform` in GumJS.
*/
platform: "windows" | "darwin" | "linux" | "freebsd" | "qnx";
/**
* Architecture, same as `Process.arch` in GumJS.
*/
arch: "ia32" | "x64" | "arm" | "arm64" | "mips";
/**
* Hardware details.
*/
hardware?: {
/**
* Product type, e.g. `"iPad6,3"`.
*/
product?: string;
/**
* Hardware platform, e.g. `"t8010"`.
*/
platform?: string;
/**
* Hardware model, e.g. `"J71bAP"`.
*/
model?: string;
}
/**
* Level of access.
*/
access: "full" | "jailed";
/**
* System name, e.g. `"Ole André’s iPhone"`.
*/
name?: string;
/**
* iOS UDID (Unique Device ID).
*/
udid?: string;
/**
* Details about cellular and networking interfaces.
*/
interfaces?: SystemInterface[];
/**
* Android API level, e.g.: `30`.
*/
apiLevel?: number;
[name: string]: any;
""",
),
"SystemInterface": CustomType(
CustomTypeKind.TYPE, "NetworkInterface | CellularInterface"
),
"NetworkInterface": CustomType(
CustomTypeKind.INTERFACE,
"""
type: "ethernet" | "wifi" | "bluetooth";
/**
* MAC address, e.g.: `"aa:bb:cc:dd:ee:ff"`.
*/
address: string;
""",
),
"CellularInterface": CustomType(
CustomTypeKind.INTERFACE,
"""
type: "cellular";
/**
* Phone number, e.g. `"+47 123 45 678"`.
*/
phoneNumber: string;
""",
),
"SpawnOptions": CustomType(
CustomTypeKind.INTERFACE,
"""
argv?: string[];
envp?: { [name: string]: string };
env?: { [name: string]: string };
cwd?: string;
stdio?: Stdio;
[name: string]: any;
""",
),
"RelayProperties": CustomType(
CustomTypeKind.INTERFACE,
"""
address: string;
username: string;
password: string;
kind: RelayKind;
""",
),
"Message": CustomType(CustomTypeKind.TYPE, "SendMessage | ErrorMessage"),
"MessageType": CustomType(
CustomTypeKind.ENUM,
"""
Send = "send",
Error = "error"
""",
),
"SendMessage": CustomType(
CustomTypeKind.INTERFACE,
"""
type: MessageType.Send;
payload: any;
""",
),
"ErrorMessage": CustomType(
CustomTypeKind.INTERFACE,
"""
type: MessageType.Error;
description: string;
stack?: string;
fileName?: string;
lineNumber?: number;
columnNumber?: number;
""",
),
"ScriptLogHandler": CustomType(
CustomTypeKind.TYPE, "(level: LogLevel, text: string) => void"
),
"ScriptExports": CustomType(
CustomTypeKind.INTERFACE,
"""
[name: string]: (...args: any[]) => Promise<any>;
""",
),
"LogLevel": CustomType(
CustomTypeKind.ENUM,
"""
Info = "info",
Warning = "warning",
Error = "error",
""",
),
"EnableDebuggerOptions": CustomType(
CustomTypeKind.INTERFACE,
"""
port?: number;
""",
),
"PortalServiceOptions": CustomType(
CustomTypeKind.INTERFACE,
"""
clusterParams?: EndpointParameters;
controlParams?: EndpointParameters;
""",
),
"PortalConnectionId": CustomType(CustomTypeKind.TYPE, "number"),
"PortalConnectionTag": CustomType(CustomTypeKind.TYPE, "string"),
"EndpointParametersSubset": CustomType(
CustomTypeKind.INTERFACE,
"""
address?: string;
port?: number;
certificate?: string;
origin?: string;
authentication?: AuthenticationScheme;
assetRoot?: string;
""",
),
"AuthenticationScheme": CustomType(
CustomTypeKind.TYPE,
"TokenAuthenticationScheme | CallbackAuthenticationScheme",
),
"TokenAuthenticationScheme": CustomType(
CustomTypeKind.INTERFACE,
"""
scheme: "token";
token: string;
""",
),
"CallbackAuthenticationScheme": CustomType(
CustomTypeKind.INTERFACE,
"""
scheme: "callback";
callback: AuthenticationCallback;
""",
),
"AuthenticationCallback": CustomType(
CustomTypeKind.TYPE,
"(token: string) => Promise<AuthenticatedSessionInfo>",
),
"AuthenticatedSessionInfo": CustomType(
CustomTypeKind.INTERFACE,
"""
[key: string]: any;
""",
),
"SocketAddress": CustomType(
CustomTypeKind.TYPE,
"IPV4SocketAddress | IPV6SocketAddress | AnonymousUnixSocketAddress | PathUnixSocketAddress | AbstractUnixSocketAddress",
),
"IPV4SocketAddress": CustomType(
CustomTypeKind.INTERFACE,
"""
family: "ipv4";
address: string;
port: number;
""",
),
"IPV6SocketAddress": CustomType(
CustomTypeKind.INTERFACE,
"""
family: "ipv6";
address: string;
port: number;
flowlabel: number;
scopeid: number;
""",
),
"AnonymousUnixSocketAddress": CustomType(
CustomTypeKind.INTERFACE,
"""
family: "unix:anonymous";
""",
),
"PathUnixSocketAddress": CustomType(
CustomTypeKind.INTERFACE,
"""
family: "unix:path";
path: string;
""",
),
"AbstractUnixSocketAddress": CustomType(
CustomTypeKind.INTERFACE,
"""
family: "unix:abstract";
path: Buffer;
""",
),
"Variant": CustomType(
CustomTypeKind.TYPE, "VariantValue | [type: symbol, value: VariantValue]"
),
"VariantValue": CustomType(
CustomTypeKind.TYPE,
"""
| boolean
| number
| string
| Buffer
| Variant[]
| VariantDict
""",
),
"VariantDict": CustomType(
CustomTypeKind.INTERFACE,
"""
[key: string]: Variant;
""",
),
}
type_customizations: Mapping[str, TypeCustomizations] = {
"DeviceManager": ObjectTypeCustomizations(
signals={
"added": SignalCustomizations(behavior="FDN_SIGNAL_KEEP_ALIVE"),
"removed": SignalCustomizations(behavior="FDN_SIGNAL_KEEP_ALIVE"),
"changed": SignalCustomizations(behavior="FDN_SIGNAL_KEEP_ALIVE"),
},
cleanup="close",
),
"Device": ObjectTypeCustomizations(
methods={
"query_system_parameters": MethodCustomizations(
return_typing="Promise<SystemParameters>",
return_wrapper="as SystemParameters",
),
"spawn": MethodCustomizations(
param_typings=[
"programOrArgv: string | string[]",
"opts?: SpawnOptions",
"cancellable?: Cancellable | null",
],
return_typing="Promise<ProcessID>",
custom_logic="""
const options: RawSpawnOptions = {};
let program: string;
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)));
}
""",
),
"input": MethodCustomizations(
param_typings=[
"target: TargetProcess",
"data: Buffer",
"cancellable?: Cancellable | null",
],
custom_logic="const pid = await this.#getPid(target, cancellable);",
),
"resume": MethodCustomizations(
param_typings=[
"target: TargetProcess",
"cancellable?: Cancellable | null",
],
custom_logic="const pid = await this.#getPid(target, cancellable);",
),
"kill": MethodCustomizations(
param_typings=[
"target: TargetProcess",
"cancellable?: Cancellable | null",
],
custom_logic="const pid = await this.#getPid(target, cancellable);",
),
"attach": MethodCustomizations(
param_typings=[
"target: TargetProcess",
"options?: SessionOptions",
"cancellable?: Cancellable | null",
],
custom_logic="const pid = await this.#getPid(target, cancellable);",
),
"inject_library_file": MethodCustomizations(
param_typings=[
"target: TargetProcess",
"path: string",
"entrypoint: string",
"data: string",
"cancellable?: Cancellable | null",
],
return_typing="Promise<InjecteeID>",
custom_logic="const pid = await this.#getPid(target, cancellable);",
),
"inject_library_blob": MethodCustomizations(
param_typings=[
"target: TargetProcess",
"blob: Buffer",
"entrypoint: string",
"data: string",
"cancellable?: Cancellable | null",
],
return_typing="Promise<InjecteeID>",
custom_logic="const pid = await this.#getPid(target, cancellable);",
),
"open_channel": MethodCustomizations(
return_typing="Promise<NodeJS.ReadWriteStream>",
return_wrapper="new IOStreamAdapter",
),
},
properties={
"dtype": PropertyCustomizations(
js_name="type",
),
},
signals={
"spawn-added": SignalCustomizations(behavior="FDN_SIGNAL_KEEP_ALIVE"),
"spawn-removed": SignalCustomizations(behavior="FDN_SIGNAL_KEEP_ALIVE"),
"child-added": SignalCustomizations(behavior="FDN_SIGNAL_KEEP_ALIVE"),
"child-removed": SignalCustomizations(behavior="FDN_SIGNAL_KEEP_ALIVE"),
"process-crashed": SignalCustomizations(
behavior="FDN_SIGNAL_KEEP_ALIVE"
),
"output": SignalCustomizations(
behavior="FDN_SIGNAL_KEEP_ALIVE",
transform={
0: ("pid: ProcessID", None),
1: ("fd: FileDescriptor", None),
},
),
"uninjected": SignalCustomizations(
behavior="FDN_SIGNAL_KEEP_ALIVE",
transform={
0: ("id: InjecteeID", None),
},
),
},
custom_code=CustomCode(
methods=[
CustomMethod(
typing="getProcess(name: string, options?: ProcessMatchOptions, cancellable?: Cancellable | null): Promise<Process>",
code="""
async getProcess(name: string, options: ProcessMatchOptions = {}, cancellable?: Cancellable | null): Promise<Process> {
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");
}
}
""",
),
CustomMethod(
typing=None,
code="""
async #getPid(target: TargetProcess, cancellable?: Cancellable | null): Promise<ProcessID> {
if (typeof target === "number") {
return target;
}
const process = await this.getProcess(target, {}, cancellable);
return process.pid;
}
""",
),
],
),
),
"SpawnOptions": ObjectTypeCustomizations(js_name="RawSpawnOptions"),
"Bus": ObjectTypeCustomizations(
methods={
"post": MethodCustomizations(
param_typings=[
"message: any",
"data?: Buffer | null",
],
custom_logic="const json = JSON.stringify(message);",
),
},
signals={
"detached": SignalCustomizations(behavior="FDN_SIGNAL_KEEP_ALIVE"),
"message": SignalCustomizations(
behavior="FDN_SIGNAL_KEEP_ALIVE",
transform={
0: ("message: any", "JSON.parse"),
},
),
},
),
"Service": ObjectTypeCustomizations(
keep_alive=KeepAliveCustomization(
is_destroyed_function="is_closed", destroy_signal_name="close"
),
),
"Relay": ObjectTypeCustomizations(
constructor=ConstructorCustomizations(
param_typings=[
"properties: RelayProperties",
],
custom_logic="const { address, username, password, kind } = properties;",
),
),
"RelayKind": EnumerationCustomizations(
members={
"turn_udp": EnumerationMemberCustomizations(js_name="TurnUDP"),
"turn_tcp": EnumerationMemberCustomizations(js_name="TurnTCP"),
"turn_tls": EnumerationMemberCustomizations(js_name="TurnTLS"),
},
),
"Script": ObjectTypeCustomizations(
methods={
"is_destroyed": MethodCustomizations(hide=True),
"post": MethodCustomizations(
param_typings=[
"message: any",
"data?: Buffer | null",
],
custom_logic="const json = JSON.stringify(message);",
),
"enable_debugger": MethodCustomizations(
param_typings=[
"options?: EnableDebuggerOptions",
"cancellable?: Cancellable | null",
],
custom_logic="const port = options?.port ?? 0;",
),
},
signals={
"message": SignalCustomizations(
transform={
0: ("message: Message", "JSON.parse"),
},
intercept="this.#services.handleMessageIntercept",
),
},
custom_code=CustomCode(
declarations=[
CustomDeclaration(
typing=None, code="#services = new ScriptServices(this);"
),
CustomDeclaration(
typing="logHandler: ScriptLogHandler",
code="logHandler: ScriptLogHandler = log;",
),
],
methods=[
CustomMethod(
typing="readonly isDestroyed: boolean",
code="""
get isDestroyed(): boolean {
return this._isDestroyed();
}
""",
),
CustomMethod(
typing="readonly exports: ScriptExports",
code="""
get exports(): ScriptExports {
return this.#services.exportsProxy;
}
""",
),
CustomMethod(
typing="readonly defaultLogHandler: ScriptLogHandler",
code="""
get defaultLogHandler(): ScriptLogHandler {
return log;
}
""",
),
],
),
keep_alive=KeepAliveCustomization(
is_destroyed_function="is_destroyed", destroy_signal_name="destroyed"
),
),
"ScriptRuntime": EnumerationCustomizations(
members={
"qjs": EnumerationMemberCustomizations(js_name="QJS"),
},
),
"ControlService": ObjectTypeCustomizations(
methods={
"start": MethodCustomizations(ref_keep_alive=True),
"stop": MethodCustomizations(unref_keep_alive=True),
"get_endpoint_params": MethodCustomizations(drop=True),
},
properties={
"endpoint-params": PropertyCustomizations(drop=True),
},
),
"PortalService": ObjectTypeCustomizations(
constructor=ConstructorCustomizations(
param_typings=[
"options?: PortalServiceOptions",
],
custom_logic="""
const clusterParams = options?.clusterParams ?? new EndpointParameters();
const controlParams = options?.controlParams ?? null;
""",
),
methods={
"start": MethodCustomizations(ref_keep_alive=True),
"stop": MethodCustomizations(unref_keep_alive=True),
"kick": MethodCustomizations(
param_typings=[
"connectionId: PortalConnectionId",
],
),
"post": MethodCustomizations(
param_typings=[
"connectionId: PortalConnectionId",
"message: any",
"data?: Buffer | null",
],
custom_logic="const json = JSON.stringify(message);",
),
"narrowcast": MethodCustomizations(
param_typings=[
"tag: string",
"message: any",
"data?: Buffer | null",
],
custom_logic="const json = JSON.stringify(message);",
),
"broadcast": MethodCustomizations(
param_typings=[
"message: any",
"data?: Buffer | null",
],
custom_logic="const json = JSON.stringify(message);",
),
"enumerate_tags": MethodCustomizations(
param_typings=[
"connectionId: PortalConnectionId",
],
),
"tag": MethodCustomizations(
param_typings=[
"connectionId: PortalConnectionId",
"tag: string",
],
),
"untag": MethodCustomizations(
param_typings=[
"connectionId: PortalConnectionId",
"tag: string",
],
),
},
signals={
"node-connected": SignalCustomizations(
transform={
0: ("connectionId: PortalConnectionId", None),
1: ("remoteAddress: SocketAddress", "parseSocketAddress"),
},
),
"node-joined": SignalCustomizations(
transform={
0: ("connectionId: PortalConnectionId", None),
},
),
"node-left": SignalCustomizations(
transform={
0: ("connectionId: PortalConnectionId", None),
},
),
"node-disconnected": SignalCustomizations(
transform={
0: ("connectionId: PortalConnectionId", None),
1: ("remoteAddress: SocketAddress", "parseSocketAddress"),
},
),
"controller-connected": SignalCustomizations(
transform={
0: ("connectionId: PortalConnectionId", None),
1: ("remoteAddress: SocketAddress", "parseSocketAddress"),
},
),
"controller-disconnected": SignalCustomizations(
transform={
0: ("connectionId: PortalConnectionId", None),
1: ("remoteAddress: SocketAddress", "parseSocketAddress"),
},
),
"authenticated": SignalCustomizations(
transform={
0: ("connectionId: PortalConnectionId", None),
1: ("sessionInfo: AuthenticatedSessionInfo", "JSON.parse"),
},
),
"subscribe": SignalCustomizations(
transform={
0: ("connectionId: PortalConnectionId", None),
},
),
"message": SignalCustomizations(
transform={
0: ("connectionId: PortalConnectionId", None),
1: ("message: any", "JSON.parse"),
},
),
},
),
"EndpointParameters": ObjectTypeCustomizations(
constructor=ConstructorCustomizations(
param_typings=[
"params?: EndpointParametersSubset",
],
custom_logic="""
const address = params?.address ?? null;
const port = params?.port ?? 0;
const certificate = params?.certificate ?? null;
const origin = params?.origin ?? null;
let authService: AuthenticationService | null = 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;
""",
),
),
"Injector": ObjectTypeCustomizations(drop=True),
"RpcClient": ObjectTypeCustomizations(drop=True),
"RpcPeer": ObjectTypeCustomizations(drop=True),
"Cancellable": ObjectTypeCustomizations(
methods={
"is_cancelled": MethodCustomizations(hide=True),
"set_error_if_cancelled": MethodCustomizations(
js_name="throwIfCancelled",
return_typing="void",
),
"make_pollfd": MethodCustomizations(drop=True),
"release_fd": MethodCustomizations(drop=True),
"source_new": MethodCustomizations(drop=True),
},
custom_code=CustomCode(
methods=[
CustomMethod(
typing="readonly isCancelled: boolean",
code="""
get isCancelled(): boolean {
return this._isCancelled();
}
""",
),
],
),
),
"IOStream": ObjectTypeCustomizations(
methods={
"close": MethodCustomizations(drop=True),
"close_async": MethodCustomizations(js_name="close"),
"splice_async": MethodCustomizations(drop=True),
"has_pending": MethodCustomizations(drop=True),
"set_pending": MethodCustomizations(drop=True),
"clear_pending": MethodCustomizations(drop=True),
},
),
"InputStream": ObjectTypeCustomizations(
methods={
"close": MethodCustomizations(drop=True),
"close_async": MethodCustomizations(js_name="close"),
"read": MethodCustomizations(drop=True),
"read_async": MethodCustomizations(drop=True),
"read_all": MethodCustomizations(drop=True),
"read_all_async": MethodCustomizations(drop=True),
"read_bytes": MethodCustomizations(drop=True),
"read_bytes_async": MethodCustomizations(js_name="read"),
"skip": MethodCustomizations(drop=True),
"skip_async": MethodCustomizations(js_name="skip"),
"is_closed": MethodCustomizations(drop=True),
"has_pending": MethodCustomizations(drop=True),
"set_pending": MethodCustomizations(drop=True),
"clear_pending": MethodCustomizations(drop=True),
},
),
"OutputStream": ObjectTypeCustomizations(
methods={
"close": MethodCustomizations(drop=True),
"close_async": MethodCustomizations(js_name="close"),
"flush": MethodCustomizations(drop=True),
"flush_async": MethodCustomizations(js_name="flush"),
"write": MethodCustomizations(drop=True),
"write_async": MethodCustomizations(drop=True),
"write_all": MethodCustomizations(drop=True),
"write_all_async": MethodCustomizations(drop=True),
"write_bytes": MethodCustomizations(drop=True),
"write_bytes_async": MethodCustomizations(js_name="write"),
"writev": MethodCustomizations(drop=True),
"writev_async": MethodCustomizations(drop=True),
"writev_all": MethodCustomizations(drop=True),
"writev_all_async": MethodCustomizations(drop=True),
"splice": MethodCustomizations(drop=True),
"splice_async": MethodCustomizations(drop=True),
"is_closing": MethodCustomizations(drop=True),
"is_closed": MethodCustomizations(drop=True),
"has_pending": MethodCustomizations(drop=True),
"set_pending": MethodCustomizations(drop=True),
"clear_pending": MethodCustomizations(drop=True),
},
),
"UnixSocketAddress": ObjectTypeCustomizations(
methods={
"get_path": MethodCustomizations(
return_typing="Buffer",
return_cconversion="fdn_buffer_to_value (env, (const guint8 *) retval, g_unix_socket_address_get_path_len (handle))",
),
"get_path_len": MethodCustomizations(drop=True),
"get_is_abstract": MethodCustomizations(drop=True),
},
properties={
"path": PropertyCustomizations(typing="path: Buffer"),
"abstract": PropertyCustomizations(drop=True),
"path-as-array": PropertyCustomizations(drop=True),
},
),
"SocketAddress": ObjectTypeCustomizations(
js_name="BaseSocketAddress",
constructor=ConstructorCustomizations(drop=True),
methods={
"to_native": MethodCustomizations(drop=True),
},
),
"SocketConnectable": ObjectTypeCustomizations(drop_abstract_base=True),
"InetAddress": ObjectTypeCustomizations(
properties={
"bytes": PropertyCustomizations(drop=True),
},
),
"Object": ObjectTypeCustomizations(
js_name="BaseObject",
methods={
"is_floating": MethodCustomizations(drop=True),
"ref": MethodCustomizations(drop=True),
"ref_sink": MethodCustomizations(drop=True),
"unref": MethodCustomizations(drop=True),
"getv": MethodCustomizations(drop=True),
"get_property": MethodCustomizations(drop=True),
"set_property": MethodCustomizations(drop=True),
"notify_by_pspec": MethodCustomizations(drop=True),
"freeze_notify": MethodCustomizations(drop=True),
"thaw_notify": MethodCustomizations(drop=True),
"bind_property": MethodCustomizations(drop=True),
"bind_property_full": MethodCustomizations(drop=True),
"bind_property_with_closures": MethodCustomizations(drop=True),
"force_floating": MethodCustomizations(drop=True),
"get_data": MethodCustomizations(drop=True),
"get_qdata": MethodCustomizations(drop=True),
"run_dispose": MethodCustomizations(drop=True),
"set_data": MethodCustomizations(drop=True),
"steal_data": MethodCustomizations(drop=True),
"steal_qdata": MethodCustomizations(drop=True),
"watch_closure": MethodCustomizations(drop=True),
},
signals={
"notify": SignalCustomizations(drop=True),
},
),
}
return Customizations(
custom_types,
type_customizations,
CUSTOMIZATION_FACADE_EXPORTS,
CUSTOMIZATION_FACADE_TS,
CUSTOMIZATION_HELPERS_IMPORTS,
CUSTOMIZATION_HELPERS_TS,
)
+60
View File
@@ -0,0 +1,60 @@
from __future__ import annotations
from collections import OrderedDict
from pathlib import Path
from .model import Customizations, Model, parse_gir
INCLUDED_GIO_OBJECT_TYPES = [
"Cancellable",
"IOStream",
"InputStream",
"OutputStream",
"InetSocketAddress",
"InetAddress",
"UnixSocketAddress",
"SocketAddress",
"SocketAddressEnumerator",
"SocketConnectable",
]
INCLUDED_GIO_ENUMERATIONS = [
"FileMonitorEvent",
"SocketFamily",
"UnixSocketAddressType",
]
def compute_model(
frida_gir: Path,
glib_gir: Path,
gobject_gir: Path,
gio_gir: Path,
customizations: Customizations,
) -> Model:
glib = parse_gir(glib_gir, [])
gobject = parse_gir(gobject_gir, [glib])
gio = parse_gir(gio_gir, [glib, gobject])
frida = parse_gir(frida_gir, [glib, gobject, gio])
object_types = OrderedDict(frida.object_types)
object_types["Object"] = gobject.object_types["Object"]
for t in INCLUDED_GIO_OBJECT_TYPES:
object_types[t] = gio.object_types[t]
enumerations = OrderedDict(frida.enumerations)
for t in INCLUDED_GIO_ENUMERATIONS:
enumerations[t] = gio.enumerations[t]
model = Model(
frida.namespace,
object_types,
enumerations,
customizations,
)
for t in object_types.values():
t.model = model
for t in enumerations.values():
t.model = model
return model
File diff suppressed because it is too large. Load diff
-62
View File
@@ -1,62 +0,0 @@
#include "glib_context.h"
#define GLIB_CONTEXT_LOCK() g_mutex_lock(&mutex_)
#define GLIB_CONTEXT_UNLOCK() g_mutex_unlock(&mutex_)
#define GLIB_CONTEXT_WAIT() g_cond_wait(&cond_, &mutex_)
#define GLIB_CONTEXT_SIGNAL() g_cond_signal(&cond_)
namespace frida {
GLibContext::GLibContext(GMainContext* main_context) : main_context_(main_context) {
g_mutex_init(&mutex_);
g_cond_init(&cond_);
}
GLibContext::~GLibContext() {
g_cond_clear(&cond_);
g_mutex_clear(&mutex_);
}
void GLibContext::Schedule(std::function<void ()> f) {
auto source = g_idle_source_new();
g_source_set_callback(source, InvokeCallback, new std::function<void ()>(f),
DestroyCallback);
g_source_attach(source, main_context_);
g_source_unref(source);
}
void GLibContext::Perform(std::function<void ()> f) {
volatile bool finished = false;
auto work = new std::function<void ()>([this, f, &finished]() {
f();
GLIB_CONTEXT_LOCK();
finished = true;
GLIB_CONTEXT_SIGNAL();
GLIB_CONTEXT_UNLOCK();
});
auto source = g_idle_source_new();
g_source_set_callback(source, InvokeCallback, work, DestroyCallback);
g_source_attach(source, main_context_);
g_source_unref(source);
GLIB_CONTEXT_LOCK();
while (!finished)
GLIB_CONTEXT_WAIT();
GLIB_CONTEXT_UNLOCK();
}
gboolean GLibContext::InvokeCallback(gpointer data) {
auto f = static_cast<std::function<void ()>*>(data);
(*f)();
return FALSE;
}
void GLibContext::DestroyCallback(gpointer data) {
auto f = static_cast<std::function<void ()>*>(data);
delete f;
}
}
-29
View File
@@ -1,29 +0,0 @@
#ifndef FRIDANODE_GLIB_CONTEXT_H
#define FRIDANODE_GLIB_CONTEXT_H
#include <frida-core.h>
#include <functional>
namespace frida {
class GLibContext {
public:
GLibContext(GMainContext* main_context);
~GLibContext();
void Schedule(std::function<void ()> f);
void Perform(std::function<void ()> f);
private:
static gboolean InvokeCallback(gpointer data);
static void DestroyCallback(gpointer data);
GMainContext* main_context_;
GMutex mutex_;
GCond cond_;
};
}
#endif
-25
View File
@@ -1,25 +0,0 @@
#include "glib_object.h"
using v8::External;
using v8::FunctionTemplate;
using v8::Local;
using v8::String;
using v8::Value;
namespace frida {
Local<FunctionTemplate> GLibObject::CreateTemplate(Local<String> name,
Nan::FunctionCallback callback, Runtime* runtime) {
Local<FunctionTemplate> tpl = Nan::New<FunctionTemplate>(callback,
Nan::New<External>(runtime));
tpl->SetClassName(name);
tpl->InstanceTemplate()->SetInternalFieldCount(1);
return tpl;
}
Runtime* GLibObject::GetRuntimeFromConstructorArgs(
const Nan::FunctionCallbackInfo<Value>& args) {
return static_cast<Runtime*>(args.Data().As<External>()->Value ());
}
}
-37
View File
@@ -1,37 +0,0 @@
#ifndef FRIDANODE_GLIB_OBJECT_H
#define FRIDANODE_GLIB_OBJECT_H
#include "runtime.h"
#include <glib.h>
#include <nan.h>
#include <node_object_wrap.h>
namespace frida {
class GLibObject : public node::ObjectWrap {
protected:
GLibObject(gpointer handle, Runtime* runtime)
: handle_(handle),
runtime_(runtime) {
}
static v8::Local<v8::FunctionTemplate> CreateTemplate(v8::Local<v8::String> name,
Nan::FunctionCallback callback, Runtime* runtime);
static Runtime* GetRuntimeFromConstructorArgs(
const Nan::FunctionCallbackInfo<v8::Value>& info);
public:
template<typename T>
T* GetHandle() const { return static_cast<T*>(handle_); }
Runtime* GetRuntime() const { return runtime_; }
protected:
gpointer handle_;
Runtime* runtime_;
};
}
#endif
-243
View File
@@ -1,243 +0,0 @@
#include "iostream.h"
#include "operation.h"
#include "signals.h"
#define IOSTREAM_DATA_CONSTRUCTOR "iostream:ctor"
using v8::DEFAULT;
using v8::External;
using v8::Function;
using v8::Isolate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::Persistent;
using v8::ReadOnly;
using v8::Value;
namespace frida {
static void UnrefGBytes(char* data, void* hint);
IOStream::IOStream(GIOStream* handle, Runtime* runtime)
: GLibObject(handle, runtime) {
g_object_ref(handle_);
}
IOStream::~IOStream() {
g_object_unref(handle_);
}
void IOStream::Init(Local<Object> exports, Runtime* runtime) {
auto isolate = Isolate::GetCurrent();
auto name = Nan::New("IOStream").ToLocalChecked();
auto tpl = CreateTemplate(name, New, runtime);
auto instance_tpl = tpl->InstanceTemplate();
auto data = Local<Value>();
Nan::SetAccessor(instance_tpl, Nan::New("isClosed").ToLocalChecked(),
IsClosed, 0, data, DEFAULT, ReadOnly);
Nan::SetPrototypeMethod(tpl, "close", Close);
Nan::SetPrototypeMethod(tpl, "read", Read);
Nan::SetPrototypeMethod(tpl, "write", Write);
auto ctor = Nan::GetFunction(tpl).ToLocalChecked();
Nan::Set(exports, name, ctor);
runtime->SetDataPointer(IOSTREAM_DATA_CONSTRUCTOR,
new Persistent<Function>(isolate, ctor));
}
Local<Object> IOStream::New(gpointer handle, Runtime* runtime) {
auto ctor = Nan::New<Function>(
*static_cast<Persistent<Function>*>(
runtime->GetDataPointer(IOSTREAM_DATA_CONSTRUCTOR)));
const int argc = 1;
Local<Value> argv[argc] = { Nan::New<External>(handle) };
return Nan::NewInstance(ctor, argc, argv).ToLocalChecked();
}
NAN_METHOD(IOStream::New) {
if (!info.IsConstructCall()) {
Nan::ThrowError("Use the `new` keyword to create a new instance");
return;
}
if (info.Length() != 1 || !info[0]->IsExternal()) {
Nan::ThrowTypeError("Bad argument, expected raw handle");
return;
}
auto runtime = GetRuntimeFromConstructorArgs(info);
auto handle = static_cast<GIOStream*>(
Local<External>::Cast(info[0])->Value());
auto wrapper = new IOStream(handle, runtime);
auto obj = info.This();
wrapper->Wrap(obj);
Nan::Set(obj, Nan::New("signals").ToLocalChecked(),
Signals::New(handle, runtime));
info.GetReturnValue().Set(obj);
}
NAN_PROPERTY_GETTER(IOStream::IsClosed) {
auto handle = ObjectWrap::Unwrap<IOStream>(
info.Holder())->GetHandle<GIOStream>();
info.GetReturnValue().Set(
static_cast<bool>(g_io_stream_is_closed(handle)));
}
namespace {
class CloseOperation : public Operation<GIOStream> {
protected:
void Begin() {
g_io_stream_close_async(handle_, G_PRIORITY_DEFAULT, cancellable_,
OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
g_io_stream_close_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
return Nan::Undefined();
}
};
}
NAN_METHOD(IOStream::Close) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<IOStream>(info.Holder());
auto operation = new CloseOperation();
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
namespace {
class ReadOperation : public Operation<GIOStream> {
public:
ReadOperation(gsize count)
: stream_(NULL),
count_(count),
bytes_(NULL) {
}
protected:
void Begin() {
stream_ = g_io_stream_get_input_stream(handle_);
g_input_stream_read_bytes_async(stream_, count_, G_PRIORITY_DEFAULT,
cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
bytes_ = g_input_stream_read_bytes_finish(stream_, result, error);
}
Local<Value> Result(Isolate* isolate) {
gsize size;
auto data = g_bytes_get_data(bytes_, &size);
return Nan::NewBuffer(static_cast<char*>(const_cast<void*>(data)), size,
UnrefGBytes, bytes_).ToLocalChecked();
}
private:
GInputStream* stream_;
gsize count_;
GBytes* bytes_;
};
}
NAN_METHOD(IOStream::Read) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<IOStream>(info.Holder());
if (info.Length() < 1 || !info[0]->IsNumber()) {
Nan::ThrowTypeError("Bad argument, expected amount to read");
return;
}
auto count = Nan::To<int32_t>(info[0]).FromMaybe(-1);
if (count <= 0) {
Nan::ThrowTypeError("Bad argument, expected amount to read");
return;
}
auto operation = new ReadOperation(count);
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
namespace {
class WriteOperation : public Operation<GIOStream> {
public:
WriteOperation(Isolate* isolate, Local<Value> buffer)
: stream_(NULL),
data_(node::Buffer::Data(buffer)),
count_(node::Buffer::Length(buffer)) {
buffer_.Reset(buffer);
}
protected:
void Begin() {
stream_ = g_io_stream_get_output_stream(handle_);
g_output_stream_write_all_async(stream_, data_, count_, G_PRIORITY_DEFAULT,
cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
g_output_stream_write_all_finish(stream_, result, NULL, error);
}
Local<Value> Result(Isolate* isolate) {
return Nan::Undefined();
}
private:
GOutputStream* stream_;
Nan::Persistent<Value, Nan::CopyablePersistentTraits<Value>> buffer_;
const void* data_;
gsize count_;
};
}
NAN_METHOD(IOStream::Write) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<IOStream>(info.Holder());
auto num_args = info.Length();
if (num_args < 1) {
Nan::ThrowTypeError("Expected a buffer");
return;
}
auto buffer = info[0];
if (!node::Buffer::HasInstance(buffer)) {
Nan::ThrowTypeError("Expected a buffer");
return;
}
auto operation = new WriteOperation(isolate, buffer);
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
static void UnrefGBytes(char* data, void* hint) {
g_bytes_unref(static_cast<GBytes*>(hint));
}
}
-30
View File
@@ -1,30 +0,0 @@
#ifndef FRIDANODE_IOSTREAM_H
#define FRIDANODE_IOSTREAM_H
#include "glib_object.h"
#include <gio/gio.h>
namespace frida {
class IOStream : public GLibObject {
public:
static void Init(v8::Local<v8::Object> exports, Runtime* runtime);
static v8::Local<v8::Object> New(gpointer handle, Runtime* runtime);
private:
explicit IOStream(GIOStream* handle, Runtime* runtime);
~IOStream();
static NAN_METHOD(New);
static NAN_PROPERTY_GETTER(IsClosed);
static NAN_METHOD(Close);
static NAN_METHOD(Read);
static NAN_METHOD(Write);
};
}
#endif
+91 -26
View File
@@ -1,31 +1,96 @@
frida_binding_sources = files(
'addon.cc',
'device_manager.cc',
'device.cc',
'application.cc',
'process.cc',
'spawn.cc',
'child.cc',
'crash.cc',
'bus.cc',
'service.cc',
'session.cc',
'script.cc',
'relay.cc',
'portal_membership.cc',
'portal_service.cc',
'endpoint_parameters.cc',
'authentication.cc',
'iostream.cc',
'cancellable.cc',
'signals.cc',
'glib_object.cc',
'runtime.cc',
'uv_context.cc',
'glib_context.cc',
'win_delay_load_hook.cc',
if frida_core_dep.type_name() == 'internal'
frida_core_subprj = subproject('frida-core')
frida_gir = frida_core_subprj.get_variable('core_public_gir')
glib_gir = frida_core_subprj.get_variable('glib_gir')
gobject_gir = frida_core_subprj.get_variable('gobject_gir')
gio_gir = frida_core_subprj.get_variable('gio_gir')
else
girdir = frida_core_dep.get_variable('frida_girdir')
frida_gir = girdir / 'Frida-1.0.gir'
glib_gir = girdir / 'GLib-2.0.gir'
gobject_gir = girdir / 'GObject-2.0.gir'
gio_gir = girdir / 'Gio-2.0.gir'
endif
env = environment()
env.set('PYTHONPATH', meson.current_source_dir())
code = custom_target('binding-code',
output: [
'frida.ts',
'frida_binding.d.ts',
'frida_binding.c',
],
input: [
frida_gir,
glib_gir,
gobject_gir,
gio_gir,
files(
'frida_bindgen' / '__init__.py',
'frida_bindgen' / '__main__.py',
'frida_bindgen' / 'cli.py',
'frida_bindgen' / 'codegen.py',
'frida_bindgen' / 'customization.py',
'frida_bindgen' / 'loader.py',
'frida_bindgen' / 'model.py',
'frida_bindgen' / 'assets' / 'codegen_helpers.c',
'frida_bindgen' / 'assets' / 'codegen_helpers.ts',
'frida_bindgen' / 'assets' / 'codegen_prototypes.h',
'frida_bindgen' / 'assets' / 'codegen_types.h',
'frida_bindgen' / 'assets' / 'customization_facade.exports',
'frida_bindgen' / 'assets' / 'customization_facade.ts',
'frida_bindgen' / 'assets' / 'customization_helpers.imports',
'frida_bindgen' / 'assets' / 'customization_helpers.ts',
)
],
command: [
python, '-m', 'frida_bindgen',
'--frida-gir=@INPUT0@',
'--glib-gir=@INPUT1@',
'--gobject-gir=@INPUT2@',
'--gio-gir=@INPUT3@',
'--output-ts=@OUTPUT0@',
'--output-dts=@OUTPUT1@',
'--output-c=@OUTPUT2@',
],
env: env,
install: true,
install_dir: [
false,
pkg_install_dir / 'build' / 'src',
false,
],
)
custom_target('js',
input: [
code[0],
code[1],
],
output: [
'frida.js',
'frida.d.ts',
],
command: [
python,
files('tsc.py'),
'@OUTDIR@',
'@PRIVATE_DIR@',
npm,
package_json,
tsconfig_json,
'@INPUT@',
],
install: true,
install_dir: pkg_install_dir / 'build' / 'src',
)
frida_binding_sources = [
code[2],
files('win_delay_load_hook.c'),
]
if system == 'windows'
if cpp.get_argument_syntax() != 'msvc'
symfile = 'addon.symbols'
-94
View File
@@ -1,94 +0,0 @@
#ifndef FRIDANODE_OPERATION_H
#define FRIDANODE_OPERATION_H
#include "cancellable.h"
#include "runtime.h"
namespace frida {
template<class T>
class Operation {
public:
void Schedule(v8::Isolate* isolate, GLibObject* parent,
const Nan::FunctionCallbackInfo<v8::Value>& info) {
wrapper_ = parent;
parent_.Reset(isolate, parent->handle(isolate));
handle_ = parent->GetHandle<T>();
resolver_.Reset(isolate,
v8::Promise::Resolver::New(isolate->GetCurrentContext())
.ToLocalChecked());
runtime_ = parent->GetRuntime();
auto num_args = info.Length();
if (num_args >= 1) {
cancellable_ = Cancellable::TryParse(info[num_args - 1], runtime_);
}
runtime_->GetUVContext()->IncreaseUsage();
runtime_->GetGLibContext()->Schedule([=]() { Begin(); });
}
v8::Local<v8::Promise> GetPromise(v8::Isolate* isolate) {
return v8::Local<v8::Promise::Resolver>::New(isolate, resolver_)->GetPromise();
}
protected:
Operation()
: wrapper_(NULL),
handle_(NULL),
cancellable_(NULL),
runtime_(NULL),
error_(NULL) {
}
virtual ~Operation() {
if (error_ != NULL) {
g_error_free(error_);
}
resolver_.Reset();
parent_.Reset();
}
virtual void Begin() = 0;
virtual void End(GAsyncResult* result, GError** error) = 0;
virtual v8::Local<v8::Value> Result(v8::Isolate* isolate) = 0;
template<typename W>
W* GetWrapper() const { return static_cast<W*>(wrapper_); }
static void OnReady(GObject* source_object, GAsyncResult* result, gpointer user_data) {
static_cast<Operation<T>*>(user_data)->PerformEnd(result);
}
void* wrapper_;
v8::Persistent<v8::Value> parent_;
T* handle_;
GCancellable* cancellable_;
v8::Persistent<v8::Promise::Resolver> resolver_;
Runtime* runtime_;
private:
void PerformEnd(GAsyncResult* result) {
End(result, &error_);
runtime_->GetUVContext()->Schedule([=]() { Deliver(); });
}
void Deliver() {
auto isolate = v8::Isolate::GetCurrent();
auto context = isolate->GetCurrentContext();
auto resolver = v8::Local<v8::Promise::Resolver>::New(isolate, resolver_);
if (error_ == NULL) {
resolver->Resolve(context, Result(isolate)).FromJust();
} else {
resolver->Reject(context, Nan::Error(error_->message)).FromJust();
}
runtime_->GetUVContext()->DecreaseUsage();
delete this;
}
GError* error_;
};
}
#endif
-100
View File
@@ -1,100 +0,0 @@
#include "portal_membership.h"
#include "operation.h"
#define PORTAL_MEMBERSHIP_DATA_CONSTRUCTOR "portal_membership:ctor"
using v8::External;
using v8::Function;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::Persistent;
using v8::Value;
namespace frida {
PortalMembership::PortalMembership(FridaPortalMembership* handle, Runtime* runtime)
: GLibObject(handle, runtime) {
g_object_ref(handle_);
}
PortalMembership::~PortalMembership() {
frida_unref(handle_);
}
void PortalMembership::Init(Local<Object> exports, Runtime* runtime) {
auto isolate = Isolate::GetCurrent();
auto name = Nan::New("PortalMembership").ToLocalChecked();
auto tpl = CreateTemplate(name, New, runtime);
Nan::SetPrototypeMethod(tpl, "terminate", Terminate);
auto ctor = Nan::GetFunction(tpl).ToLocalChecked();
Nan::Set(exports, name, ctor);
runtime->SetDataPointer(PORTAL_MEMBERSHIP_DATA_CONSTRUCTOR,
new Persistent<Function>(isolate, ctor));
}
Local<Object> PortalMembership::New(gpointer handle, Runtime* runtime) {
auto ctor = Nan::New<Function>(
*static_cast<Persistent<Function>*>(
runtime->GetDataPointer(PORTAL_MEMBERSHIP_DATA_CONSTRUCTOR)));
const int argc = 1;
Local<Value> argv[argc] = { Nan::New<External>(handle) };
return Nan::NewInstance(ctor, argc, argv).ToLocalChecked();
}
NAN_METHOD(PortalMembership::New) {
if (!info.IsConstructCall()) {
Nan::ThrowError("Use the `new` keyword to create a new instance");
return;
}
if (info.Length() != 1 || !info[0]->IsExternal()) {
Nan::ThrowTypeError("Bad argument, expected raw handle");
return;
}
auto runtime = GetRuntimeFromConstructorArgs(info);
auto handle = static_cast<FridaPortalMembership*>(
Local<External>::Cast(info[0])->Value());
auto wrapper = new PortalMembership(handle, runtime);
auto obj = info.This();
wrapper->Wrap(obj);
info.GetReturnValue().Set(obj);
}
namespace {
class TerminateOperation : public Operation<FridaPortalMembership> {
protected:
void Begin() {
frida_portal_membership_terminate(handle_, cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
frida_portal_membership_terminate_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
return Nan::Undefined();
}
};
}
NAN_METHOD(PortalMembership::Terminate) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<PortalMembership>(info.Holder());
auto operation = new TerminateOperation();
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
}
-26
View File
@@ -1,26 +0,0 @@
#ifndef FRIDANODE_PORTAL_MEMBERSHIP_H
#define FRIDANODE_PORTAL_MEMBERSHIP_H
#include "glib_object.h"
#include <frida-core.h>
namespace frida {
class PortalMembership : public GLibObject {
public:
static void Init(v8::Local<v8::Object> exports, Runtime* runtime);
static v8::Local<v8::Object> New(gpointer handle, Runtime* runtime);
private:
explicit PortalMembership(FridaPortalMembership* handle, Runtime* runtime);
~PortalMembership();
static NAN_METHOD(New);
static NAN_METHOD(Terminate);
};
}
#endif
-401
View File
@@ -1,401 +0,0 @@
#include "portal_service.h"
#include "application.h"
#include "device.h"
#include "endpoint_parameters.h"
#include "operation.h"
#include "signals.h"
#include <cstring>
using std::strcmp;
using v8::DEFAULT;
using v8::Function;
using v8::FunctionTemplate;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::Persistent;
using v8::ReadOnly;
using v8::Value;
namespace frida {
PortalService::PortalService(FridaPortalService* handle,
Runtime* runtime)
: GLibObject(handle, runtime) {
g_object_ref(handle_);
}
PortalService::~PortalService() {
frida_unref(handle_);
}
void PortalService::Init(Local<Object> exports, Runtime* runtime) {
auto name = Nan::New("PortalService").ToLocalChecked();
auto tpl = CreateTemplate(name, PortalService::New, runtime);
auto instance_tpl = tpl->InstanceTemplate();
auto data = Local<Value>();
Nan::SetAccessor(instance_tpl, Nan::New("device").ToLocalChecked(), GetDevice,
0, data, DEFAULT, ReadOnly);
Nan::SetPrototypeMethod(tpl, "start", Start);
Nan::SetPrototypeMethod(tpl, "stop", Stop);
Nan::SetPrototypeMethod(tpl, "post", Post);
Nan::SetPrototypeMethod(tpl, "narrowcast", Narrowcast);
Nan::SetPrototypeMethod(tpl, "broadcast", Broadcast);
Nan::SetPrototypeMethod(tpl, "enumerateTags", EnumerateTags);
Nan::SetPrototypeMethod(tpl, "tag", Tag);
Nan::SetPrototypeMethod(tpl, "untag", Untag);
auto ctor = Nan::GetFunction(tpl).ToLocalChecked();
Nan::Set(exports, name, ctor);
}
NAN_METHOD(PortalService::New) {
if (!info.IsConstructCall()) {
Nan::ThrowError("Use the `new` keyword to create a new instance");
return;
}
if (info.Length() < 2) {
Nan::ThrowTypeError("Missing one or more arguments");
return;
}
auto runtime = GetRuntimeFromConstructorArgs(info);
auto cluster_params_value = info[0];
auto control_params_value = info[1];
FridaEndpointParameters* cluster_params = NULL;
FridaEndpointParameters* control_params = NULL;
bool valid = true;
if (!cluster_params_value->IsNull()) {
cluster_params =
EndpointParameters::TryParse(cluster_params_value, runtime);
if (cluster_params != NULL) {
g_object_ref(cluster_params);
} else {
Nan::ThrowTypeError("Bad argument, 'clusterParams' type mismatch");
valid = false;
}
} else {
cluster_params =
frida_endpoint_parameters_new(NULL, 0, NULL, NULL, NULL, NULL);
}
if (valid && !control_params_value->IsNull()) {
control_params =
EndpointParameters::TryParse(control_params_value, runtime);
if (control_params != NULL) {
g_object_ref(control_params);
} else {
Nan::ThrowTypeError("Bad argument, 'controlParams' type mismatch");
valid = false;
}
}
if (valid) {
auto handle = frida_portal_service_new(cluster_params, control_params);
auto wrapper = new PortalService(handle, runtime);
g_object_unref(handle);
auto obj = info.This();
wrapper->Wrap(obj);
Nan::Set(obj, Nan::New("signals").ToLocalChecked(),
Signals::New(handle, runtime, TransformSignal, runtime));
info.GetReturnValue().Set(obj);
}
g_clear_object(&control_params);
g_clear_object(&cluster_params);
}
NAN_PROPERTY_GETTER(PortalService::GetDevice) {
auto wrapper = ObjectWrap::Unwrap<PortalService>(info.Holder());
auto handle = wrapper->GetHandle<FridaPortalService>();
info.GetReturnValue().Set(
Device::New(frida_portal_service_get_device(handle), wrapper->runtime_));
}
namespace {
class StartOperation : public Operation<FridaPortalService> {
protected:
void Begin() {
frida_portal_service_start(handle_, cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
frida_portal_service_start_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
runtime_->GetUVContext()->IncreaseUsage();
return Nan::Undefined();
}
};
}
NAN_METHOD(PortalService::Start) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<PortalService>(info.Holder());
auto operation = new StartOperation();
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
namespace {
class StopOperation : public Operation<FridaPortalService> {
protected:
void Begin() {
frida_portal_service_stop(handle_, cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
frida_portal_service_stop_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
runtime_->GetUVContext()->DecreaseUsage();
return Nan::Undefined();
}
};
}
NAN_METHOD(PortalService::Stop) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<PortalService>(info.Holder());
auto operation = new StopOperation();
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
NAN_METHOD(PortalService::Post) {
auto wrapper = ObjectWrap::Unwrap<PortalService>(info.Holder());
auto num_args = info.Length();
if (num_args < 3) {
Nan::ThrowTypeError("Missing one or more arguments");
return;
}
guint connection_id;
if (!ValueToConnectionId(info[0], &connection_id))
return;
Nan::Utf8String message(wrapper->runtime_->ValueToJson(info[1]));
GBytes* data = NULL;
auto buffer = info[2];
if (!buffer->IsNull()) {
if (!node::Buffer::HasInstance(buffer)) {
Nan::ThrowTypeError("Expected a buffer");
return;
}
data = g_bytes_new(node::Buffer::Data(buffer),
node::Buffer::Length(buffer));
}
frida_portal_service_post(wrapper->GetHandle<FridaPortalService>(),
connection_id, *message, data);
g_bytes_unref(data);
}
NAN_METHOD(PortalService::Narrowcast) {
auto wrapper = ObjectWrap::Unwrap<PortalService>(info.Holder());
auto num_args = info.Length();
if (num_args < 3) {
Nan::ThrowTypeError("Missing one or more arguments");
return;
}
Nan::Utf8String tag(info[0]);
Nan::Utf8String message(wrapper->runtime_->ValueToJson(info[1]));
GBytes* data = NULL;
auto buffer = info[2];
if (!buffer->IsNull()) {
if (!node::Buffer::HasInstance(buffer)) {
Nan::ThrowTypeError("Expected a buffer");
return;
}
data = g_bytes_new(node::Buffer::Data(buffer),
node::Buffer::Length(buffer));
}
frida_portal_service_narrowcast(wrapper->GetHandle<FridaPortalService>(),
*tag, *message, data);
g_bytes_unref(data);
}
NAN_METHOD(PortalService::Broadcast) {
auto wrapper = ObjectWrap::Unwrap<PortalService>(info.Holder());
auto num_args = info.Length();
if (num_args < 2) {
Nan::ThrowTypeError("Missing one or more arguments");
return;
}
Nan::Utf8String message(wrapper->runtime_->ValueToJson(info[0]));
GBytes* data = NULL;
auto buffer = info[1];
if (!buffer->IsNull()) {
if (!node::Buffer::HasInstance(buffer)) {
Nan::ThrowTypeError("Expected a buffer");
return;
}
data = g_bytes_new(node::Buffer::Data(buffer),
node::Buffer::Length(buffer));
}
frida_portal_service_broadcast(wrapper->GetHandle<FridaPortalService>(),
*message, data);
g_bytes_unref(data);
}
namespace {
class EnumerateTagsOperation : public Operation<FridaPortalService> {
public:
EnumerateTagsOperation(guint connection_id)
: connection_id_(connection_id),
tags_(NULL),
n_(0) {
}
~EnumerateTagsOperation() {
g_strfreev(tags_);
}
protected:
void Begin() {
tags_ = frida_portal_service_enumerate_tags(handle_, connection_id_, &n_);
OnReady(G_OBJECT(handle_), NULL, this);
}
void End(GAsyncResult* result, GError** error) {
}
Local<Value> Result(Isolate* isolate) {
return Runtime::ValueFromStrv(tags_, n_);
}
private:
guint connection_id_;
gchar** tags_;
gint n_;
};
}
NAN_METHOD(PortalService::EnumerateTags) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<PortalService>(info.Holder());
if (info.Length() < 1) {
Nan::ThrowTypeError("Bad argument, expected a connection ID");
return;
}
guint connection_id;
if (!ValueToConnectionId(info[0], &connection_id))
return;
auto operation = new EnumerateTagsOperation(connection_id);
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
NAN_METHOD(PortalService::Tag) {
auto wrapper = ObjectWrap::Unwrap<PortalService>(info.Holder());
auto num_args = info.Length();
if (num_args < 2) {
Nan::ThrowTypeError("Missing one or more arguments");
return;
}
guint connection_id;
if (!ValueToConnectionId(info[0], &connection_id))
return;
Nan::Utf8String tag(info[1]);
frida_portal_service_tag(wrapper->GetHandle<FridaPortalService>(),
connection_id, *tag);
}
NAN_METHOD(PortalService::Untag) {
auto wrapper = ObjectWrap::Unwrap<PortalService>(info.Holder());
auto num_args = info.Length();
if (num_args < 2) {
Nan::ThrowTypeError("Missing one or more arguments");
return;
}
guint connection_id;
if (!ValueToConnectionId(info[0], &connection_id))
return;
Nan::Utf8String tag(info[1]);
frida_portal_service_untag(wrapper->GetHandle<FridaPortalService>(),
connection_id, *tag);
}
bool PortalService::ValueToConnectionId(Local<Value> value,
guint* connection_id) {
if (!value->IsNumber()) {
Nan::ThrowTypeError("Expected a connection ID");
return false;
}
auto id = Nan::To<uint32_t>(value).FromMaybe(0);
if (id == 0) {
Nan::ThrowTypeError("Expected a connection ID");
return false;
}
*connection_id = id;
return true;
}
Local<Value> PortalService::TransformSignal(const gchar* name, guint index,
const GValue* value, gpointer user_data) {
auto runtime = static_cast<Runtime*>(user_data);
if (index == 1 && (strcmp(name, "node-joined") == 0 ||
strcmp(name, "node-left") == 0))
return Application::New(g_value_get_object(value), runtime);
if (index == 1 && (strcmp(name, "authenticated") == 0 ||
strcmp(name, "message") == 0)) {
auto json = Nan::New(g_value_get_string(value)).ToLocalChecked();
return runtime->ValueFromJson(json);
}
return Local<Value>();
}
}
-40
View File
@@ -1,40 +0,0 @@
#ifndef FRIDANODE_PORTAL_SERVICE_H
#define FRIDANODE_PORTAL_SERVICE_H
#include "glib_object.h"
#include <frida-core.h>
namespace frida {
class PortalService : public GLibObject {
public:
static void Init(v8::Local<v8::Object> exports, Runtime* runtime);
private:
explicit PortalService(FridaPortalService* handle, Runtime* runtime);
~PortalService();
static NAN_METHOD(New);
static NAN_PROPERTY_GETTER(GetDevice);
static NAN_METHOD(Start);
static NAN_METHOD(Stop);
static NAN_METHOD(Post);
static NAN_METHOD(Narrowcast);
static NAN_METHOD(Broadcast);
static NAN_METHOD(EnumerateTags);
static NAN_METHOD(Tag);
static NAN_METHOD(Untag);
static bool ValueToConnectionId(v8::Local<v8::Value> value,
guint* connection_id);
static v8::Local<v8::Value> TransformSignal(const gchar* name, guint index,
const GValue* value, gpointer user_data);
};
}
#endif
-135
View File
@@ -1,135 +0,0 @@
#include "process.h"
#include <cstring>
#define PROCESS_DATA_CONSTRUCTOR "process:ctor"
using std::strcmp;
using v8::DEFAULT;
using v8::External;
using v8::Function;
using v8::Integer;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::Persistent;
using v8::ReadOnly;
using v8::String;
using v8::Value;
namespace frida {
Process::Process(FridaProcess* handle, Runtime* runtime)
: GLibObject(handle, runtime) {
g_object_ref(handle_);
}
Process::~Process() {
g_object_unref(handle_);
}
void Process::Init(Local<Object> exports, Runtime* runtime) {
auto isolate = Isolate::GetCurrent();
auto name = Nan::New("Process").ToLocalChecked();
auto tpl = CreateTemplate(name, Process::New, runtime);
auto instance_tpl = tpl->InstanceTemplate();
auto data = Local<Value>();
Nan::SetAccessor(instance_tpl, Nan::New("parameters").ToLocalChecked(),
GetParameters, 0, data, DEFAULT, ReadOnly);
Nan::SetAccessor(instance_tpl, Nan::New("name").ToLocalChecked(),
GetName, 0, data, DEFAULT, ReadOnly);
Nan::SetAccessor(instance_tpl, Nan::New("pid").ToLocalChecked(),
GetPid, 0, data, DEFAULT, ReadOnly);
auto ctor = Nan::GetFunction(tpl).ToLocalChecked();
Nan::Set(exports, name, ctor);
runtime->SetDataPointer(PROCESS_DATA_CONSTRUCTOR,
new Persistent<Function>(isolate, ctor));
}
Local<Object> Process::New(gpointer handle, Runtime* runtime) {
auto ctor = Nan::New<Function>(
*static_cast<Persistent<Function>*>(
runtime->GetDataPointer(PROCESS_DATA_CONSTRUCTOR)));
const int argc = 1;
Local<Value> argv[argc] = { Nan::New<External>(handle) };
return Nan::NewInstance(ctor, argc, argv).ToLocalChecked();
}
NAN_METHOD(Process::New) {
if (!info.IsConstructCall()) {
Nan::ThrowError("Use the `new` keyword to create a new instance");
return;
}
if (info.Length() != 1 || !info[0]->IsExternal()) {
Nan::ThrowTypeError("Bad argument, expected raw handle");
return;
}
auto runtime = GetRuntimeFromConstructorArgs(info);
auto handle = static_cast<FridaProcess*>(
Local<External>::Cast(info[0])->Value());
auto wrapper = new Process(handle, runtime);
auto obj = info.This();
wrapper->Wrap(obj);
info.GetReturnValue().Set(obj);
}
NAN_PROPERTY_GETTER(Process::GetPid) {
auto handle = ObjectWrap::Unwrap<Process>(
info.Holder())->GetHandle<FridaProcess>();
info.GetReturnValue().Set(Nan::New<Integer>(frida_process_get_pid(handle)));
}
NAN_PROPERTY_GETTER(Process::GetName) {
auto handle = ObjectWrap::Unwrap<Process>(
info.Holder())->GetHandle<FridaProcess>();
info.GetReturnValue().Set(
Nan::New(frida_process_get_name(handle)).ToLocalChecked());
}
NAN_PROPERTY_GETTER(Process::GetParameters) {
auto wrapper = ObjectWrap::Unwrap<Process>(info.Holder());
auto handle = wrapper->GetHandle<FridaProcess>();
GHashTable* parameters = frida_process_get_parameters(handle);
info.GetReturnValue().Set(ParseParameters(parameters));
}
Local<Value> Process::ParseParameters(GHashTable* dict) {
auto result = Nan::New<Object>();
GHashTableIter iter;
gpointer raw_key, raw_value;
g_hash_table_iter_init(&iter, dict);
while (g_hash_table_iter_next(&iter, &raw_key, &raw_value)) {
char* canonicalized_key =
Runtime::ParameterNameFromC(static_cast<char*>(raw_key));
GVariant* var_value = static_cast<GVariant*>(raw_value);
Local<String> key = Nan::New(canonicalized_key).ToLocalChecked();
Local<Value> value;
if (strcmp(canonicalized_key, "started") == 0 &&
g_variant_is_of_type(var_value, G_VARIANT_TYPE_STRING)) {
value = Runtime::ValueFromDatetime(g_variant_get_string(var_value, NULL));
} else {
value = Runtime::ValueFromVariant(var_value);
}
Nan::Set(result, key, value);
g_free(canonicalized_key);
}
return result;
}
}
-30
View File
@@ -1,30 +0,0 @@
#ifndef FRIDANODE_PROCESS_H
#define FRIDANODE_PROCESS_H
#include "glib_object.h"
#include <frida-core.h>
namespace frida {
class Process : public GLibObject {
public:
static void Init(v8::Local<v8::Object> exports, Runtime* runtime);
static v8::Local<v8::Object> New(gpointer handle, Runtime* runtime);
private:
explicit Process(FridaProcess* handle, Runtime* runtime);
~Process();
static NAN_METHOD(New);
static NAN_PROPERTY_GETTER(GetPid);
static NAN_PROPERTY_GETTER(GetName);
static NAN_PROPERTY_GETTER(GetParameters);
static v8::Local<v8::Value> ParseParameters(GHashTable* dict);
};
}
#endif
-139
View File
@@ -1,139 +0,0 @@
#include "relay.h"
#define RELAY_DATA_TEMPLATE "relay:tpl"
using v8::DEFAULT;
using v8::Function;
using v8::FunctionTemplate;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::Persistent;
using v8::ReadOnly;
using v8::Value;
namespace frida {
Relay::Relay(FridaRelay* handle, Runtime* runtime)
: GLibObject(handle, runtime) {
g_object_ref(handle_);
}
Relay::~Relay() {
g_object_unref(handle_);
}
void Relay::Init(Local<Object> exports, Runtime* runtime) {
auto isolate = Isolate::GetCurrent();
auto name = Nan::New("Relay").ToLocalChecked();
auto tpl = CreateTemplate(name, Relay::New, runtime);
auto instance_tpl = tpl->InstanceTemplate();
auto data = Local<Value>();
Nan::SetAccessor(instance_tpl, Nan::New("kind").ToLocalChecked(),
GetKind, 0, data, DEFAULT, ReadOnly);
Nan::SetAccessor(instance_tpl, Nan::New("password").ToLocalChecked(),
GetPassword, 0, data, DEFAULT, ReadOnly);
Nan::SetAccessor(instance_tpl, Nan::New("username").ToLocalChecked(),
GetUsername, 0, data, DEFAULT, ReadOnly);
Nan::SetAccessor(instance_tpl, Nan::New("address").ToLocalChecked(),
GetAddress, 0, data, DEFAULT, ReadOnly);
auto ctor = Nan::GetFunction(tpl).ToLocalChecked();
Nan::Set(exports, name, ctor);
runtime->SetDataPointer(RELAY_DATA_TEMPLATE,
new Persistent<FunctionTemplate>(isolate, tpl));
}
FridaRelay* Relay::TryParse(Local<Value> value, Runtime* runtime) {
if (!value->IsObject())
return NULL;
auto impl = Nan::Get(value.As<Object>(), Nan::New("impl").ToLocalChecked())
.ToLocalChecked();
if (!HasInstance(impl, runtime))
return NULL;
return ObjectWrap::Unwrap<Relay>(impl.As<Object>())->GetHandle<FridaRelay>();
}
bool Relay::HasInstance(Local<Value> value, Runtime* runtime) {
auto tpl = Nan::New<FunctionTemplate>(
*static_cast<Persistent<FunctionTemplate>*>(
runtime->GetDataPointer(RELAY_DATA_TEMPLATE)));
return tpl->HasInstance(value);
}
NAN_METHOD(Relay::New) {
if (!info.IsConstructCall()) {
Nan::ThrowError("Use the `new` keyword to create a new instance");
return;
}
if (info.Length() < 4) {
Nan::ThrowTypeError("Missing one or more arguments");
return;
}
auto address_value = info[0];
auto username_value = info[1];
auto password_value = info[2];
if (!address_value->IsString() ||
!username_value->IsString() ||
!password_value->IsString()) {
Nan::ThrowTypeError("Bad argument");
return;
}
Nan::Utf8String address(address_value);
Nan::Utf8String username(username_value);
Nan::Utf8String password(password_value);
FridaRelayKind kind;
if (!Runtime::ValueToEnum(info[3], FRIDA_TYPE_RELAY_KIND, &kind))
return;
auto runtime = GetRuntimeFromConstructorArgs(info);
auto handle = frida_relay_new(*address, *username, *password, kind);
auto wrapper = new Relay(handle, runtime);
g_object_unref(handle);
auto obj = info.This();
wrapper->Wrap(obj);
info.GetReturnValue().Set(obj);
}
NAN_PROPERTY_GETTER(Relay::GetAddress) {
auto handle = ObjectWrap::Unwrap<Relay>(
info.Holder())->GetHandle<FridaRelay>();
info.GetReturnValue().Set(
Nan::New(frida_relay_get_address(handle)).ToLocalChecked());
}
NAN_PROPERTY_GETTER(Relay::GetUsername) {
auto handle = ObjectWrap::Unwrap<Relay>(
info.Holder())->GetHandle<FridaRelay>();
info.GetReturnValue().Set(
Nan::New(frida_relay_get_username(handle)).ToLocalChecked());
}
NAN_PROPERTY_GETTER(Relay::GetPassword) {
auto handle = ObjectWrap::Unwrap<Relay>(
info.Holder())->GetHandle<FridaRelay>();
info.GetReturnValue().Set(
Nan::New(frida_relay_get_password(handle)).ToLocalChecked());
}
NAN_PROPERTY_GETTER(Relay::GetKind) {
auto handle = ObjectWrap::Unwrap<Relay>(
info.Holder())->GetHandle<FridaRelay>();
info.GetReturnValue().Set(Runtime::ValueFromEnum(
frida_relay_get_kind(handle), FRIDA_TYPE_RELAY_KIND));
}
}
-31
View File
@@ -1,31 +0,0 @@
#ifndef FRIDANODE_RELAY_H
#define FRIDANODE_RELAY_H
#include "glib_object.h"
#include <frida-core.h>
namespace frida {
class Relay : public GLibObject {
public:
static void Init(v8::Local<v8::Object> exports, Runtime* runtime);
static FridaRelay* TryParse(v8::Local<v8::Value> value, Runtime* runtime);
static bool HasInstance(v8::Local<v8::Value> value, Runtime* runtime);
private:
explicit Relay(FridaRelay* handle, Runtime* runtime);
~Relay();
static NAN_METHOD(New);
static NAN_PROPERTY_GETTER(GetAddress);
static NAN_PROPERTY_GETTER(GetUsername);
static NAN_PROPERTY_GETTER(GetPassword);
static NAN_PROPERTY_GETTER(GetKind);
};
}
#endif
-568
View File
@@ -1,568 +0,0 @@
#include "runtime.h"
#include <cstring>
#include <nan.h>
#ifdef G_OS_UNIX
# include <gio/gunixsocketaddress.h>
#endif
using std::strchr;
using v8::Array;
using v8::Boolean;
using v8::Date;
using v8::Function;
using v8::Isolate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::String;
using v8::Symbol;
using v8::Value;
namespace frida {
Runtime::Runtime(UVContext* uv_context, GLibContext* glib_context)
: uv_context_(uv_context),
glib_context_(glib_context),
data_(g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL)) {
auto isolate = Isolate::GetCurrent();
auto global = isolate->GetCurrentContext()->Global();
auto json_module = Local<Object>::Cast(
Nan::Get(global, Nan::New("JSON").ToLocalChecked()).ToLocalChecked());
auto json_stringify = Local<Function>::Cast(
Nan::Get(json_module,
Nan::New("stringify").ToLocalChecked()).ToLocalChecked());
auto json_parse = Local<Function>::Cast(
Nan::Get(json_module,
Nan::New("parse").ToLocalChecked()).ToLocalChecked());
json_module_.Reset(isolate, json_module);
json_stringify_.Reset(isolate, json_stringify);
json_parse_.Reset(isolate, json_parse);
}
Runtime::~Runtime() {
json_parse_.Reset();
json_stringify_.Reset();
json_module_.Reset();
g_hash_table_unref(data_);
delete glib_context_;
delete uv_context_;
}
UVContext* Runtime::GetUVContext() const {
return uv_context_;
}
GLibContext* Runtime::GetGLibContext() const {
return glib_context_;
}
void* Runtime::GetDataPointer(const char* id) {
return g_hash_table_lookup(data_, id);
}
void Runtime::SetDataPointer(const char* id, void* value) {
g_hash_table_insert(data_, const_cast<char*>(id), value);
}
Local<String> Runtime::ValueToJson(Local<Value> value) {
auto context = Isolate::GetCurrent()->GetCurrentContext();
auto module = Nan::New<Object>(json_module_);
auto stringify = Nan::New<Function>(json_stringify_);
Local<Value> argv[] = { value };
return Local<String>::Cast(
stringify->Call(context, module, 1, argv).ToLocalChecked());
}
Local<Value> Runtime::ValueFromJson(Local<String> json) {
auto context = Isolate::GetCurrent()->GetCurrentContext();
auto module = Nan::New<Object>(json_module_);
auto parse = Nan::New<Function>(json_parse_);
Local<Value> argv[] = { json };
return parse->Call(context, module, 1, argv).ToLocalChecked();
}
Local<Value> Runtime::ValueFromDatetime(const char* iso8601_text) {
GDateTime* dt = g_date_time_new_from_iso8601(iso8601_text, NULL);
if (dt == NULL)
return Nan::Null();
double unix_msec = static_cast<double>(g_date_time_to_unix(dt) * 1000) +
(static_cast<double>(g_date_time_get_microsecond(dt)) / 1000.0);
Local<Date> result = Nan::New<Date>(unix_msec).ToLocalChecked();
g_date_time_unref(dt);
return result;
}
bool Runtime::ValueToStrv(Local<Value> value, gchar*** strv, gint* length) {
if (!value->IsArray()) {
Nan::ThrowTypeError("Bad argument, expected an array of strings");
return false;
}
auto array = Local<Array>::Cast(value);
uint32_t n = array->Length();
gchar** elements = g_new0(gchar*, n + 1);
for (uint32_t i = 0; i != n; i++) {
auto element_value = Nan::Get(array, i).ToLocalChecked();
if (!element_value->IsString()) {
g_strfreev(elements);
Nan::ThrowTypeError("Bad argument, expected an array of strings only");
return false;
}
Nan::Utf8String element(element_value);
elements[i] = g_strdup(*element);
}
*strv = elements;
*length = n;
return true;
}
Local<Value> Runtime::ValueFromStrv(gchar* const* strv, gint length) {
if (strv == NULL)
return Nan::Null();
auto result = Nan::New<Array>(length);
for (gint i = 0; i != length; i++)
Nan::Set(result, i, Nan::New(strv[i]).ToLocalChecked());
return result;
}
bool Runtime::ValueToEnvp(Local<Value> value, gchar*** envp, gint* length) {
auto isolate = Isolate::GetCurrent();
auto context = isolate->GetCurrentContext();
if (!value->IsObject()) {
Nan::ThrowTypeError("Bad argument, expected an object");
return false;
}
auto object = Local<Object>::Cast(value);
Local<Array> names(object->GetOwnPropertyNames(context).ToLocalChecked());
uint32_t n = names->Length();
gchar** elements = g_new0(gchar*, n + 1);
for (uint32_t i = 0; i != n; i++) {
auto name = Nan::Get(names, i).ToLocalChecked();
auto value = Nan::Get(object, name).ToLocalChecked();
Nan::Utf8String name_str(name);
Nan::Utf8String value_str(value);
elements[i] = g_strconcat(*name_str, "=", *value_str, NULL);
}
*envp = elements;
*length = n;
return true;
}
Local<Value> Runtime::ValueFromEnvp(gchar* const* envp, gint length) {
if (envp == NULL)
return Nan::Null();
auto result = Nan::New<Object>();
for (gint i = 0; i != length; i++) {
auto tokens = g_strsplit(envp[i], "=", 2);
if (g_strv_length(tokens) == 2) {
Nan::Set(result, Nan::New(tokens[0]).ToLocalChecked(),
Nan::New(tokens[1]).ToLocalChecked());
}
g_strfreev(tokens);
}
return result;
}
bool Runtime::ValueToEnum(Local<Value> value, GType type, gpointer result) {
if (!value->IsString()) {
Nan::ThrowTypeError("Bad argument, expected a string");
return false;
}
Nan::Utf8String str(value);
bool success = false;
auto enum_class = static_cast<GEnumClass*>(g_type_class_ref(type));
auto enum_value = g_enum_get_value_by_nick(enum_class, *str);
if (enum_value != NULL) {
*((gint*) result) = enum_value->value;
success = true;
} else {
auto message = g_string_sized_new(128);
g_string_append_printf(message,
"Enum type %s does not have a value named '%s', it only has: ",
ClassNameFromC(g_type_name(type)), *str);
for (guint i = 0; i != enum_class->n_values; i++) {
if (i != 0)
g_string_append(message, ", ");
g_string_append_c(message, '\'');
g_string_append(message, enum_class->values[i].value_nick);
g_string_append_c(message, '\'');
}
Nan::ThrowTypeError(message->str);
g_string_free(message, TRUE);
}
g_type_class_unref(enum_class);
return success;
}
Local<String> Runtime::ValueFromEnum(gint value, GType type) {
auto enum_class = static_cast<GEnumClass*>(g_type_class_ref(type));
auto result = Nan::New(g_enum_get_value(enum_class, value)->value_nick)
.ToLocalChecked();
g_type_class_unref(enum_class);
return result;
}
Local<Value> Runtime::ValueFromParametersDict(GHashTable* dict) {
auto result = Nan::New<Object>();
GHashTableIter iter;
gpointer raw_key, raw_value;
g_hash_table_iter_init(&iter, dict);
while (g_hash_table_iter_next(&iter, &raw_key, &raw_value)) {
char* canonicalized_key = ParameterNameFromC(static_cast<char*>(raw_key));
Local<String> key = Nan::New(canonicalized_key).ToLocalChecked();
Local<Value> value = ValueFromVariant(static_cast<GVariant*>(raw_value));
Nan::Set(result, key, value);
g_free(canonicalized_key);
}
return result;
}
GVariant* Runtime::ValueToVariant(Local<Value> value) {
if (value->IsString()) {
Nan::Utf8String str(value);
return g_variant_new_string(*str);
}
if (value->IsNumber()) {
return g_variant_new_int64(
static_cast<gint64>(Local<Number>::Cast(value)->Value()));
}
if (value->IsBoolean()) {
return g_variant_new_boolean(Local<Boolean>::Cast(value)->Value());
}
if (node::Buffer::HasInstance(value)) {
auto size = node::Buffer::Length(value);
auto copy = g_memdup2(node::Buffer::Data(value), size);
return g_variant_new_from_data(G_VARIANT_TYPE_BYTESTRING, copy, size, TRUE,
g_free, copy);
}
if (value->IsArray()) {
auto array = Local<Array>::Cast(value);
uint32_t n = array->Length();
if (n == 2) {
auto first = Nan::Get(array, 0).ToLocalChecked();
if (first->IsSymbol()) {
auto sym = first.As<Symbol>();
auto desc = sym->Description(
#if V8_MAJOR_VERSION > 9 || (V8_MAJOR_VERSION == 9 && V8_MINOR_VERSION >= 5)
Isolate::GetCurrent()
#endif
);
Nan::Utf8String type(desc);
auto val = ValueToVariant(Nan::Get(array, 1).ToLocalChecked());
if (val == NULL) {
return NULL;
}
GVariant* t[2] = { g_variant_new_string(*type), val };
return g_variant_new_tuple(t, G_N_ELEMENTS(t));
}
}
GVariantBuilder builder;
g_variant_builder_init(&builder, G_VARIANT_TYPE ("av"));
for (uint32_t i = 0; i != n; i++) {
auto v = ValueToVariant(Nan::Get(array, i).ToLocalChecked());
if (v == NULL) {
g_variant_builder_clear(&builder);
return NULL;
}
g_variant_builder_add(&builder, "v", v);
}
return g_variant_builder_end(&builder);
}
if (value->IsObject()) {
GVariantBuilder builder;
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
auto isolate = Isolate::GetCurrent();
auto context = isolate->GetCurrentContext();
auto object = Local<Object>::Cast(value);
Local<Array> names(object->GetOwnPropertyNames(context).ToLocalChecked());
uint32_t n = names->Length();
for (uint32_t i = 0; i != n; i++) {
auto key = Nan::Get(names, i).ToLocalChecked();
auto val = Nan::Get(object, key).ToLocalChecked();
if (val->IsUndefined()) {
continue;
}
Nan::Utf8String k(key);
auto v = ValueToVariant(val);
if (v == NULL) {
g_variant_builder_clear(&builder);
return NULL;
}
g_variant_builder_add(&builder, "{sv}", *k, v);
}
return g_variant_builder_end(&builder);
}
Nan::ThrowTypeError("Bad argument, expected value serializable to GVariant");
return NULL;
}
Local<Value> Runtime::ValueFromVariant(GVariant* v) {
switch (g_variant_classify(v)) {
case G_VARIANT_CLASS_STRING:
return Nan::New<String>(g_variant_get_string(v, NULL)).ToLocalChecked();
case G_VARIANT_CLASS_INT64:
return Nan::New<Number>(static_cast<double>(g_variant_get_int64(v)));
case G_VARIANT_CLASS_UINT64:
return Nan::New<Number>(static_cast<double>(g_variant_get_uint64(v)));
case G_VARIANT_CLASS_DOUBLE:
return Nan::New<Number>(static_cast<double>(g_variant_get_double(v)));
case G_VARIANT_CLASS_BOOLEAN:
return Nan::New<Boolean>(static_cast<bool>(g_variant_get_boolean(v)));
case G_VARIANT_CLASS_ARRAY:
if (g_variant_is_of_type(v, G_VARIANT_TYPE("ay"))) {
return ValueFromVariantByteArray(v);
}
if (g_variant_is_of_type(v, G_VARIANT_TYPE_VARDICT)) {
return ValueFromVariantDict(v);
}
if (g_variant_is_of_type(v, G_VARIANT_TYPE_ARRAY)) {
return ValueFromVariantArray(v);
}
break;
case G_VARIANT_CLASS_TUPLE:
return Nan::Undefined();
default:
break;
}
return Nan::Null();
}
Local<Value> Runtime::ValueFromVariantByteArray(GVariant* v) {
gsize size;
gconstpointer data = g_variant_get_fixed_array(v, &size, sizeof(guint8));
return Nan::CopyBuffer(static_cast<const char*>(data), size).ToLocalChecked();
}
Local<Value> Runtime::ValueFromVariantDict(GVariant* v) {
auto dict = Nan::New<Object>();
GVariantIter iter;
gchar* raw_key;
GVariant* raw_value;
g_variant_iter_init(&iter, v);
while (g_variant_iter_next(&iter, "{sv}", &raw_key, &raw_value)) {
char* canonicalized_key = ParameterNameFromC(raw_key);
Local<String> key = Nan::New(canonicalized_key).ToLocalChecked();
Local<Value> value = ValueFromVariant(raw_value);
Nan::Set(dict, key, value);
g_free(canonicalized_key);
g_variant_unref(raw_value);
g_free(raw_key);
}
return dict;
}
Local<Value> Runtime::ValueFromVariantArray(GVariant* v) {
GVariantIter iter;
g_variant_iter_init(&iter, v);
auto array = Nan::New<Array>(g_variant_iter_n_children(&iter));
GVariant* child;
for (int i = 0; (child = g_variant_iter_next_value(&iter)) != NULL; i++) {
if (g_variant_is_of_type(child, G_VARIANT_TYPE_VARIANT)) {
GVariant* inner = g_variant_get_variant(child);
g_variant_unref(child);
child = inner;
}
Nan::Set(array, i, ValueFromVariant(child));
g_variant_unref(child);
}
return array;
}
Local<Object> Runtime::ValueFromSocketAddress(GSocketAddress* address) {
auto result = Nan::New<Object>();
if (G_IS_INET_SOCKET_ADDRESS(address)) {
GSocketFamily family = g_socket_address_get_family(address);
GInetSocketAddress* sa = G_INET_SOCKET_ADDRESS(address);
Nan::Set(result,
Nan::New("family").ToLocalChecked(),
Nan::New((family == G_SOCKET_FAMILY_IPV6) ? "ipv6" : "ipv4")
.ToLocalChecked());
gchar* host = g_inet_address_to_string(
g_inet_socket_address_get_address(sa));
Nan::Set(result,
Nan::New("address").ToLocalChecked(),
Nan::New(host).ToLocalChecked());
g_free(host);
Nan::Set(result, Nan::New("port").ToLocalChecked(),
Nan::New(static_cast<uint32_t>(g_inet_socket_address_get_port(sa))));
if (family == G_SOCKET_FAMILY_IPV6) {
Nan::Set(result,
Nan::New("flowlabel").ToLocalChecked(),
Nan::New(g_inet_socket_address_get_flowinfo(sa)));
Nan::Set(result,
Nan::New("scopeid").ToLocalChecked(),
Nan::New(g_inet_socket_address_get_scope_id(sa)));
}
}
#ifdef G_OS_UNIX
if (G_IS_UNIX_SOCKET_ADDRESS(address)) {
GUnixSocketAddress* sa = G_UNIX_SOCKET_ADDRESS(address);
switch (g_unix_socket_address_get_address_type(sa)) {
case G_UNIX_SOCKET_ADDRESS_ANONYMOUS: {
Nan::Set(result,
Nan::New("family").ToLocalChecked(),
Nan::New("unix:anonymous").ToLocalChecked());
break;
}
case G_UNIX_SOCKET_ADDRESS_PATH: {
Nan::Set(result,
Nan::New("family").ToLocalChecked(),
Nan::New("unix:path").ToLocalChecked());
gchar* path = g_filename_to_utf8(g_unix_socket_address_get_path(sa), -1,
NULL, NULL, NULL);
Nan::Set(result,
Nan::New("path").ToLocalChecked(),
Nan::New(path).ToLocalChecked());
g_free(path);
break;
}
case G_UNIX_SOCKET_ADDRESS_ABSTRACT:
case G_UNIX_SOCKET_ADDRESS_ABSTRACT_PADDED: {
Nan::Set(result,
Nan::New("family").ToLocalChecked(),
Nan::New("unix:abstract").ToLocalChecked());
Nan::Set(result,
Nan::New("path").ToLocalChecked(),
Nan::CopyBuffer(
g_unix_socket_address_get_path(sa),
g_unix_socket_address_get_path_len(sa)).ToLocalChecked());
break;
}
default:
break;
}
}
#endif
return result;
}
bool Runtime::ValueToCertificate(Local<Value> value,
GTlsCertificate** certificate) {
if (!value->IsString()) {
Nan::ThrowTypeError("Bad argument, expected a string");
return false;
}
Nan::Utf8String str(value);
const char* cstr = *str;
GError* error = NULL;
if (strchr(cstr, '\n') != NULL)
*certificate = g_tls_certificate_new_from_pem(cstr, -1, &error);
else
*certificate = g_tls_certificate_new_from_file(cstr, &error);
if (error != NULL) {
Nan::ThrowError(Nan::Error(error->message));
g_error_free(error);
return false;
}
return true;
}
const char* Runtime::ClassNameFromC(const char* cname) {
if (g_str_has_prefix(cname, "Frida"))
return cname + 5;
return cname;
}
char* Runtime::ParameterNameFromC(const char* cname) {
auto name = g_string_new("");
bool need_uppercase = false;
for (const char* cursor = cname; *cursor != '\0'; cursor++) {
char ch = *cursor;
if (ch == '-') {
need_uppercase = true;
} else {
g_string_append_c(name, need_uppercase ? g_ascii_toupper(ch) : ch);
need_uppercase = false;
}
}
return g_string_free(name, FALSE);
}
}
-69
View File
@@ -1,69 +0,0 @@
#ifndef FRIDANODE_RUNTIME_H
#define FRIDANODE_RUNTIME_H
#include "glib_context.h"
#include "uv_context.h"
#include <v8.h>
namespace frida {
class Runtime {
public:
Runtime(UVContext* uv_context, GLibContext* glib_context);
~Runtime();
UVContext* GetUVContext() const;
GLibContext* GetGLibContext() const;
void* GetDataPointer(const char* id);
void SetDataPointer(const char* id, void* value);
v8::Local<v8::String> ValueToJson(v8::Local<v8::Value> value);
v8::Local<v8::Value> ValueFromJson(v8::Local<v8::String> json);
static v8::Local<v8::Value> ValueFromDatetime(const char* iso8601_text);
static bool ValueToStrv(v8::Local<v8::Value> value, gchar*** strv,
gint* length);
static v8::Local<v8::Value> ValueFromStrv(gchar* const* strv, gint length);
static bool ValueToEnvp(v8::Local<v8::Value> value, gchar*** envp,
gint* length);
static v8::Local<v8::Value> ValueFromEnvp(gchar* const* envp, gint length);
static bool ValueToEnum(v8::Local<v8::Value> value, GType type,
gpointer result);
static v8::Local<v8::String> ValueFromEnum(gint value, GType type);
static v8::Local<v8::Value> ValueFromParametersDict(GHashTable* dict);
static GVariant* ValueToVariant(v8::Local<v8::Value> value);
static v8::Local<v8::Value> ValueFromVariant(GVariant* v);
static v8::Local<v8::Object> ValueFromSocketAddress(GSocketAddress* address);
static bool ValueToCertificate(v8::Local<v8::Value> value,
GTlsCertificate** certificate);
static const char* ClassNameFromC(const char* cname);
static char* ParameterNameFromC(const char* cname);
private:
static v8::Local<v8::Value> ValueFromVariantByteArray(GVariant* v);
static v8::Local<v8::Value> ValueFromVariantDict(GVariant* v);
static v8::Local<v8::Value> ValueFromVariantArray(GVariant* v);
UVContext* uv_context_;
GLibContext* glib_context_;
GHashTable* data_;
v8::Persistent<v8::Object> json_module_;
v8::Persistent<v8::Function> json_stringify_;
v8::Persistent<v8::Function> json_parse_;
};
}
#endif
-301
View File
@@ -1,301 +0,0 @@
#include "script.h"
#include "operation.h"
#include "signals.h"
#include "usage_monitor.h"
#include <cstring>
#define SCRIPT_DATA_CONSTRUCTOR "script:ctor"
using std::strcmp;
using v8::DEFAULT;
using v8::External;
using v8::Function;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::Persistent;
using v8::ReadOnly;
using v8::String;
using v8::Value;
namespace frida {
Script::Script(FridaScript* handle, Runtime* runtime)
: GLibObject(handle, runtime) {
g_object_ref(handle_);
}
Script::~Script() {
frida_unref(handle_);
}
void Script::Init(Local<Object> exports, Runtime* runtime) {
auto isolate = Isolate::GetCurrent();
auto name = Nan::New("Script").ToLocalChecked();
auto tpl = CreateTemplate(name, New, runtime);
auto instance_tpl = tpl->InstanceTemplate();
auto data = Local<Value>();
Nan::SetAccessor(instance_tpl, Nan::New("isDestroyed").ToLocalChecked(),
IsDestroyed, 0, data, DEFAULT, ReadOnly);
Nan::SetPrototypeMethod(tpl, "load", Load);
Nan::SetPrototypeMethod(tpl, "unload", Unload);
Nan::SetPrototypeMethod(tpl, "eternalize", Eternalize);
Nan::SetPrototypeMethod(tpl, "post", Post);
Nan::SetPrototypeMethod(tpl, "enableDebugger", EnableDebugger);
Nan::SetPrototypeMethod(tpl, "disableDebugger", DisableDebugger);
auto ctor = Nan::GetFunction(tpl).ToLocalChecked();
Nan::Set(exports, name, ctor);
runtime->SetDataPointer(SCRIPT_DATA_CONSTRUCTOR,
new Persistent<Function>(isolate, ctor));
}
Local<Object> Script::New(gpointer handle, Runtime* runtime) {
auto ctor = Nan::New<Function>(
*static_cast<Persistent<Function>*>(
runtime->GetDataPointer(SCRIPT_DATA_CONSTRUCTOR)));
const int argc = 1;
Local<Value> argv[argc] = { Nan::New<External>(handle) };
return Nan::NewInstance(ctor, argc, argv).ToLocalChecked();
}
NAN_METHOD(Script::New) {
if (!info.IsConstructCall()) {
Nan::ThrowError("Use the `new` keyword to create a new instance");
return;
}
if (info.Length() != 1 || !info[0]->IsExternal()) {
Nan::ThrowTypeError("Bad argument, expected raw handle");
return;
}
auto runtime = GetRuntimeFromConstructorArgs(info);
auto handle = static_cast<FridaScript*>(
Local<External>::Cast(info[0])->Value());
auto wrapper = new Script(handle, runtime);
auto obj = info.This();
wrapper->Wrap(obj);
Nan::Set(obj, Nan::New("signals").ToLocalChecked(),
Signals::New(handle, runtime, TransformMessageSignal, runtime));
auto monitor =
new UsageMonitor<FridaScript>(frida_script_is_destroyed, "destroyed");
monitor->Enable(wrapper);
info.GetReturnValue().Set(obj);
}
NAN_PROPERTY_GETTER(Script::IsDestroyed) {
auto handle = ObjectWrap::Unwrap<Script>(
info.Holder())->GetHandle<FridaScript>();
info.GetReturnValue().Set(
Nan::New(static_cast<bool>(frida_script_is_destroyed(handle))));
}
namespace {
class LoadOperation : public Operation<FridaScript> {
protected:
void Begin() {
frida_script_load(handle_, cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
frida_script_load_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
return Nan::Undefined();
}
};
}
NAN_METHOD(Script::Load) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<Script>(info.Holder());
auto operation = new LoadOperation();
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
namespace {
class UnloadOperation : public Operation<FridaScript> {
protected:
void Begin() {
frida_script_unload(handle_, cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
frida_script_unload_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
return Nan::Undefined();
}
};
}
NAN_METHOD(Script::Unload) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<Script>(info.Holder());
auto operation = new UnloadOperation();
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
namespace {
class EternalizeOperation : public Operation<FridaScript> {
protected:
void Begin() {
frida_script_eternalize(handle_, cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
frida_script_eternalize_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
return Nan::Undefined();
}
};
}
NAN_METHOD(Script::Eternalize) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<Script>(info.Holder());
auto operation = new EternalizeOperation();
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
NAN_METHOD(Script::Post) {
auto wrapper = ObjectWrap::Unwrap<Script>(info.Holder());
auto num_args = info.Length();
if (num_args < 2) {
Nan::ThrowTypeError("Expected message and data");
return;
}
Nan::Utf8String message(wrapper->runtime_->ValueToJson(info[0]));
GBytes* data = NULL;
auto buffer = info[1];
if (!buffer->IsNull()) {
if (!node::Buffer::HasInstance(buffer)) {
Nan::ThrowTypeError("Expected a buffer");
return;
}
data = g_bytes_new(node::Buffer::Data(buffer),
node::Buffer::Length(buffer));
}
frida_script_post(wrapper->GetHandle<FridaScript>(), *message, data);
g_bytes_unref(data);
}
namespace {
class EnableDebuggerOperation : public Operation<FridaScript> {
public:
EnableDebuggerOperation(guint16 port) : port_(port) {
}
protected:
void Begin() {
frida_script_enable_debugger(handle_, port_, cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
frida_script_enable_debugger_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
return Nan::Undefined();
}
private:
guint16 port_;
};
}
NAN_METHOD(Script::EnableDebugger) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<Script>(info.Holder());
if (info.Length() < 1 || !info[0]->IsNumber()) {
Nan::ThrowTypeError("Bad argument, expected port number");
return;
}
auto port = Nan::To<int32_t>(info[0]).FromMaybe(-1);
if (port < 0 || port >= 65536) {
Nan::ThrowTypeError("Bad argument, expected port number");
return;
}
auto operation = new EnableDebuggerOperation(static_cast<guint16>(port));
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
namespace {
class DisableDebuggerOperation : public Operation<FridaScript> {
protected:
void Begin() {
frida_script_disable_debugger(handle_, cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
frida_script_disable_debugger_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
return Nan::Undefined();
}
};
}
NAN_METHOD(Script::DisableDebugger) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<Script>(info.Holder());
auto operation = new DisableDebuggerOperation();
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
Local<Value> Script::TransformMessageSignal(const gchar* name, guint index,
const GValue* value, gpointer user_data) {
if (index != 0 || strcmp(name, "message") != 0)
return Local<Value>();
auto runtime = static_cast<Runtime*>(user_data);
auto json = Nan::New(g_value_get_string(value)).ToLocalChecked();
return runtime->ValueFromJson(json);
}
}
-36
View File
@@ -1,36 +0,0 @@
#ifndef FRIDANODE_SCRIPT_H
#define FRIDANODE_SCRIPT_H
#include "glib_object.h"
#include <frida-core.h>
namespace frida {
class Script : public GLibObject {
public:
static void Init(v8::Local<v8::Object> exports, Runtime* runtime);
static v8::Local<v8::Object> New(gpointer handle, Runtime* runtime);
private:
explicit Script(FridaScript* handle, Runtime* runtime);
~Script();
static NAN_METHOD(New);
static NAN_PROPERTY_GETTER(IsDestroyed);
static NAN_METHOD(Load);
static NAN_METHOD(Unload);
static NAN_METHOD(Eternalize);
static NAN_METHOD(Post);
static NAN_METHOD(EnableDebugger);
static NAN_METHOD(DisableDebugger);
static v8::Local<v8::Value> TransformMessageSignal(const gchar* name,
guint index, const GValue* value, gpointer user_data);
};
}
#endif
-224
View File
@@ -1,224 +0,0 @@
#include "service.h"
#include "operation.h"
#include "signals.h"
#include "usage_monitor.h"
#include <cstring>
#define SERVICE_DATA_CONSTRUCTOR "service:ctor"
using std::strcmp;
using v8::External;
using v8::Function;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::Persistent;
using v8::Value;
namespace frida {
Service::Service(FridaService* handle, Runtime* runtime)
: GLibObject(handle, runtime),
usage_monitor_created_(false) {
g_object_ref(handle_);
}
Service::~Service() {
g_object_unref(handle_);
}
void Service::Init(Local<Object> exports, Runtime* runtime) {
auto isolate = Isolate::GetCurrent();
auto name = Nan::New("Service").ToLocalChecked();
auto tpl = CreateTemplate(name, New, runtime);
Nan::SetPrototypeMethod(tpl, "activate", Activate);
Nan::SetPrototypeMethod(tpl, "cancel", Cancel);
Nan::SetPrototypeMethod(tpl, "request", Request);
auto ctor = Nan::GetFunction(tpl).ToLocalChecked();
Nan::Set(exports, name, ctor);
runtime->SetDataPointer(SERVICE_DATA_CONSTRUCTOR,
new Persistent<Function>(isolate, ctor));
}
Local<Object> Service::New(gpointer handle, Runtime* runtime) {
auto ctor = Nan::New<Function>(
*static_cast<Persistent<Function>*>(
runtime->GetDataPointer(SERVICE_DATA_CONSTRUCTOR)));
const int argc = 1;
Local<Value> argv[argc] = { Nan::New<External>(handle) };
return Nan::NewInstance(ctor, argc, argv).ToLocalChecked();
}
NAN_METHOD(Service::New) {
if (!info.IsConstructCall()) {
Nan::ThrowError("Use the `new` keyword to create a new instance");
return;
}
if (info.Length() != 1 || !info[0]->IsExternal()) {
Nan::ThrowTypeError("Bad argument, expected raw handle");
return;
}
auto runtime = GetRuntimeFromConstructorArgs(info);
auto handle = static_cast<FridaService*>(
Local<External>::Cast(info[0])->Value());
auto wrapper = new Service(handle, runtime);
auto obj = info.This();
wrapper->Wrap(obj);
auto signals_obj = Signals::New(handle, runtime, TransformSignal, runtime);
Nan::Set(obj, Nan::New("signals").ToLocalChecked(), signals_obj);
auto signals_wrapper = ObjectWrap::Unwrap<Signals>(signals_obj);
signals_wrapper->SetConnectCallback(OnConnect, wrapper);
info.GetReturnValue().Set(obj);
}
namespace {
class ActivateOperation : public Operation<FridaService> {
protected:
void Begin() {
frida_service_activate(handle_, cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
frida_service_activate_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
return Nan::Undefined();
}
};
}
NAN_METHOD(Service::Activate) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<Service>(info.Holder());
auto operation = new ActivateOperation();
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
namespace {
class CancelOperation : public Operation<FridaService> {
protected:
void Begin() {
frida_service_cancel(handle_, cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
frida_service_cancel_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
return Nan::Undefined();
}
};
}
NAN_METHOD(Service::Cancel) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<Service>(info.Holder());
auto operation = new CancelOperation();
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
namespace {
class RequestOperation : public Operation<FridaService> {
public:
RequestOperation(GVariant* parameters)
: parameters_(parameters) {
}
~RequestOperation() {
g_variant_unref(parameters_);
}
protected:
void Begin() {
frida_service_request(handle_, parameters_, cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
response_ = frida_service_request_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
auto wrapper = Runtime::ValueFromVariant(response_);
g_variant_unref(response_);
return wrapper;
}
private:
GVariant* parameters_;
GVariant* response_;
};
}
NAN_METHOD(Service::Request) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<Service>(info.Holder());
if (info.Length() == 0) {
Nan::ThrowTypeError("Expected a parameters value");
return;
}
auto parameters = Runtime::ValueToVariant(info[0]);
if (parameters == NULL) {
return;
}
auto operation = new RequestOperation(parameters);
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
Local<Value> Service::TransformSignal(const gchar* name, guint index,
const GValue* value, gpointer user_data) {
if (index != 0 || strcmp(name, "message") != 0)
return Local<Value>();
return Runtime::ValueFromVariant(g_value_get_variant(value));
}
void Service::OnConnect(const gchar* name, gpointer user_data) {
auto wrapper = static_cast<Service*>(user_data);
if (ShouldStayAliveToEmit(name))
wrapper->EnsureUsageMonitorCreated();
}
bool Service::ShouldStayAliveToEmit(const gchar* name) {
return strcmp(name, "close") == 0 || strcmp(name, "message") == 0;
}
void Service::EnsureUsageMonitorCreated() {
if (!usage_monitor_created_) {
usage_monitor_created_ = true;
auto monitor =
new UsageMonitor<FridaService>(frida_service_is_closed, "close");
monitor->Enable(this);
}
}
}
-36
View File
@@ -1,36 +0,0 @@
#ifndef FRIDANODE_SERVICE_H
#define FRIDANODE_SERVICE_H
#include "glib_object.h"
#include <frida-core.h>
namespace frida {
class Service : public GLibObject {
public:
static void Init(v8::Local<v8::Object> exports, Runtime* runtime);
static v8::Local<v8::Object> New(gpointer handle, Runtime* runtime);
private:
explicit Service(FridaService* handle, Runtime* runtime);
~Service();
static NAN_METHOD(New);
static NAN_METHOD(Activate);
static NAN_METHOD(Cancel);
static NAN_METHOD(Request);
static v8::Local<v8::Value> TransformSignal(const gchar* name, guint index,
const GValue* value, gpointer user_data);
static void OnConnect(const gchar* signal, gpointer user_data);
static bool ShouldStayAliveToEmit(const gchar* signal);
void EnsureUsageMonitorCreated();
bool usage_monitor_created_;
};
}
#endif
-860
View File
@@ -1,860 +0,0 @@
#include "session.h"
#include "crash.h"
#include "operation.h"
#include "portal_membership.h"
#include "relay.h"
#include "script.h"
#include "signals.h"
#include "usage_monitor.h"
#include <cstring>
#define SESSION_DATA_CONSTRUCTOR "session:ctor"
using std::strcmp;
using v8::Array;
using v8::DEFAULT;
using v8::External;
using v8::Function;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::Persistent;
using v8::ReadOnly;
using v8::String;
using v8::Uint32;
using v8::Value;
namespace frida {
static FridaScriptOptions* ParseScriptOptions(Local<Value> name_value,
Local<Value> snapshot_value, Local<Value> runtime_value);
static FridaSnapshotOptions* ParseSnapshotOptions(
Local<Value> warmup_script_value, Local<Value> runtime_value);
static void UnrefGBytes(char* data, void* hint);
Session::Session(FridaSession* handle, Runtime* runtime)
: GLibObject(handle, runtime) {
g_object_ref(handle_);
}
Session::~Session() {
frida_unref(handle_);
}
void Session::Init(Local<Object> exports, Runtime* runtime) {
auto isolate = Isolate::GetCurrent();
auto name = Nan::New("Session").ToLocalChecked();
auto tpl = CreateTemplate(name, Session::New, runtime);
auto instance_tpl = tpl->InstanceTemplate();
auto data = Local<Value>();
Nan::SetAccessor(instance_tpl, Nan::New("isDetached").ToLocalChecked(),
IsDetached, 0, data, DEFAULT, ReadOnly);
Nan::SetAccessor(instance_tpl, Nan::New("persistTimeout").ToLocalChecked(),
GetPersistTimeout, 0, data, DEFAULT, ReadOnly);
Nan::SetAccessor(instance_tpl, Nan::New("pid").ToLocalChecked(), GetPid, 0,
data, DEFAULT, ReadOnly);
Nan::SetPrototypeMethod(tpl, "detach", Detach);
Nan::SetPrototypeMethod(tpl, "resume", Resume);
Nan::SetPrototypeMethod(tpl, "enableChildGating", EnableChildGating);
Nan::SetPrototypeMethod(tpl, "disableChildGating", DisableChildGating);
Nan::SetPrototypeMethod(tpl, "createScript", CreateScript);
Nan::SetPrototypeMethod(tpl, "createScriptFromBytes", CreateScriptFromBytes);
Nan::SetPrototypeMethod(tpl, "compileScript", CompileScript);
Nan::SetPrototypeMethod(tpl, "snapshotScript", SnapshotScript);
Nan::SetPrototypeMethod(tpl, "setupPeerConnection", SetupPeerConnection);
Nan::SetPrototypeMethod(tpl, "joinPortal", JoinPortal);
auto ctor = Nan::GetFunction(tpl).ToLocalChecked();
Nan::Set(exports, name, ctor);
runtime->SetDataPointer(SESSION_DATA_CONSTRUCTOR,
new Persistent<Function>(isolate, ctor));
}
Local<Object> Session::New(gpointer handle, Runtime* runtime) {
auto ctor = Nan::New<Function>(
*static_cast<Persistent<Function>*>(
runtime->GetDataPointer(SESSION_DATA_CONSTRUCTOR)));
const int argc = 1;
Local<Value> argv[argc] = { Nan::New<External>(handle) };
return Nan::NewInstance(ctor, argc, argv).ToLocalChecked();
}
NAN_METHOD(Session::New) {
if (!info.IsConstructCall()) {
Nan::ThrowError("Use the `new` keyword to create a new instance");
return;
}
if (info.Length() != 1 || !info[0]->IsExternal()) {
Nan::ThrowTypeError("Bad argument, expected raw handle");
return;
}
auto runtime = GetRuntimeFromConstructorArgs(info);
auto handle = static_cast<FridaSession*>(
Local<External>::Cast(info[0])->Value());
auto wrapper = new Session(handle, runtime);
auto obj = info.This();
wrapper->Wrap(obj);
Nan::Set(obj, Nan::New("signals").ToLocalChecked(),
Signals::New(handle, runtime, TransformSignal, runtime));
info.GetReturnValue().Set(obj);
}
NAN_PROPERTY_GETTER(Session::GetPid) {
auto handle = ObjectWrap::Unwrap<Session>(
info.Holder())->GetHandle<FridaSession>();
info.GetReturnValue().Set(Nan::New<Uint32>(
frida_session_get_pid(handle)));
}
NAN_PROPERTY_GETTER(Session::GetPersistTimeout) {
auto handle = ObjectWrap::Unwrap<Session>(
info.Holder())->GetHandle<FridaSession>();
info.GetReturnValue().Set(Nan::New<Uint32>(
frida_session_get_persist_timeout(handle)));
}
NAN_PROPERTY_GETTER(Session::IsDetached) {
auto handle = ObjectWrap::Unwrap<Session>(
info.Holder())->GetHandle<FridaSession>();
info.GetReturnValue().Set(
Nan::New(static_cast<bool>(frida_session_is_detached(handle))));
}
namespace {
class DetachOperation : public Operation<FridaSession> {
protected:
void Begin() {
frida_session_detach(handle_, cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
frida_session_detach_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
return Nan::Undefined();
}
};
}
NAN_METHOD(Session::Detach) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<Session>(info.Holder());
auto operation = new DetachOperation();
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
namespace {
class ResumeOperation : public Operation<FridaSession> {
protected:
void Begin() {
frida_session_resume(handle_, cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
frida_session_resume_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
return Nan::Undefined();
}
};
}
NAN_METHOD(Session::Resume) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<Session>(info.Holder());
auto operation = new ResumeOperation();
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
namespace {
class EnableChildGatingOperation : public Operation<FridaSession> {
protected:
void Begin() {
frida_session_enable_child_gating(handle_, cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
frida_session_enable_child_gating_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
return Nan::Undefined();
}
};
}
NAN_METHOD(Session::EnableChildGating) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<Session>(info.Holder());
auto operation = new EnableChildGatingOperation();
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
namespace {
class DisableChildGatingOperation : public Operation<FridaSession> {
protected:
void Begin() {
frida_session_disable_child_gating(handle_, cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
frida_session_disable_child_gating_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
return Nan::Undefined();
}
};
}
NAN_METHOD(Session::DisableChildGating) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<Session>(info.Holder());
auto operation = new DisableChildGatingOperation();
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
namespace {
class CreateScriptOperation : public Operation<FridaSession> {
public:
CreateScriptOperation(gchar* source, FridaScriptOptions* options)
: source_(source),
options_(options) {
}
~CreateScriptOperation() {
g_object_unref(options_);
g_free(source_);
}
protected:
void Begin() {
frida_session_create_script(handle_, source_, options_, cancellable_,
OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
script_ = frida_session_create_script_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
auto wrapper = Script::New(script_, runtime_);
g_object_unref(script_);
return wrapper;
}
private:
gchar* source_;
FridaScriptOptions* options_;
FridaScript* script_;
};
}
NAN_METHOD(Session::CreateScript) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<Session>(info.Holder());
if (info.Length() < 4) {
Nan::ThrowTypeError("Missing one or more arguments");
return;
}
auto source_value = info[0];
auto name_value = info[1];
auto snapshot_value = info[2];
auto runtime_value = info[3];
bool valid = true;
gchar* source;
Nan::Utf8String val(source_value);
source = g_strdup(*val);
if (source == NULL) {
Nan::ThrowTypeError("Bad argument, 'source' must be a string");
valid = false;
}
FridaScriptOptions* options = NULL;
if (valid) {
options = ParseScriptOptions(name_value, snapshot_value, runtime_value);
valid = options != NULL;
}
if (!valid) {
g_free(source);
g_clear_object(&options);
return;
}
auto operation = new CreateScriptOperation(source, options);
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
namespace {
class CreateScriptFromBytesOperation : public Operation<FridaSession> {
public:
CreateScriptFromBytesOperation(GBytes* bytes, FridaScriptOptions* options)
: bytes_(bytes),
options_(options) {
}
~CreateScriptFromBytesOperation() {
g_object_unref(options_);
g_bytes_unref(bytes_);
}
protected:
void Begin() {
frida_session_create_script_from_bytes(handle_, bytes_, options_,
cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
script_ = frida_session_create_script_from_bytes_finish(handle_, result,
error);
}
Local<Value> Result(Isolate* isolate) {
auto wrapper = Script::New(script_, runtime_);
g_object_unref(script_);
return wrapper;
}
private:
GBytes* bytes_;
FridaScriptOptions* options_;
FridaScript* script_;
};
}
NAN_METHOD(Session::CreateScriptFromBytes) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<Session>(info.Holder());
if (info.Length() < 4) {
Nan::ThrowTypeError("Missing one or more arguments");
return;
}
auto bytes_value = info[0];
auto name_value = info[1];
auto snapshot_value = info[2];
auto runtime_value = info[3];
bool valid = true;
GBytes* bytes = NULL;
if (node::Buffer::HasInstance(bytes_value)) {
bytes = g_bytes_new(node::Buffer::Data(bytes_value),
node::Buffer::Length(bytes_value));
} else {
Nan::ThrowTypeError("Bad argument, 'bytes' must be a Buffer");
valid = false;
}
FridaScriptOptions* options = NULL;
if (valid) {
options = ParseScriptOptions(name_value, snapshot_value, runtime_value);
valid = options != NULL;
}
if (!valid) {
g_bytes_unref(bytes);
g_clear_object(&options);
return;
}
auto operation = new CreateScriptFromBytesOperation(bytes, options);
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
namespace {
class CompileScriptOperation : public Operation<FridaSession> {
public:
CompileScriptOperation(gchar* source, FridaScriptOptions* options)
: source_(source),
options_(options) {
}
~CompileScriptOperation() {
g_object_unref(options_);
g_free(source_);
}
protected:
void Begin() {
frida_session_compile_script(handle_, source_, options_, cancellable_,
OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
bytes_ = frida_session_compile_script_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
gsize size;
auto data = g_bytes_get_data(bytes_, &size);
return Nan::NewBuffer(static_cast<char*>(const_cast<void*>(data)), size,
UnrefGBytes, bytes_).ToLocalChecked();
}
private:
gchar* source_;
FridaScriptOptions* options_;
GBytes* bytes_;
};
}
NAN_METHOD(Session::CompileScript) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<Session>(info.Holder());
if (info.Length() < 3) {
Nan::ThrowTypeError("Missing one or more arguments");
return;
}
auto source_value = info[0];
auto name_value = info[1];
auto runtime_value = info[2];
bool valid = true;
gchar* source;
Nan::Utf8String val(source_value);
source = g_strdup(*val);
if (source == NULL) {
Nan::ThrowTypeError("Bad argument, 'source' must be a string");
valid = false;
}
FridaScriptOptions* options = NULL;
if (valid) {
options = ParseScriptOptions(name_value, Nan::Null(), runtime_value);
valid = options != NULL;
}
if (!valid) {
g_free(source);
g_clear_object(&options);
return;
}
auto operation = new CompileScriptOperation(source, options);
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
static FridaScriptOptions* ParseScriptOptions(Local<Value> name_value,
Local<Value> snapshot_value, Local<Value> runtime_value) {
auto options = frida_script_options_new();
bool valid = true;
if (!name_value->IsNull()) {
Nan::Utf8String val(name_value);
const gchar* name = *val;
if (name != NULL) {
frida_script_options_set_name(options, name);
} else {
Nan::ThrowTypeError("Bad argument, 'name' must be a string");
valid = false;
}
}
if (valid && !snapshot_value->IsNull()) {
if (node::Buffer::HasInstance(snapshot_value)) {
auto snapshot = g_bytes_new(node::Buffer::Data(snapshot_value),
node::Buffer::Length(snapshot_value));
frida_script_options_set_snapshot(options, snapshot);
g_bytes_unref(snapshot);
} else {
Nan::ThrowTypeError("Bad argument, 'snapshot' must be a Buffer");
valid = false;
}
}
if (valid && !runtime_value->IsNull()) {
FridaScriptRuntime runtime;
valid = Runtime::ValueToEnum(runtime_value, FRIDA_TYPE_SCRIPT_RUNTIME,
&runtime);
if (valid) {
frida_script_options_set_runtime(options, runtime);
}
}
if (!valid) {
g_object_unref(options);
return NULL;
}
return options;
}
namespace {
class SnapshotScriptOperation : public Operation<FridaSession> {
public:
SnapshotScriptOperation(gchar* embed_script, FridaSnapshotOptions* options)
: embed_script_(embed_script),
options_(options) {
}
~SnapshotScriptOperation() {
g_object_unref(options_);
g_free(embed_script_);
}
protected:
void Begin() {
frida_session_snapshot_script(handle_, embed_script_, options_,
cancellable_, OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
bytes_ = frida_session_snapshot_script_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
gsize size;
auto data = g_bytes_get_data(bytes_, &size);
return Nan::NewBuffer(static_cast<char*>(const_cast<void*>(data)), size,
UnrefGBytes, bytes_).ToLocalChecked();
}
private:
gchar* embed_script_;
FridaSnapshotOptions* options_;
GBytes* bytes_;
};
}
NAN_METHOD(Session::SnapshotScript) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<Session>(info.Holder());
if (info.Length() < 3) {
Nan::ThrowTypeError("Missing one or more arguments");
return;
}
auto embed_script_value = info[0];
auto warmup_script_value = info[1];
auto runtime_value = info[2];
bool valid = true;
gchar* embed_script;
Nan::Utf8String val(embed_script_value);
embed_script = g_strdup(*val);
if (embed_script == NULL) {
Nan::ThrowTypeError("Bad argument, 'embedScript' must be a string");
valid = false;
}
FridaSnapshotOptions* options = NULL;
if (valid) {
options = ParseSnapshotOptions(warmup_script_value, runtime_value);
valid = options != NULL;
}
if (!valid) {
g_free(embed_script);
g_clear_object(&options);
return;
}
auto operation = new SnapshotScriptOperation(embed_script, options);
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
static FridaSnapshotOptions* ParseSnapshotOptions(
Local<Value> warmup_script_value, Local<Value> runtime_value) {
auto options = frida_snapshot_options_new();
bool valid = true;
if (!warmup_script_value->IsNull()) {
Nan::Utf8String val(warmup_script_value);
const gchar* warmup_script = *val;
if (warmup_script != NULL) {
frida_snapshot_options_set_warmup_script(options, warmup_script);
} else {
Nan::ThrowTypeError("Bad argument, 'warmupScript' must be a string");
valid = false;
}
}
if (valid && !runtime_value->IsNull()) {
FridaScriptRuntime runtime;
valid = Runtime::ValueToEnum(runtime_value, FRIDA_TYPE_SCRIPT_RUNTIME,
&runtime);
if (valid) {
frida_snapshot_options_set_runtime(options, runtime);
}
}
if (!valid) {
g_object_unref(options);
return NULL;
}
return options;
}
namespace {
class SetupPeerConnectionOperation : public Operation<FridaSession> {
public:
SetupPeerConnectionOperation(FridaPeerOptions* options) : options_(options) {
}
~SetupPeerConnectionOperation() {
g_object_unref(options_);
}
protected:
void Begin() {
frida_session_setup_peer_connection(handle_, options_, cancellable_,
OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
frida_session_setup_peer_connection_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
return Nan::Undefined();
}
private:
FridaPeerOptions* options_;
};
}
NAN_METHOD(Session::SetupPeerConnection) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<Session>(info.Holder());
if (info.Length() < 2) {
Nan::ThrowTypeError("Missing one or more arguments");
return;
}
auto stun_server_value = info[0];
auto relays_value = info[1];
auto options = frida_peer_options_new();
bool valid = true;
if (!stun_server_value->IsNull()) {
if (stun_server_value->IsString()) {
Nan::Utf8String stun_server(stun_server_value);
frida_peer_options_set_stun_server(options, *stun_server);
} else {
Nan::ThrowTypeError("Bad argument, 'stunServer' must be a string");
valid = false;
}
}
if (valid) {
if (relays_value->IsArray()) {
auto array = Local<Array>::Cast(relays_value);
uint32_t n = array->Length();
for (uint32_t i = 0; i != n; i++) {
auto element_value = Nan::Get(array, i).ToLocalChecked();
FridaRelay* relay = Relay::TryParse(element_value, wrapper->runtime_);
if (relay == NULL) {
Nan::ThrowTypeError("Bad argument, 'relays' element type mismatch");
valid = false;
break;
}
frida_peer_options_add_relay(options, relay);
}
} else {
Nan::ThrowTypeError("Bad argument, 'relays' must be an array");
valid = false;
}
}
if (!valid) {
g_object_unref(options);
return;
}
auto operation = new SetupPeerConnectionOperation(options);
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
namespace {
class JoinPortalOperation : public Operation<FridaSession> {
public:
JoinPortalOperation(gchar* address, FridaPortalOptions* options)
: address_(address),
options_(options) {
}
~JoinPortalOperation() {
g_object_unref(options_);
g_free(address_);
}
protected:
void Begin() {
frida_session_join_portal(handle_, address_, options_, cancellable_,
OnReady, this);
}
void End(GAsyncResult* result, GError** error) {
membership_ = frida_session_join_portal_finish(handle_, result, error);
}
Local<Value> Result(Isolate* isolate) {
auto wrapper = PortalMembership::New(membership_, runtime_);
g_object_unref(membership_);
return wrapper;
}
private:
gchar* address_;
FridaPortalOptions* options_;
FridaPortalMembership* membership_;
};
}
NAN_METHOD(Session::JoinPortal) {
auto isolate = info.GetIsolate();
auto wrapper = ObjectWrap::Unwrap<Session>(info.Holder());
if (info.Length() < 4) {
Nan::ThrowTypeError("Missing one or more arguments");
return;
}
auto address_value = info[0];
auto certificate_value = info[1];
auto token_value = info[2];
auto acl_value = info[3];
if (!address_value->IsString()) {
Nan::ThrowTypeError("Bad argument, 'address' must be a string");
return;
}
Nan::Utf8String address(address_value);
auto options = frida_portal_options_new();
bool valid = true;
if (!certificate_value->IsNull()) {
GTlsCertificate* certificate;
valid = Runtime::ValueToCertificate(certificate_value, &certificate);
if (valid) {
frida_portal_options_set_certificate(options, certificate);
g_object_unref(certificate);
}
}
if (valid && !token_value->IsNull()) {
if (token_value->IsString()) {
Nan::Utf8String token(token_value);
frida_portal_options_set_token(options, *token);
} else {
Nan::ThrowTypeError("Bad argument, 'token' must be a string");
valid = false;
}
}
if (valid && !acl_value->IsNull()) {
gchar** acl;
gint acl_length;
valid = Runtime::ValueToEnvp(acl_value, &acl, &acl_length);
if (valid) {
frida_portal_options_set_acl(options, acl, acl_length);
g_strfreev(acl);
}
}
if (!valid) {
g_object_unref(options);
return;
}
auto operation = new JoinPortalOperation(g_strdup(*address), options);
operation->Schedule(isolate, wrapper, info);
info.GetReturnValue().Set(operation->GetPromise(isolate));
}
Local<Value> Session::TransformSignal(const gchar* name, guint index,
const GValue* value, gpointer user_data) {
auto runtime = static_cast<Runtime*>(user_data);
if (index == 1 && strcmp(name, "detached") == 0) {
auto crash = g_value_get_object(value);
if (crash == NULL)
return Nan::Null();
return Crash::New(crash, runtime);
}
return Local<Value>();
}
static void UnrefGBytes(char* data, void* hint) {
g_bytes_unref(static_cast<GBytes*>(hint));
}
}
-42
View File
@@ -1,42 +0,0 @@
#ifndef FRIDANODE_SESSION_H
#define FRIDANODE_SESSION_H
#include "glib_object.h"
#include <frida-core.h>
namespace frida {
class Session : public GLibObject {
public:
static void Init(v8::Local<v8::Object> exports, Runtime* runtime);
static v8::Local<v8::Object> New(gpointer handle, Runtime* runtime);
private:
explicit Session(FridaSession* handle, Runtime* runtime);
~Session();
static NAN_METHOD(New);
static NAN_PROPERTY_GETTER(GetPid);
static NAN_PROPERTY_GETTER(GetPersistTimeout);
static NAN_PROPERTY_GETTER(IsDetached);
static NAN_METHOD(Detach);
static NAN_METHOD(Resume);
static NAN_METHOD(EnableChildGating);
static NAN_METHOD(DisableChildGating);
static NAN_METHOD(CreateScript);
static NAN_METHOD(CreateScriptFromBytes);
static NAN_METHOD(CompileScript);
static NAN_METHOD(SnapshotScript);
static NAN_METHOD(SetupPeerConnection);
static NAN_METHOD(JoinPortal);
static v8::Local<v8::Value> TransformSignal(const gchar* name, guint index,
const GValue* value, gpointer user_data);
};
}
#endif
-334
View File
@@ -1,334 +0,0 @@
#include "signals.h"
#include <cstring>
#define SIGNALS_DATA_CONSTRUCTOR "signals:ctor"
using std::memset;
using v8::Boolean;
using v8::Exception;
using v8::External;
using v8::Function;
using v8::Integer;
using v8::Isolate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::Persistent;
using v8::String;
using v8::Uint32;
using v8::Value;
namespace frida {
struct SignalsClosure {
GClosure closure;
gboolean alive;
guint signal_id;
guint handler_id;
Persistent<Function>* callback;
Persistent<Object>* parent;
Signals::TransformCallback transform;
gpointer transform_data;
Runtime* runtime;
};
static SignalsClosure* signals_closure_new(guint signal_id,
Local<Function> callback, Local<Object> parent,
Signals::TransformCallback transform, gpointer transform_data,
Runtime* runtime);
static void signals_closure_finalize(gpointer data, GClosure* closure);
static void signals_closure_marshal(GClosure* closure, GValue* return_gvalue,
guint n_param_values, const GValue* param_values, gpointer invocation_hint,
gpointer marshal_data);
static Local<Value> signals_closure_gvalue_to_jsvalue(const GValue* gvalue);
Signals::Signals(gpointer handle, TransformCallback transform,
gpointer transform_data, Runtime* runtime)
: GLibObject(handle, runtime),
transform_(transform),
transform_data_(transform_data),
connect_(NULL),
connect_data_(NULL),
disconnect_(NULL),
disconnect_data_(NULL),
closures_(NULL) {
g_object_ref(handle_);
}
Signals::~Signals() {
g_assert(closures_ == NULL); // They keep us alive
frida_unref(handle_);
}
void Signals::Init(Local<Object> exports, Runtime* runtime) {
auto isolate = Isolate::GetCurrent();
auto name = Nan::New("Signals").ToLocalChecked();
auto tpl = CreateTemplate(name, Signals::New, runtime);
Nan::SetPrototypeMethod(tpl, "connect", Connect);
Nan::SetPrototypeMethod(tpl, "disconnect", Disconnect);
auto ctor = Nan::GetFunction(tpl).ToLocalChecked();
Nan::Set(exports, name, ctor);
runtime->SetDataPointer(SIGNALS_DATA_CONSTRUCTOR,
new Persistent<Function>(isolate, ctor));
}
Local<Object> Signals::New(gpointer handle, Runtime* runtime,
TransformCallback transform, gpointer transform_data) {
auto ctor = Nan::New<Function>(
*static_cast<Persistent<Function>*>(
runtime->GetDataPointer(SIGNALS_DATA_CONSTRUCTOR)));
const int argc = 3;
Local<Value> argv[argc] = {
Nan::New<External>(handle),
Nan::New<External>(reinterpret_cast<void*>(transform)),
Nan::New<External>(transform_data)
};
return Nan::NewInstance(ctor, argc, argv).ToLocalChecked();
}
void Signals::SetConnectCallback(ConnectCallback callback,
gpointer user_data) {
connect_ = callback;
connect_data_ = user_data;
}
void Signals::SetDisconnectCallback(DisconnectCallback callback,
gpointer user_data) {
disconnect_ = callback;
disconnect_data_ = user_data;
}
NAN_METHOD(Signals::New) {
if (!info.IsConstructCall()) {
Nan::ThrowError("Use the `new` keyword to create a new instance");
return;
}
if (info.Length() != 3 ||
!info[0]->IsExternal() ||
!info[1]->IsExternal() ||
!info[2]->IsExternal()) {
Nan::ThrowTypeError("Bad argument, expected raw handles");
return;
}
auto handle = Local<External>::Cast(info[0])->Value();
auto transform = reinterpret_cast<TransformCallback>(
Local<External>::Cast(info[1])->Value());
auto transform_data = Local<External>::Cast(info[2])->Value();
auto wrapper = new Signals(handle, transform, transform_data,
GetRuntimeFromConstructorArgs(info));
auto obj = info.This();
wrapper->Wrap(obj);
info.GetReturnValue().Set(obj);
}
NAN_METHOD(Signals::Connect) {
auto obj = info.Holder();
auto wrapper = ObjectWrap::Unwrap<Signals>(obj);
auto runtime = wrapper->runtime_;
guint signal_id;
Local<Function> callback;
if (!wrapper->GetSignalArguments(info, signal_id, callback))
return;
auto signals_closure = signals_closure_new(signal_id, callback, obj,
wrapper->transform_, wrapper->transform_data_, runtime);
auto closure = reinterpret_cast<GClosure*>(signals_closure);
g_closure_ref(closure);
g_closure_sink(closure);
wrapper->closures_ = g_slist_append(wrapper->closures_, signals_closure);
signals_closure->handler_id = g_signal_connect_closure_by_id(wrapper->handle_,
signal_id, 0, closure, TRUE);
g_assert(signals_closure->handler_id != 0);
if (wrapper->connect_ != NULL) {
wrapper->connect_(g_signal_name(signal_id), wrapper->connect_data_);
}
}
NAN_METHOD(Signals::Disconnect) {
auto wrapper = ObjectWrap::Unwrap<Signals>(info.Holder());
guint signal_id;
Local<Function> callback;
if (!wrapper->GetSignalArguments(info, signal_id, callback))
return;
for (GSList* cur = wrapper->closures_; cur != NULL; cur = cur->next) {
auto signals_closure = static_cast<SignalsClosure*>(cur->data);
auto closure = reinterpret_cast<GClosure*>(signals_closure);
auto closure_callback = Nan::New<Function>(*signals_closure->callback);
if (signals_closure->signal_id == signal_id &&
closure_callback->SameValue(callback)) {
if (wrapper->disconnect_ != NULL) {
wrapper->disconnect_(g_signal_name(signal_id),
wrapper->disconnect_data_);
}
wrapper->closures_ = g_slist_delete_link(wrapper->closures_, cur);
signals_closure->alive = FALSE;
g_assert(signals_closure->handler_id != 0);
g_signal_handler_disconnect(wrapper->handle_,
signals_closure->handler_id);
g_closure_unref(closure);
break;
}
}
}
bool Signals::GetSignalArguments(const Nan::FunctionCallbackInfo<Value>& info,
guint& signal_id, Local<Function>& callback) {
if (info.Length() < 2 || !info[0]->IsString() || !info[1]->IsFunction()) {
Nan::ThrowTypeError("Bad arguments, expected string and function");
return false;
}
Nan::Utf8String signal_name(info[0]);
signal_id = g_signal_lookup(*signal_name, G_OBJECT_TYPE(handle_));
if (signal_id == 0) {
Nan::ThrowTypeError("Bad signal name");
return false;
}
callback = Local<Function>::Cast(info[1]);
return true;
}
static SignalsClosure* signals_closure_new(guint signal_id,
Local<Function> callback, Local<Object> parent,
Signals::TransformCallback transform, gpointer transform_data,
Runtime* runtime) {
auto isolate = Isolate::GetCurrent();
GClosure* closure = g_closure_new_simple(sizeof(SignalsClosure), NULL);
g_closure_add_finalize_notifier(closure, NULL, signals_closure_finalize);
g_closure_set_marshal(closure, signals_closure_marshal);
SignalsClosure* self = reinterpret_cast<SignalsClosure*>(closure);
self->alive = TRUE;
self->signal_id = signal_id;
self->handler_id = 0;
self->callback = new Persistent<Function>(isolate, callback);
self->parent = new Persistent<Object>(isolate, parent);
self->transform = transform;
self->transform_data = transform_data;
self->runtime = runtime;
return self;
}
static void signals_closure_finalize(gpointer data, GClosure* closure) {
SignalsClosure* self = reinterpret_cast<SignalsClosure*>(closure);
self->callback->Reset();
self->parent->Reset();
delete self->callback;
delete self->parent;
}
static void signals_closure_marshal(GClosure* closure, GValue* return_gvalue,
guint n_param_values, const GValue* param_values, gpointer invocation_hint,
gpointer marshal_data) {
SignalsClosure* self = reinterpret_cast<SignalsClosure*>(closure);
g_closure_ref(closure);
GArray* args = g_array_sized_new(FALSE, FALSE, sizeof(GValue),
n_param_values);
g_assert(n_param_values >= 1);
for (guint i = 1; i != n_param_values; i++) {
GValue val;
memset(&val, 0, sizeof(val));
g_value_init(&val, param_values[i].g_type);
g_value_copy(&param_values[i], &val);
g_array_append_val(args, val);
}
self->runtime->GetUVContext()->Schedule([=]() {
if (self->alive) {
auto transform = self->transform;
auto transform_data = self->transform_data;
auto signal_name = g_signal_name(self->signal_id);
const int argc = args->len;
Local<Value>* argv = new Local<Value>[argc];
for (guint i = 0; i != args->len; i++) {
auto value = &g_array_index(args, GValue, i);
argv[i] = transform != NULL
? transform(signal_name, i, value, transform_data)
: Local<Value>();
if (argv[i].IsEmpty())
argv[i] = signals_closure_gvalue_to_jsvalue(value);
}
auto context = Isolate::GetCurrent()->GetCurrentContext();
auto callback = Nan::New<Function>(*self->callback);
auto recv = Nan::Undefined();
Local<Value> no_result;
callback->Call(context, recv, argc, argv).FromMaybe(no_result);
delete[] argv;
}
for (guint i = 0; i != args->len; i++)
g_value_reset(&g_array_index(args, GValue, i));
g_array_free(args, TRUE);
g_closure_unref(closure);
});
}
static Local<Value> signals_closure_gvalue_to_jsvalue(const GValue* gvalue) {
auto gtype = G_VALUE_TYPE(gvalue);
switch (gtype) {
case G_TYPE_BOOLEAN:
return Nan::New<Boolean>(g_value_get_boolean(gvalue));
case G_TYPE_INT:
return Nan::New<Integer>(g_value_get_int(gvalue));
case G_TYPE_UINT:
return Nan::New<Uint32>(g_value_get_uint(gvalue));
case G_TYPE_FLOAT:
return Nan::New<Number>(g_value_get_float(gvalue));
case G_TYPE_DOUBLE:
return Nan::New<Number>(g_value_get_double(gvalue));
case G_TYPE_STRING: {
auto str = g_value_get_string(gvalue);
if (str == NULL)
return Nan::Null();
return Nan::New<String>(str).ToLocalChecked();
}
default: {
if (G_TYPE_IS_ENUM(gtype))
return Runtime::ValueFromEnum(g_value_get_enum(gvalue), gtype);
if (g_type_is_a(gtype, G_TYPE_SOCKET_ADDRESS)) {
return Runtime::ValueFromSocketAddress(
G_SOCKET_ADDRESS(g_value_get_object(gvalue)));
}
g_assert(gtype == G_TYPE_BYTES);
auto bytes = static_cast<GBytes*>(g_value_get_boxed(gvalue));
if (bytes != NULL) {
gsize size;
gconstpointer data = g_bytes_get_data(bytes, &size);
return Nan::CopyBuffer(static_cast<const char*>(data), size)
.ToLocalChecked();
} else {
return Nan::Null();
}
}
}
}
}
-47
View File
@@ -1,47 +0,0 @@
#ifndef FRIDANODE_SIGNALS_H
#define FRIDANODE_SIGNALS_H
#include "glib_object.h"
namespace frida {
class Signals : public GLibObject {
public:
typedef v8::Local<v8::Value>(*TransformCallback)(const gchar* name,
guint index, const GValue* value, gpointer user_data);
typedef void (*ConnectCallback)(const gchar* name, gpointer user_data);
typedef void (*DisconnectCallback)(const gchar* name, gpointer user_data);
static void Init(v8::Local<v8::Object> exports, Runtime* runtime);
static v8::Local<v8::Object> New(gpointer handle, Runtime* runtime,
TransformCallback transform = NULL, gpointer transform_data = NULL);
void SetConnectCallback(ConnectCallback callback, gpointer user_data);
void SetDisconnectCallback(DisconnectCallback callback, gpointer user_data);
private:
Signals(gpointer handle, TransformCallback transform, gpointer transform_data,
Runtime* runtime);
~Signals();
static NAN_METHOD(New);
static NAN_METHOD(Connect);
static NAN_METHOD(Disconnect);
bool GetSignalArguments(
const Nan::FunctionCallbackInfo<v8::Value>& info,
guint& signal_id, v8::Local<v8::Function>& callback);
TransformCallback transform_;
gpointer transform_data_;
ConnectCallback connect_;
gpointer connect_data_;
DisconnectCallback disconnect_;
gpointer disconnect_data_;
GSList* closures_;
};
}
#endif
-95
View File
@@ -1,95 +0,0 @@
#include "spawn.h"
#define SPAWN_DATA_CONSTRUCTOR "spawn:ctor"
using v8::DEFAULT;
using v8::External;
using v8::Function;
using v8::Integer;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::Persistent;
using v8::ReadOnly;
using v8::Value;
namespace frida {
Spawn::Spawn(FridaSpawn* handle, Runtime* runtime)
: GLibObject(handle, runtime) {
g_object_ref(handle_);
}
Spawn::~Spawn() {
g_object_unref(handle_);
}
void Spawn::Init(Local<Object> exports, Runtime* runtime) {
auto isolate = Isolate::GetCurrent();
auto name = Nan::New("Spawn").ToLocalChecked();
auto tpl = CreateTemplate(name, Spawn::New, runtime);
auto instance_tpl = tpl->InstanceTemplate();
auto data = Local<Value>();
Nan::SetAccessor(instance_tpl, Nan::New("identifier").ToLocalChecked(),
GetIdentifier, 0, data, DEFAULT, ReadOnly);
Nan::SetAccessor(instance_tpl, Nan::New("pid").ToLocalChecked(),
GetPid, 0, data, DEFAULT, ReadOnly);
auto ctor = Nan::GetFunction(tpl).ToLocalChecked();
Nan::Set(exports, name, ctor);
runtime->SetDataPointer(SPAWN_DATA_CONSTRUCTOR,
new Persistent<Function>(isolate, ctor));
}
Local<Object> Spawn::New(gpointer handle, Runtime* runtime) {
auto ctor = Nan::New<Function>(
*static_cast<Persistent<Function>*>(
runtime->GetDataPointer(SPAWN_DATA_CONSTRUCTOR)));
const int argc = 1;
Local<Value> argv[argc] = { Nan::New<External>(handle) };
return Nan::NewInstance(ctor, argc, argv).ToLocalChecked();
}
NAN_METHOD(Spawn::New) {
if (!info.IsConstructCall()) {
Nan::ThrowError("Use the `new` keyword to create a new instance");
return;
}
if (info.Length() != 1 || !info[0]->IsExternal()) {
Nan::ThrowTypeError("Bad argument, expected raw handle");
return;
}
auto runtime = GetRuntimeFromConstructorArgs(info);
auto handle = static_cast<FridaSpawn*>(
Local<External>::Cast(info[0])->Value());
auto wrapper = new Spawn(handle, runtime);
auto obj = info.This();
wrapper->Wrap(obj);
info.GetReturnValue().Set(obj);
}
NAN_PROPERTY_GETTER(Spawn::GetPid) {
auto handle = ObjectWrap::Unwrap<Spawn>(
info.Holder())->GetHandle<FridaSpawn>();
info.GetReturnValue().Set(Nan::New<Integer>(frida_spawn_get_pid(handle)));
}
NAN_PROPERTY_GETTER(Spawn::GetIdentifier) {
auto handle = ObjectWrap::Unwrap<Spawn>(
info.Holder())->GetHandle<FridaSpawn>();
auto identifier = frida_spawn_get_identifier(handle);
if (identifier != NULL)
info.GetReturnValue().Set(Nan::New(identifier).ToLocalChecked());
else
info.GetReturnValue().Set(Nan::Null());
}
}
-27
View File
@@ -1,27 +0,0 @@
#ifndef FRIDANODE_SPAWN_H
#define FRIDANODE_SPAWN_H
#include "glib_object.h"
#include <frida-core.h>
namespace frida {
class Spawn : public GLibObject {
public:
static void Init(v8::Local<v8::Object> exports, Runtime* runtime);
static v8::Local<v8::Object> New(gpointer handle, Runtime* runtime);
private:
explicit Spawn(FridaSpawn* handle, Runtime* runtime);
~Spawn();
static NAN_METHOD(New);
static NAN_PROPERTY_GETTER(GetPid);
static NAN_PROPERTY_GETTER(GetIdentifier);
};
}
#endif
+12 -12
View File
@@ -12,16 +12,16 @@ def main(argv: list[str]):
for asset in [package_json, tsconfig]:
shutil.copy(asset, privdir)
libdir = privdir / "lib"
if libdir.exists():
shutil.rmtree(libdir)
libdir.mkdir()
srcdir = privdir / "src"
if srcdir.exists():
shutil.rmtree(srcdir)
srcdir.mkdir()
for asset in sources:
shutil.copy(asset, libdir)
shutil.copy(asset, srcdir)
distdir = privdir / "dist"
if distdir.exists():
shutil.rmtree(distdir)
srcoutdir = privdir / "build" / "src"
if srcoutdir.exists():
shutil.rmtree(srcoutdir)
run_kwargs = {
"stdout": subprocess.PIPE,
@@ -32,13 +32,13 @@ def main(argv: list[str]):
subprocess.run([npm, "install", "--ignore-scripts"],
cwd=privdir,
**run_kwargs)
subprocess.run([npm, "run", "build"],
subprocess.run([npm, "exec", "tsc"],
cwd=privdir,
**run_kwargs)
for asset in sources:
for ext in [".js", ".d.ts"]:
shutil.copy(distdir / (asset.stem + ext), outdir)
for asset in (s for s in sources if not s.name.endswith(".d.ts")):
shutil.copy(srcoutdir / (asset.stem + ".js"), outdir)
shutil.copy(srcoutdir / (asset.stem + ".d.ts"), outdir)
except subprocess.CalledProcessError as e:
print(e, file=sys.stderr)
print("Output:\n\t| " + "\n\t| ".join(e.output.strip().split("\n")), file=sys.stderr)
-117
View File
@@ -1,117 +0,0 @@
#ifndef FRIDANODE_USAGE_MONITOR_H
#define FRIDANODE_USAGE_MONITOR_H
#include "glib_object.h"
using Nan::HandleScope;
namespace frida {
template<typename T>
class UsageMonitor {
public:
typedef gboolean (*CheckCallback)(T* self);
UsageMonitor(CheckCallback check, const gchar* signal)
: ref_count_(1),
check_(check),
signal_(signal),
instance_(NULL),
handler_id_(0),
runtime_(NULL) {
}
private:
void Ref() {
g_atomic_int_add(&ref_count_, 1);
}
void Unref() {
if (g_atomic_int_add(&ref_count_, -1) - 1 == 0) {
delete this;
}
}
~UsageMonitor() {
if (!object_.IsEmpty()) {
object_.ClearWeak();
object_.Reset();
}
}
public:
void Enable(GLibObject* wrapper) {
g_assert(instance_ == NULL);
auto isolate = v8::Isolate::GetCurrent();
object_.Reset(wrapper->handle(isolate));
object_.SetWeak(this, OnWeakNotifyWrapper, Nan::WeakCallbackType::kParameter);
#if NODE_MAJOR_VERSION < 12
object_.MarkIndependent();
#endif
instance_ = wrapper->GetHandle<T>();
g_object_ref(instance_);
runtime_ = wrapper->GetRuntime();
runtime_->GetUVContext()->IncreaseUsage();
runtime_->GetGLibContext()->Schedule([=]() {
handler_id_ = g_signal_connect_swapped(instance_, signal_,
reinterpret_cast<GCallback>(OnSignal), this);
Check();
});
}
private:
void Disable() {
if (instance_ == NULL)
return;
g_signal_handler_disconnect(instance_, handler_id_);
g_object_unref(instance_);
instance_ = NULL;
runtime_->GetUVContext()->Schedule([=]() {
runtime_->GetUVContext()->DecreaseUsage();
Unref();
});
}
void Check() {
if (check_(instance_))
Disable();
}
static void OnWeakNotifyWrapper(
const Nan::WeakCallbackInfo<UsageMonitor<T>>& data) {
HandleScope scope;
data.GetParameter()->OnWeakNotify();
}
void OnWeakNotify() {
object_.Reset();
Ref();
runtime_->GetGLibContext()->Schedule([=]() {
Disable();
runtime_->GetUVContext()->Schedule([=]() {
Unref();
});
});
}
static void OnSignal(UsageMonitor* monitor) {
monitor->Check();
}
volatile gint ref_count_;
CheckCallback check_;
const gchar* signal_;
Nan::Persistent<v8::Object> object_;
T* instance_;
guint handler_id_;
Runtime* runtime_;
};
}
#endif
-118
View File
@@ -1,118 +0,0 @@
#include "uv_context.h"
#define UV_CONTEXT_LOCK() g_mutex_lock(&mutex_)
#define UV_CONTEXT_UNLOCK() g_mutex_unlock(&mutex_)
#define UV_CONTEXT_WAIT() g_cond_wait(&cond_, &mutex_)
#define UV_CONTEXT_SIGNAL() g_cond_signal(&cond_)
using v8::Context;
using v8::External;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::Value;
using Nan::HandleScope;
namespace frida {
UVContext::UVContext(uv_loop_t* loop)
: usage_count_(0), async_(new uv_async_t), async_resource_("frida"),
pending_(NULL) {
async_->data = this;
uv_async_init(loop, async_, ProcessPendingWrapper);
uv_unref(reinterpret_cast<uv_handle_t*>(async_));
g_mutex_init(&mutex_);
g_cond_init(&cond_);
auto isolate = Isolate::GetCurrent();
auto context = isolate->GetCurrentContext();
auto module = Nan::New<Object>();
auto process_pending = Function::New(context, ProcessPendingWrapper,
External::New(isolate, this)).ToLocalChecked();
auto process_pending_name = Nan::New("processPending").ToLocalChecked();
process_pending->SetName(process_pending_name);
Nan::Set(module, process_pending_name, process_pending);
module_.Reset(isolate, module);
process_pending_.Reset(isolate, process_pending);
}
UVContext::~UVContext() {
process_pending_.Reset();
module_.Reset();
g_cond_clear(&cond_);
g_mutex_clear(&mutex_);
uv_close(reinterpret_cast<uv_handle_t*>(async_), DeleteAsyncHandle);
}
void UVContext::DeleteAsyncHandle(uv_handle_t* handle) {
delete reinterpret_cast<uv_async_t*>(handle);
}
void UVContext::IncreaseUsage() {
if (++usage_count_ == 1)
uv_ref(reinterpret_cast<uv_handle_t*>(async_));
}
void UVContext::DecreaseUsage() {
if (usage_count_-- == 1)
uv_unref(reinterpret_cast<uv_handle_t*>(async_));
}
void UVContext::Schedule(std::function<void ()> f) {
auto work = new std::function<void ()>(f);
UV_CONTEXT_LOCK();
pending_ = g_slist_append(pending_, work);
UV_CONTEXT_UNLOCK();
uv_async_send(async_);
}
void UVContext::Perform(std::function<void ()> f) {
volatile bool finished = false;
Schedule([this, f, &finished]() {
f();
UV_CONTEXT_LOCK();
finished = true;
UV_CONTEXT_SIGNAL();
UV_CONTEXT_UNLOCK();
});
UV_CONTEXT_LOCK();
while (!finished)
UV_CONTEXT_WAIT();
UV_CONTEXT_UNLOCK();
}
void UVContext::ProcessPending() {
UV_CONTEXT_LOCK();
while (pending_ != NULL) {
auto work = static_cast<std::function<void ()>*>(pending_->data);
pending_ = g_slist_delete_link(pending_, pending_);
UV_CONTEXT_UNLOCK();
(*work)();
delete work;
UV_CONTEXT_LOCK();
}
UV_CONTEXT_UNLOCK();
}
void UVContext::ProcessPendingWrapper(const FunctionCallbackInfo<Value>& info) {
UVContext* self = static_cast<UVContext*>(
info.Data().As<External>()->Value ());
self->ProcessPending();
}
void UVContext::ProcessPendingWrapper(uv_async_t* handle) {
HandleScope scope;
auto self = static_cast<UVContext*>(handle->data);
auto module = Nan::New<Object>(self->module_);
auto process_pending = Nan::New<Function>(self->process_pending_);
self->async_resource_.runInAsyncScope(module, process_pending, 0, NULL);
}
}
-40
View File
@@ -1,40 +0,0 @@
#ifndef FRIDANODE_UV_CONTEXT_H
#define FRIDANODE_UV_CONTEXT_H
#include <glib.h>
#include <nan.h>
#include <functional>
namespace frida {
class UVContext {
public:
UVContext(uv_loop_t* handle);
~UVContext();
void IncreaseUsage();
void DecreaseUsage();
void Schedule(std::function<void ()> f);
void Perform(std::function<void ()> f);
private:
static void DeleteAsyncHandle(uv_handle_t* handle);
void ProcessPending();
static void ProcessPendingWrapper(const v8::FunctionCallbackInfo<v8::Value>& info);
static void ProcessPendingWrapper(uv_async_t* handle);
int usage_count_;
uv_async_t* async_;
Nan::AsyncResource async_resource_;
GMutex mutex_;
GCond cond_;
GSList* pending_;
v8::Persistent<v8::Object> module_;
v8::Persistent<v8::Function> process_pending_;
};
}
#endif
@@ -20,38 +20,45 @@
#include <delayimp.h>
#include <string.h>
static FARPROC WINAPI load_exe_hook(unsigned int event, DelayLoadInfo* info) {
static FARPROC WINAPI
load_exe_hook (unsigned int event,
DelayLoadInfo * info)
{
#ifdef FRIDA_NODE_WEBKIT
static HMODULE node_dll = NULL;
static HMODULE nw_dll = NULL;
switch (event) {
switch (event)
{
case dliStartProcessing:
node_dll = GetModuleHandle("node.dll");
nw_dll = GetModuleHandle("nw.dll");
node_dll = GetModuleHandle ("node.dll");
nw_dll = GetModuleHandle ("nw.dll");
return NULL;
case dliNotePreLoadLibrary:
if (_stricmp(info->szDll, "node.exe") == 0)
if (_stricmp (info->szDll, "node.exe") == 0)
return (FARPROC) node_dll;
return NULL;
case dliNotePreGetProcAddress: {
FARPROC ret = GetProcAddress(node_dll, info->dlp.szProcName);
case dliNotePreGetProcAddress:
{
FARPROC ret = GetProcAddress (node_dll, info->dlp.szProcName);
if (ret)
return ret;
return GetProcAddress(nw_dll, info->dlp.szProcName);
return GetProcAddress (nw_dll, info->dlp.szProcName);
}
default:
return NULL;
}
#else
HMODULE m;
if (event != dliNotePreLoadLibrary)
return NULL;
if (_stricmp(info->szDll, HOST_BINARY) != 0)
if (_stricmp (info->szDll, HOST_BINARY) != 0)
return NULL;
m = GetModuleHandle(NULL);
m = GetModuleHandle (NULL);
return (FARPROC) m;
#endif
}
Loaded 100 of 109 files, more files were not shown because too many files have changed in this diff. Show more