mirror of
https://github.com/frida/frida-node.git
synced 2026-11-07 09:35:57 -10:00
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:
1 parent
4675cf8471
commit
e2ffd0aa6c
109 files changed
+8346
-9964
No files matched your search
+1
-2
@@ -1,7 +1,6 @@
|
||||
/build/
|
||||
/deps/
|
||||
/dist/
|
||||
/node_modules/
|
||||
/npm-debug.log
|
||||
/prebuilds/
|
||||
/src/frida_bindgen/__pycache__/
|
||||
/subprojects/*
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export interface AuthenticatedSessionInfo {
|
||||
[key: string]: any;
|
||||
}
|
||||
-30
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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
@@ -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",
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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',
|
||||
)
|
||||
@@ -1,9 +0,0 @@
|
||||
import * as bindings from "bindings";
|
||||
|
||||
export const binding = bindings({
|
||||
bindings: "frida_binding",
|
||||
try: [
|
||||
["module_root", "build", "bindings"],
|
||||
[process.cwd(), "bindings"],
|
||||
]
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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());
|
||||
}
|
||||
@@ -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
@@ -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[];
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export interface Spawn {
|
||||
pid: number;
|
||||
identifier: string | null;
|
||||
}
|
||||
@@ -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
@@ -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@',
|
||||
],
|
||||
)
|
||||
Generated
+681
-270
File diff suppressed because it is too large.
Load diff
+21
-18
@@ -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
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
@@ -0,0 +1,3 @@
|
||||
EXPORTS
|
||||
node_api_module_get_api_version_v1
|
||||
napi_register_module_v1
|
||||
+2
-1
@@ -1 +1,2 @@
|
||||
# No exported symbols.
|
||||
_node_api_module_get_api_version_v1
|
||||
_napi_register_module_v1
|
||||
@@ -1,4 +1,8 @@
|
||||
{
|
||||
global:
|
||||
node_api_module_get_api_version_v1;
|
||||
napi_register_module_v1;
|
||||
|
||||
local:
|
||||
*;
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large.
Load diff
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
Whitespace-only changes.
@@ -0,0 +1,4 @@
|
||||
from .cli import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
File diff suppressed because it is too large.
Load diff
@@ -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);
|
||||
@@ -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}`);
|
||||
}
|
||||
@@ -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
@@ -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,
|
||||
)
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 ());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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'
|
||||
|
||||
@@ -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
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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>();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
@@ -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(¶m_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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
Reference in new issue
Block a user