frida 16.2.5 → 16.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/device.d.ts +2 -0
- package/dist/device.js +4 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +3 -1
- package/dist/script.js +13 -4
- package/dist/service.d.ts +16 -0
- package/dist/service.js +26 -0
- package/lib/device.ts +5 -0
- package/lib/index.ts +5 -0
- package/lib/meson.build +1 -0
- package/lib/script.ts +14 -5
- package/lib/service.ts +34 -0
- package/package.json +1 -1
- package/releng/deps.toml +8 -8
- package/src/addon.cc +2 -0
- package/src/device.cc +53 -13
- package/src/device.h +1 -0
- package/src/meson.build +1 -0
- package/src/runtime.cc +126 -37
- package/src/runtime.h +5 -0
- package/src/service.cc +224 -0
- package/src/service.h +36 -0
- package/subprojects/frida-core.wrap +1 -1
- package/test/script.ts +64 -43
package/dist/device.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { Crash } from "./crash";
|
|
|
8
8
|
import { Icon } from "./icon";
|
|
9
9
|
import { IOStream } from "./iostream";
|
|
10
10
|
import { Process } from "./process";
|
|
11
|
+
import { Service } from "./service";
|
|
11
12
|
import { Session } from "./session";
|
|
12
13
|
import { Signal } from "./signals";
|
|
13
14
|
import { Spawn } from "./spawn";
|
|
@@ -47,6 +48,7 @@ export declare class Device {
|
|
|
47
48
|
injectLibraryFile(target: TargetProcess, path: string, entrypoint: string, data: string, cancellable?: Cancellable): Promise<InjecteeID>;
|
|
48
49
|
injectLibraryBlob(target: TargetProcess, blob: Buffer, entrypoint: string, data: string, cancellable?: Cancellable): Promise<InjecteeID>;
|
|
49
50
|
openChannel(address: string, cancellable?: Cancellable): Promise<IOStream>;
|
|
51
|
+
openService(address: string, cancellable?: Cancellable): Promise<Service>;
|
|
50
52
|
unpair(cancellable?: Cancellable): Promise<void>;
|
|
51
53
|
private getPid;
|
|
52
54
|
[inspect.custom](depth: any, options: any): string;
|
package/dist/device.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Realm = exports.Stdio = exports.Scope = exports.DeviceType = exports.Device = void 0;
|
|
4
4
|
const bus_1 = require("./bus");
|
|
5
5
|
const iostream_1 = require("./iostream");
|
|
6
|
+
const service_1 = require("./service");
|
|
6
7
|
const session_1 = require("./session");
|
|
7
8
|
const signals_1 = require("./signals");
|
|
8
9
|
const minimatch_1 = require("minimatch");
|
|
@@ -131,6 +132,9 @@ class Device {
|
|
|
131
132
|
async openChannel(address, cancellable) {
|
|
132
133
|
return new iostream_1.IOStream(await this.impl.openChannel(address, cancellable));
|
|
133
134
|
}
|
|
135
|
+
async openService(address, cancellable) {
|
|
136
|
+
return new service_1.Service(await this.impl.openService(address, cancellable));
|
|
137
|
+
}
|
|
134
138
|
async unpair(cancellable) {
|
|
135
139
|
await this.impl.unpair(cancellable);
|
|
136
140
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ import * as portalServiceModule from "./portal_service";
|
|
|
15
15
|
import * as processModule from "./process";
|
|
16
16
|
import * as relayModule from "./relay";
|
|
17
17
|
import * as scriptModule from "./script";
|
|
18
|
+
import * as serviceModule from "./service";
|
|
18
19
|
import * as sessionModule from "./session";
|
|
19
20
|
import * as socketAddressModule from "./socket_address";
|
|
20
21
|
import * as spawnModule from "./spawn";
|
|
@@ -132,6 +133,10 @@ export type Bus = busModule.Bus;
|
|
|
132
133
|
export declare const Bus: typeof busModule.Bus;
|
|
133
134
|
export type BusDetachedHandler = busModule.BusDetachedHandler;
|
|
134
135
|
export type BusMessageHandler = busModule.BusMessageHandler;
|
|
136
|
+
export type Service = serviceModule.Service;
|
|
137
|
+
export declare const Service: typeof serviceModule.Service;
|
|
138
|
+
export type ServiceCloseHandler = serviceModule.ServiceCloseHandler;
|
|
139
|
+
export type ServiceMessageHandler = serviceModule.ServiceMessageHandler;
|
|
135
140
|
export type SocketAddress = socketAddressModule.SocketAddress;
|
|
136
141
|
export type IPV4SocketAddress = socketAddressModule.IPV4SocketAddress;
|
|
137
142
|
export type IPV6SocketAddress = socketAddressModule.IPV6SocketAddress;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getDevice = exports.getUsbDevice = exports.getRemoteDevice = exports.getLocalDevice = exports.getDeviceManager = exports.enumerateDevices = exports.injectLibraryBlob = exports.injectLibraryFile = exports.attach = exports.kill = exports.resume = exports.spawn = exports.querySystemParameters = exports.Bus = exports.ChildOrigin = exports.Cancellable = exports.IOStream = exports.EndpointParameters = exports.PortalService = exports.PortalMembership = exports.RelayKind = exports.Relay = exports.LogLevel = exports.MessageType = exports.ScriptRuntime = exports.Script = exports.SessionDetachReason = exports.Session = exports.Realm = exports.Stdio = exports.Scope = exports.DeviceType = exports.Device = exports.DeviceManager = void 0;
|
|
3
|
+
exports.getDevice = exports.getUsbDevice = exports.getRemoteDevice = exports.getLocalDevice = exports.getDeviceManager = exports.enumerateDevices = exports.injectLibraryBlob = exports.injectLibraryFile = exports.attach = exports.kill = exports.resume = exports.spawn = exports.querySystemParameters = exports.Service = exports.Bus = exports.ChildOrigin = exports.Cancellable = exports.IOStream = exports.EndpointParameters = exports.PortalService = exports.PortalMembership = exports.RelayKind = exports.Relay = exports.LogLevel = exports.MessageType = exports.ScriptRuntime = exports.Script = exports.SessionDetachReason = exports.Session = exports.Realm = exports.Stdio = exports.Scope = exports.DeviceType = exports.Device = exports.DeviceManager = void 0;
|
|
4
4
|
const busModule = require("./bus");
|
|
5
5
|
const cancellableModule = require("./cancellable");
|
|
6
6
|
const childModule = require("./child");
|
|
@@ -12,6 +12,7 @@ const portalMembershipModule = require("./portal_membership");
|
|
|
12
12
|
const portalServiceModule = require("./portal_service");
|
|
13
13
|
const relayModule = require("./relay");
|
|
14
14
|
const scriptModule = require("./script");
|
|
15
|
+
const serviceModule = require("./service");
|
|
15
16
|
const sessionModule = require("./session");
|
|
16
17
|
exports.DeviceManager = deviceManagerModule.DeviceManager;
|
|
17
18
|
exports.Device = deviceModule.Device;
|
|
@@ -34,6 +35,7 @@ exports.IOStream = iostreamModule.IOStream;
|
|
|
34
35
|
exports.Cancellable = cancellableModule.Cancellable;
|
|
35
36
|
exports.ChildOrigin = childModule.ChildOrigin;
|
|
36
37
|
exports.Bus = busModule.Bus;
|
|
38
|
+
exports.Service = serviceModule.Service;
|
|
37
39
|
let sharedDeviceManager = null;
|
|
38
40
|
async function querySystemParameters(cancellable) {
|
|
39
41
|
const device = await getLocalDevice(cancellable);
|
package/dist/script.js
CHANGED
|
@@ -105,7 +105,7 @@ class ScriptServices extends signals_1.SignalAdapter {
|
|
|
105
105
|
}
|
|
106
106
|
return null;
|
|
107
107
|
}
|
|
108
|
-
request(operation, params, cancellable) {
|
|
108
|
+
request(operation, params, data, cancellable) {
|
|
109
109
|
return new Promise((resolve, reject) => {
|
|
110
110
|
const id = this.nextRequestId++;
|
|
111
111
|
const complete = (error, result) => {
|
|
@@ -128,7 +128,7 @@ class ScriptServices extends signals_1.SignalAdapter {
|
|
|
128
128
|
complete(new Error("Operation was cancelled"));
|
|
129
129
|
}
|
|
130
130
|
this.pendingRequests[id] = complete;
|
|
131
|
-
this.script.post(["frida:rpc", id, operation]
|
|
131
|
+
this.script.post(["frida:rpc", id, operation, ...params], data);
|
|
132
132
|
this.signals.connect("destroyed", onScriptDestroyed);
|
|
133
133
|
if (cancellable !== undefined) {
|
|
134
134
|
cancellable.cancelled.connect(onOperationCancelled);
|
|
@@ -151,7 +151,12 @@ class ScriptServices extends signals_1.SignalAdapter {
|
|
|
151
151
|
let value = null;
|
|
152
152
|
let error = null;
|
|
153
153
|
if (operation === RpcOperation.Ok) {
|
|
154
|
-
|
|
154
|
+
if (data !== null) {
|
|
155
|
+
value = (params.length > 1) ? [params[1], data] : data;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
value = params[0];
|
|
159
|
+
}
|
|
155
160
|
}
|
|
156
161
|
else {
|
|
157
162
|
const [message, name, stack, rawErr] = params;
|
|
@@ -185,7 +190,11 @@ function ScriptExportsProxy(rpcController) {
|
|
|
185
190
|
if (args[args.length - 1] instanceof cancellable_1.Cancellable) {
|
|
186
191
|
cancellable = args.pop();
|
|
187
192
|
}
|
|
188
|
-
|
|
193
|
+
let data = null;
|
|
194
|
+
if (Buffer.isBuffer(args[args.length - 1])) {
|
|
195
|
+
data = args.pop();
|
|
196
|
+
}
|
|
197
|
+
return rpcController.request("call", [property, args], data, cancellable);
|
|
189
198
|
};
|
|
190
199
|
},
|
|
191
200
|
set(target, property, value, receiver) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Cancellable } from "./cancellable";
|
|
3
|
+
import { Signal } from "./signals";
|
|
4
|
+
import { inspect } from "util";
|
|
5
|
+
export declare class Service {
|
|
6
|
+
private impl;
|
|
7
|
+
close: Signal<ServiceCloseHandler>;
|
|
8
|
+
message: Signal<ServiceMessageHandler>;
|
|
9
|
+
constructor(impl: any);
|
|
10
|
+
activate(cancellable?: Cancellable): Promise<void>;
|
|
11
|
+
cancel(cancellable?: Cancellable): Promise<void>;
|
|
12
|
+
request(parameters: any, cancellable?: Cancellable): Promise<any>;
|
|
13
|
+
[inspect.custom](depth: any, options: any): string;
|
|
14
|
+
}
|
|
15
|
+
export type ServiceCloseHandler = () => void;
|
|
16
|
+
export type ServiceMessageHandler = (message: any) => void;
|
package/dist/service.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Service = void 0;
|
|
4
|
+
const signals_1 = require("./signals");
|
|
5
|
+
const util_1 = require("util");
|
|
6
|
+
class Service {
|
|
7
|
+
constructor(impl) {
|
|
8
|
+
this.impl = impl;
|
|
9
|
+
const signals = impl.signals;
|
|
10
|
+
this.close = new signals_1.Signal(signals, "close");
|
|
11
|
+
this.message = new signals_1.Signal(signals, "message");
|
|
12
|
+
}
|
|
13
|
+
activate(cancellable) {
|
|
14
|
+
return this.impl.activate(cancellable);
|
|
15
|
+
}
|
|
16
|
+
cancel(cancellable) {
|
|
17
|
+
return this.impl.cancel(cancellable);
|
|
18
|
+
}
|
|
19
|
+
request(parameters, cancellable) {
|
|
20
|
+
return this.impl.request(parameters, cancellable);
|
|
21
|
+
}
|
|
22
|
+
[util_1.inspect.custom](depth, options) {
|
|
23
|
+
return "Service {}";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.Service = Service;
|
package/lib/device.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { Crash } from "./crash";
|
|
|
6
6
|
import { Icon } from "./icon";
|
|
7
7
|
import { IOStream } from "./iostream";
|
|
8
8
|
import { Process } from "./process";
|
|
9
|
+
import { Service } from "./service";
|
|
9
10
|
import { Session } from "./session";
|
|
10
11
|
import { Signal } from "./signals";
|
|
11
12
|
import { Spawn } from "./spawn";
|
|
@@ -190,6 +191,10 @@ export class Device {
|
|
|
190
191
|
return new IOStream(await this.impl.openChannel(address, cancellable));
|
|
191
192
|
}
|
|
192
193
|
|
|
194
|
+
async openService(address: string, cancellable?: Cancellable): Promise<Service> {
|
|
195
|
+
return new Service(await this.impl.openService(address, cancellable));
|
|
196
|
+
}
|
|
197
|
+
|
|
193
198
|
async unpair(cancellable?: Cancellable): Promise<void> {
|
|
194
199
|
await this.impl.unpair(cancellable);
|
|
195
200
|
}
|
package/lib/index.ts
CHANGED
|
@@ -14,6 +14,7 @@ import * as portalServiceModule from "./portal_service";
|
|
|
14
14
|
import * as processModule from "./process";
|
|
15
15
|
import * as relayModule from "./relay";
|
|
16
16
|
import * as scriptModule from "./script";
|
|
17
|
+
import * as serviceModule from "./service";
|
|
17
18
|
import * as sessionModule from "./session";
|
|
18
19
|
import * as socketAddressModule from "./socket_address";
|
|
19
20
|
import * as spawnModule from "./spawn";
|
|
@@ -143,6 +144,10 @@ export type Bus = busModule.Bus;
|
|
|
143
144
|
export const Bus = busModule.Bus;
|
|
144
145
|
export type BusDetachedHandler = busModule.BusDetachedHandler;
|
|
145
146
|
export type BusMessageHandler = busModule.BusMessageHandler;
|
|
147
|
+
export type Service = serviceModule.Service;
|
|
148
|
+
export const Service = serviceModule.Service;
|
|
149
|
+
export type ServiceCloseHandler = serviceModule.ServiceCloseHandler;
|
|
150
|
+
export type ServiceMessageHandler = serviceModule.ServiceMessageHandler;
|
|
146
151
|
export type SocketAddress = socketAddressModule.SocketAddress;
|
|
147
152
|
export type IPV4SocketAddress = socketAddressModule.IPV4SocketAddress;
|
|
148
153
|
export type IPV6SocketAddress = socketAddressModule.IPV6SocketAddress;
|
package/lib/meson.build
CHANGED
package/lib/script.ts
CHANGED
|
@@ -173,7 +173,7 @@ class ScriptServices extends SignalAdapter implements RpcController {
|
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
request(operation: string, params: any[], cancellable?: Cancellable): Promise<any> {
|
|
176
|
+
request(operation: string, params: any[], data: Buffer | null, cancellable?: Cancellable): Promise<any> {
|
|
177
177
|
return new Promise((resolve, reject) => {
|
|
178
178
|
const id = this.nextRequestId++;
|
|
179
179
|
|
|
@@ -202,7 +202,7 @@ class ScriptServices extends SignalAdapter implements RpcController {
|
|
|
202
202
|
|
|
203
203
|
this.pendingRequests[id] = complete;
|
|
204
204
|
|
|
205
|
-
this.script.post(["frida:rpc", id, operation]
|
|
205
|
+
this.script.post(["frida:rpc", id, operation, ...params], data);
|
|
206
206
|
this.signals.connect("destroyed", onScriptDestroyed);
|
|
207
207
|
if (cancellable !== undefined) {
|
|
208
208
|
cancellable.cancelled.connect(onOperationCancelled);
|
|
@@ -227,7 +227,11 @@ class ScriptServices extends SignalAdapter implements RpcController {
|
|
|
227
227
|
let value = null;
|
|
228
228
|
let error = null;
|
|
229
229
|
if (operation === RpcOperation.Ok) {
|
|
230
|
-
|
|
230
|
+
if (data !== null) {
|
|
231
|
+
value = (params.length > 1) ? [params[1], data] : data;
|
|
232
|
+
} else {
|
|
233
|
+
value = params[0];
|
|
234
|
+
}
|
|
231
235
|
} else {
|
|
232
236
|
const [message, name, stack, rawErr] = params;
|
|
233
237
|
error = new Error(message);
|
|
@@ -265,7 +269,12 @@ function ScriptExportsProxy(rpcController: RpcController): void {
|
|
|
265
269
|
cancellable = args.pop();
|
|
266
270
|
}
|
|
267
271
|
|
|
268
|
-
|
|
272
|
+
let data: Buffer | null = null;
|
|
273
|
+
if (Buffer.isBuffer(args[args.length - 1])) {
|
|
274
|
+
data = args.pop();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return rpcController.request("call", [property, args], data, cancellable);
|
|
269
278
|
};
|
|
270
279
|
},
|
|
271
280
|
set(target, property, value, receiver) {
|
|
@@ -298,7 +307,7 @@ function inspectProxy() {
|
|
|
298
307
|
}
|
|
299
308
|
|
|
300
309
|
interface RpcController {
|
|
301
|
-
request(operation: string, params: any[], cancellable?: Cancellable): Promise<any>;
|
|
310
|
+
request(operation: string, params: any[], data: ArrayBuffer | null, cancellable?: Cancellable): Promise<any>;
|
|
302
311
|
}
|
|
303
312
|
|
|
304
313
|
enum RpcOperation {
|
package/lib/service.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Cancellable } from "./cancellable";
|
|
2
|
+
import { Signals, Signal } from "./signals";
|
|
3
|
+
|
|
4
|
+
import { inspect } from "util";
|
|
5
|
+
|
|
6
|
+
export class Service {
|
|
7
|
+
close: Signal<ServiceCloseHandler>;
|
|
8
|
+
message: Signal<ServiceMessageHandler>;
|
|
9
|
+
|
|
10
|
+
constructor(private impl: any) {
|
|
11
|
+
const signals: Signals = impl.signals;
|
|
12
|
+
this.close = new Signal<ServiceCloseHandler>(signals, "close");
|
|
13
|
+
this.message = new Signal<ServiceMessageHandler>(signals, "message");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
activate(cancellable?: Cancellable): Promise<void> {
|
|
17
|
+
return this.impl.activate(cancellable);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
cancel(cancellable?: Cancellable): Promise<void> {
|
|
21
|
+
return this.impl.cancel(cancellable);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
request(parameters: any, cancellable?: Cancellable): Promise<any> {
|
|
25
|
+
return this.impl.request(parameters, cancellable);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
[inspect.custom](depth, options) {
|
|
29
|
+
return "Service {}";
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type ServiceCloseHandler = () => void;
|
|
34
|
+
export type ServiceMessageHandler = (message: any) => void;
|
package/package.json
CHANGED
package/releng/deps.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[dependencies]
|
|
2
|
-
version = "
|
|
3
|
-
bootstrap_version = "
|
|
2
|
+
version = "20240601"
|
|
3
|
+
bootstrap_version = "20240531"
|
|
4
4
|
|
|
5
5
|
[ninja]
|
|
6
6
|
scope = "toolchain"
|
|
@@ -176,7 +176,7 @@ dependencies = [
|
|
|
176
176
|
|
|
177
177
|
[glib-networking]
|
|
178
178
|
name = "glib-networking"
|
|
179
|
-
version = "
|
|
179
|
+
version = "d276694f40cbadb7e19a3d82ceb7305c819fbda8"
|
|
180
180
|
url = "https://github.com/frida/glib-networking.git"
|
|
181
181
|
options = [
|
|
182
182
|
"-Dgnutls=disabled",
|
|
@@ -192,7 +192,7 @@ dependencies = [
|
|
|
192
192
|
|
|
193
193
|
[libnice]
|
|
194
194
|
name = "libnice"
|
|
195
|
-
version = "
|
|
195
|
+
version = "e12567b0a16a0c2eb5dfe5e0782baba8496772ff"
|
|
196
196
|
url = "https://github.com/frida/libnice.git"
|
|
197
197
|
options = [
|
|
198
198
|
"-Dgupnp=disabled",
|
|
@@ -208,8 +208,8 @@ dependencies = [
|
|
|
208
208
|
]
|
|
209
209
|
|
|
210
210
|
[lwip]
|
|
211
|
-
name = "
|
|
212
|
-
version = "
|
|
211
|
+
name = "lwIP"
|
|
212
|
+
version = "9f64c40cac2e77eddf6b3913961fdd056d307716"
|
|
213
213
|
url = "https://github.com/frida/lwip.git"
|
|
214
214
|
options = [
|
|
215
215
|
"-Dipv4=disabled",
|
|
@@ -284,7 +284,7 @@ options = [
|
|
|
284
284
|
|
|
285
285
|
[ngtcp2]
|
|
286
286
|
name = "ngtcp2"
|
|
287
|
-
version = "
|
|
287
|
+
version = "cc5cf523615b7fa7a9340234c7eec3c3645ae969"
|
|
288
288
|
url = "https://github.com/frida/ngtcp2.git"
|
|
289
289
|
dependencies = [
|
|
290
290
|
"openssl",
|
|
@@ -355,7 +355,7 @@ url = "https://github.com/frida/tinycc.git"
|
|
|
355
355
|
|
|
356
356
|
[openssl]
|
|
357
357
|
name = "OpenSSL"
|
|
358
|
-
version = "
|
|
358
|
+
version = "ca4781aaf7910b623d3ae21c6a017e7e9ca6936c"
|
|
359
359
|
url = "https://github.com/frida/openssl.git"
|
|
360
360
|
options = [
|
|
361
361
|
"-Dcli=disabled",
|
package/src/addon.cc
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
#include "relay.h"
|
|
15
15
|
#include "runtime.h"
|
|
16
16
|
#include "script.h"
|
|
17
|
+
#include "service.h"
|
|
17
18
|
#include "session.h"
|
|
18
19
|
#include "signals.h"
|
|
19
20
|
#include "spawn.h"
|
|
@@ -47,6 +48,7 @@ static void InitAll(Local<Object> exports,
|
|
|
47
48
|
Child::Init(exports, runtime);
|
|
48
49
|
Crash::Init(exports, runtime);
|
|
49
50
|
Bus::Init(exports, runtime);
|
|
51
|
+
Service::Init(exports, runtime);
|
|
50
52
|
Session::Init(exports, runtime);
|
|
51
53
|
Script::Init(exports, runtime);
|
|
52
54
|
Relay::Init(exports, runtime);
|
package/src/device.cc
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
#include "iostream.h"
|
|
8
8
|
#include "operation.h"
|
|
9
9
|
#include "process.h"
|
|
10
|
+
#include "service.h"
|
|
10
11
|
#include "session.h"
|
|
11
12
|
#include "signals.h"
|
|
12
13
|
#include "spawn.h"
|
|
@@ -81,6 +82,7 @@ void Device::Init(Local<Object> exports, Runtime* runtime) {
|
|
|
81
82
|
Nan::SetPrototypeMethod(tpl, "injectLibraryFile", InjectLibraryFile);
|
|
82
83
|
Nan::SetPrototypeMethod(tpl, "injectLibraryBlob", InjectLibraryBlob);
|
|
83
84
|
Nan::SetPrototypeMethod(tpl, "openChannel", OpenChannel);
|
|
85
|
+
Nan::SetPrototypeMethod(tpl, "openService", OpenService);
|
|
84
86
|
Nan::SetPrototypeMethod(tpl, "unpair", Unpair);
|
|
85
87
|
|
|
86
88
|
auto ctor = Nan::GetFunction(tpl).ToLocalChecked();
|
|
@@ -760,20 +762,8 @@ NAN_METHOD(Device::Spawn) {
|
|
|
760
762
|
|
|
761
763
|
Nan::Utf8String key_str(key);
|
|
762
764
|
|
|
763
|
-
GVariant* raw_value;
|
|
764
|
-
if (value->IsBoolean()) {
|
|
765
|
-
raw_value = g_variant_new_boolean(
|
|
766
|
-
Local<Boolean>::Cast(value)->Value());
|
|
767
|
-
} else if (value->IsNumber()) {
|
|
768
|
-
raw_value = g_variant_new_int64(
|
|
769
|
-
static_cast<gint64>(Local<Number>::Cast(value)->Value()));
|
|
770
|
-
} else {
|
|
771
|
-
Nan::Utf8String value_str(value);
|
|
772
|
-
raw_value = g_variant_new_string(*value_str);
|
|
773
|
-
}
|
|
774
|
-
|
|
775
765
|
g_hash_table_insert(aux, g_strdup(*key_str),
|
|
776
|
-
g_variant_ref_sink(
|
|
766
|
+
g_variant_ref_sink(Runtime::ValueToVariant(value)));
|
|
777
767
|
}
|
|
778
768
|
} else {
|
|
779
769
|
Nan::ThrowTypeError("Bad argument, 'aux' must be an object");
|
|
@@ -1230,6 +1220,56 @@ NAN_METHOD(Device::OpenChannel) {
|
|
|
1230
1220
|
|
|
1231
1221
|
namespace {
|
|
1232
1222
|
|
|
1223
|
+
class OpenServiceOperation : public Operation<FridaDevice> {
|
|
1224
|
+
public:
|
|
1225
|
+
OpenServiceOperation(gchar* address)
|
|
1226
|
+
: address_(address) {
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
~OpenServiceOperation() {
|
|
1230
|
+
g_free(address_);
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
protected:
|
|
1234
|
+
void Begin() {
|
|
1235
|
+
frida_device_open_service(handle_, address_, cancellable_, OnReady, this);
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
void End(GAsyncResult* result, GError** error) {
|
|
1239
|
+
service_ = frida_device_open_service_finish(handle_, result, error);
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
Local<Value> Result(Isolate* isolate) {
|
|
1243
|
+
auto wrapper = Service::New(service_, runtime_);
|
|
1244
|
+
g_object_unref(service_);
|
|
1245
|
+
return wrapper;
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
private:
|
|
1249
|
+
gchar* address_;
|
|
1250
|
+
FridaService* service_;
|
|
1251
|
+
};
|
|
1252
|
+
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
NAN_METHOD(Device::OpenService) {
|
|
1256
|
+
auto isolate = info.GetIsolate();
|
|
1257
|
+
auto wrapper = ObjectWrap::Unwrap<Device>(info.Holder());
|
|
1258
|
+
|
|
1259
|
+
if (info.Length() < 1 || !info[0]->IsString()) {
|
|
1260
|
+
Nan::ThrowTypeError("Bad argument");
|
|
1261
|
+
return;
|
|
1262
|
+
}
|
|
1263
|
+
Nan::Utf8String address(info[0]);
|
|
1264
|
+
|
|
1265
|
+
auto operation = new OpenServiceOperation(g_strdup(*address));
|
|
1266
|
+
operation->Schedule(isolate, wrapper, info);
|
|
1267
|
+
|
|
1268
|
+
info.GetReturnValue().Set(operation->GetPromise(isolate));
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
namespace {
|
|
1272
|
+
|
|
1233
1273
|
class UnpairOperation : public Operation<FridaDevice> {
|
|
1234
1274
|
protected:
|
|
1235
1275
|
void Begin() {
|
package/src/device.h
CHANGED
|
@@ -41,6 +41,7 @@ class Device : public GLibObject {
|
|
|
41
41
|
static NAN_METHOD(InjectLibraryFile);
|
|
42
42
|
static NAN_METHOD(InjectLibraryBlob);
|
|
43
43
|
static NAN_METHOD(OpenChannel);
|
|
44
|
+
static NAN_METHOD(OpenService);
|
|
44
45
|
static NAN_METHOD(Unpair);
|
|
45
46
|
|
|
46
47
|
static v8::Local<v8::Value> TransformSignal(const gchar* name, guint index,
|
package/src/meson.build
CHANGED
package/src/runtime.cc
CHANGED
|
@@ -251,66 +251,155 @@ Local<Value> Runtime::ValueFromParametersDict(GHashTable* dict) {
|
|
|
251
251
|
return result;
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
Local<Value>
|
|
255
|
-
if (
|
|
256
|
-
|
|
254
|
+
GVariant* Runtime::ValueToVariant(Local<Value> value) {
|
|
255
|
+
if (value->IsString()) {
|
|
256
|
+
Nan::Utf8String str(value);
|
|
257
|
+
return g_variant_new_string(*str);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (value->IsNumber()) {
|
|
261
|
+
return g_variant_new_int64(
|
|
262
|
+
static_cast<gint64>(Local<Number>::Cast(value)->Value()));
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (value->IsBoolean()) {
|
|
266
|
+
return g_variant_new_boolean(Local<Boolean>::Cast(value)->Value());
|
|
267
|
+
}
|
|
257
268
|
|
|
258
|
-
if (
|
|
259
|
-
|
|
269
|
+
if (node::Buffer::HasInstance(value)) {
|
|
270
|
+
auto size = node::Buffer::Length(value);
|
|
271
|
+
auto copy = g_memdup2(node::Buffer::Data(value), size);
|
|
272
|
+
return g_variant_new_from_data(G_VARIANT_TYPE_BYTESTRING, copy, size, TRUE,
|
|
273
|
+
g_free, copy);
|
|
274
|
+
}
|
|
260
275
|
|
|
261
|
-
if (
|
|
262
|
-
|
|
276
|
+
if (value->IsArray()) {
|
|
277
|
+
GVariantBuilder builder;
|
|
278
|
+
g_variant_builder_init(&builder, G_VARIANT_TYPE ("av"));
|
|
263
279
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
280
|
+
auto array = Local<Array>::Cast(value);
|
|
281
|
+
uint32_t n = array->Length();
|
|
282
|
+
for (uint32_t i = 0; i != n; i++) {
|
|
283
|
+
auto element_value = Nan::Get(array, i).ToLocalChecked();
|
|
284
|
+
g_variant_builder_add(&builder, "v", ValueToVariant(element_value));
|
|
285
|
+
}
|
|
267
286
|
|
|
268
|
-
return
|
|
269
|
-
.ToLocalChecked();
|
|
287
|
+
return g_variant_builder_end(&builder);
|
|
270
288
|
}
|
|
271
289
|
|
|
272
|
-
if (
|
|
273
|
-
|
|
290
|
+
if (value->IsObject()) {
|
|
291
|
+
GVariantBuilder builder;
|
|
292
|
+
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
|
|
274
293
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
GVariant* raw_value;
|
|
294
|
+
auto isolate = Isolate::GetCurrent();
|
|
295
|
+
auto context = isolate->GetCurrentContext();
|
|
278
296
|
|
|
279
|
-
|
|
297
|
+
auto object = Local<Object>::Cast(value);
|
|
280
298
|
|
|
281
|
-
|
|
282
|
-
|
|
299
|
+
Local<Array> names(object->GetOwnPropertyNames(context).ToLocalChecked());
|
|
300
|
+
uint32_t n = names->Length();
|
|
283
301
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
Nan::
|
|
302
|
+
for (uint32_t i = 0; i != n; i++) {
|
|
303
|
+
auto key = Nan::Get(names, i).ToLocalChecked();
|
|
304
|
+
auto val = Nan::Get(object, key).ToLocalChecked();
|
|
287
305
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
g_free(raw_key);
|
|
306
|
+
Nan::Utf8String key_str(key);
|
|
307
|
+
g_variant_builder_add(&builder, "{sv}", *key_str, ValueToVariant(val));
|
|
291
308
|
}
|
|
292
309
|
|
|
293
|
-
return
|
|
310
|
+
return g_variant_builder_end(&builder);
|
|
294
311
|
}
|
|
295
312
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
313
|
+
Nan::ThrowTypeError("Bad argument, expected value serializable to GVariant");
|
|
314
|
+
return NULL;
|
|
315
|
+
}
|
|
299
316
|
|
|
300
|
-
|
|
317
|
+
Local<Value> Runtime::ValueFromVariant(GVariant* v) {
|
|
318
|
+
switch (g_variant_classify(v)) {
|
|
319
|
+
case G_VARIANT_CLASS_STRING:
|
|
320
|
+
return Nan::New<String>(g_variant_get_string(v, NULL)).ToLocalChecked();
|
|
321
|
+
case G_VARIANT_CLASS_INT64:
|
|
322
|
+
return Nan::New<Number>(static_cast<double>(g_variant_get_int64(v)));
|
|
323
|
+
case G_VARIANT_CLASS_UINT64:
|
|
324
|
+
return Nan::New<Number>(static_cast<double>(g_variant_get_uint64(v)));
|
|
325
|
+
case G_VARIANT_CLASS_DOUBLE:
|
|
326
|
+
return Nan::New<Number>(static_cast<double>(g_variant_get_double(v)));
|
|
327
|
+
case G_VARIANT_CLASS_BOOLEAN:
|
|
328
|
+
return Nan::New<Boolean>(static_cast<bool>(g_variant_get_boolean(v)));
|
|
329
|
+
case G_VARIANT_CLASS_ARRAY:
|
|
330
|
+
if (g_variant_is_of_type(v, G_VARIANT_TYPE("ay"))) {
|
|
331
|
+
return ValueFromVariantByteArray(v);
|
|
332
|
+
}
|
|
301
333
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
g_variant_unref(child);
|
|
306
|
-
}
|
|
334
|
+
if (g_variant_is_of_type(v, G_VARIANT_TYPE_VARDICT)) {
|
|
335
|
+
return ValueFromVariantDict(v);
|
|
336
|
+
}
|
|
307
337
|
|
|
308
|
-
|
|
338
|
+
if (g_variant_is_of_type(v, G_VARIANT_TYPE_ARRAY)) {
|
|
339
|
+
return ValueFromVariantArray(v);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
break;
|
|
343
|
+
case G_VARIANT_CLASS_TUPLE:
|
|
344
|
+
return Nan::Undefined();
|
|
345
|
+
default:
|
|
346
|
+
break;
|
|
309
347
|
}
|
|
310
348
|
|
|
311
349
|
return Nan::Null();
|
|
312
350
|
}
|
|
313
351
|
|
|
352
|
+
Local<Value> Runtime::ValueFromVariantByteArray(GVariant* v) {
|
|
353
|
+
gsize size;
|
|
354
|
+
gconstpointer data = g_variant_get_fixed_array(v, &size, sizeof(guint8));
|
|
355
|
+
|
|
356
|
+
return Nan::CopyBuffer(static_cast<const char*>(data), size).ToLocalChecked();
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
Local<Value> Runtime::ValueFromVariantDict(GVariant* v) {
|
|
360
|
+
auto dict = Nan::New<Object>();
|
|
361
|
+
|
|
362
|
+
GVariantIter iter;
|
|
363
|
+
gchar* raw_key;
|
|
364
|
+
GVariant* raw_value;
|
|
365
|
+
|
|
366
|
+
g_variant_iter_init(&iter, v);
|
|
367
|
+
|
|
368
|
+
while (g_variant_iter_next(&iter, "{sv}", &raw_key, &raw_value)) {
|
|
369
|
+
char* canonicalized_key = ParameterNameFromC(raw_key);
|
|
370
|
+
|
|
371
|
+
Local<String> key = Nan::New(canonicalized_key).ToLocalChecked();
|
|
372
|
+
Local<Value> value = ValueFromVariant(raw_value);
|
|
373
|
+
Nan::Set(dict, key, value);
|
|
374
|
+
|
|
375
|
+
g_free(canonicalized_key);
|
|
376
|
+
g_variant_unref(raw_value);
|
|
377
|
+
g_free(raw_key);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return dict;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
Local<Value> Runtime::ValueFromVariantArray(GVariant* v) {
|
|
384
|
+
GVariantIter iter;
|
|
385
|
+
g_variant_iter_init(&iter, v);
|
|
386
|
+
|
|
387
|
+
auto array = Nan::New<Array>(g_variant_iter_n_children(&iter));
|
|
388
|
+
|
|
389
|
+
GVariant* child;
|
|
390
|
+
for (int i = 0; (child = g_variant_iter_next_value(&iter)) != NULL; i++) {
|
|
391
|
+
if (g_variant_is_of_type(child, G_VARIANT_TYPE_VARIANT)) {
|
|
392
|
+
GVariant* inner = g_variant_get_variant(child);
|
|
393
|
+
g_variant_unref(child);
|
|
394
|
+
child = inner;
|
|
395
|
+
}
|
|
396
|
+
Nan::Set(array, i, ValueFromVariant(child));
|
|
397
|
+
g_variant_unref(child);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return array;
|
|
401
|
+
}
|
|
402
|
+
|
|
314
403
|
Local<Object> Runtime::ValueFromSocketAddress(GSocketAddress* address) {
|
|
315
404
|
auto result = Nan::New<Object>();
|
|
316
405
|
|
package/src/runtime.h
CHANGED
|
@@ -38,6 +38,7 @@ class Runtime {
|
|
|
38
38
|
|
|
39
39
|
static v8::Local<v8::Value> ValueFromParametersDict(GHashTable* dict);
|
|
40
40
|
|
|
41
|
+
static GVariant* ValueToVariant(v8::Local<v8::Value> value);
|
|
41
42
|
static v8::Local<v8::Value> ValueFromVariant(GVariant* v);
|
|
42
43
|
|
|
43
44
|
static v8::Local<v8::Object> ValueFromSocketAddress(GSocketAddress* address);
|
|
@@ -49,6 +50,10 @@ class Runtime {
|
|
|
49
50
|
static char* ParameterNameFromC(const char* cname);
|
|
50
51
|
|
|
51
52
|
private:
|
|
53
|
+
static v8::Local<v8::Value> ValueFromVariantByteArray(GVariant* v);
|
|
54
|
+
static v8::Local<v8::Value> ValueFromVariantDict(GVariant* v);
|
|
55
|
+
static v8::Local<v8::Value> ValueFromVariantArray(GVariant* v);
|
|
56
|
+
|
|
52
57
|
UVContext* uv_context_;
|
|
53
58
|
GLibContext* glib_context_;
|
|
54
59
|
|
package/src/service.cc
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
#include "service.h"
|
|
2
|
+
|
|
3
|
+
#include "operation.h"
|
|
4
|
+
#include "signals.h"
|
|
5
|
+
#include "usage_monitor.h"
|
|
6
|
+
|
|
7
|
+
#include <cstring>
|
|
8
|
+
|
|
9
|
+
#define SERVICE_DATA_CONSTRUCTOR "service:ctor"
|
|
10
|
+
|
|
11
|
+
using std::strcmp;
|
|
12
|
+
using v8::External;
|
|
13
|
+
using v8::Function;
|
|
14
|
+
using v8::Isolate;
|
|
15
|
+
using v8::Local;
|
|
16
|
+
using v8::Object;
|
|
17
|
+
using v8::Persistent;
|
|
18
|
+
using v8::Value;
|
|
19
|
+
|
|
20
|
+
namespace frida {
|
|
21
|
+
|
|
22
|
+
Service::Service(FridaService* handle, Runtime* runtime)
|
|
23
|
+
: GLibObject(handle, runtime),
|
|
24
|
+
usage_monitor_created_(false) {
|
|
25
|
+
g_object_ref(handle_);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
Service::~Service() {
|
|
29
|
+
g_object_unref(handle_);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
void Service::Init(Local<Object> exports, Runtime* runtime) {
|
|
33
|
+
auto isolate = Isolate::GetCurrent();
|
|
34
|
+
|
|
35
|
+
auto name = Nan::New("Service").ToLocalChecked();
|
|
36
|
+
auto tpl = CreateTemplate(name, New, runtime);
|
|
37
|
+
|
|
38
|
+
Nan::SetPrototypeMethod(tpl, "activate", Activate);
|
|
39
|
+
Nan::SetPrototypeMethod(tpl, "cancel", Cancel);
|
|
40
|
+
Nan::SetPrototypeMethod(tpl, "request", Request);
|
|
41
|
+
|
|
42
|
+
auto ctor = Nan::GetFunction(tpl).ToLocalChecked();
|
|
43
|
+
Nan::Set(exports, name, ctor);
|
|
44
|
+
runtime->SetDataPointer(SERVICE_DATA_CONSTRUCTOR,
|
|
45
|
+
new Persistent<Function>(isolate, ctor));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
Local<Object> Service::New(gpointer handle, Runtime* runtime) {
|
|
49
|
+
auto ctor = Nan::New<Function>(
|
|
50
|
+
*static_cast<Persistent<Function>*>(
|
|
51
|
+
runtime->GetDataPointer(SERVICE_DATA_CONSTRUCTOR)));
|
|
52
|
+
const int argc = 1;
|
|
53
|
+
Local<Value> argv[argc] = { Nan::New<External>(handle) };
|
|
54
|
+
return Nan::NewInstance(ctor, argc, argv).ToLocalChecked();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
NAN_METHOD(Service::New) {
|
|
58
|
+
if (!info.IsConstructCall()) {
|
|
59
|
+
Nan::ThrowError("Use the `new` keyword to create a new instance");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (info.Length() != 1 || !info[0]->IsExternal()) {
|
|
64
|
+
Nan::ThrowTypeError("Bad argument, expected raw handle");
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
auto runtime = GetRuntimeFromConstructorArgs(info);
|
|
69
|
+
|
|
70
|
+
auto handle = static_cast<FridaService*>(
|
|
71
|
+
Local<External>::Cast(info[0])->Value());
|
|
72
|
+
auto wrapper = new Service(handle, runtime);
|
|
73
|
+
auto obj = info.This();
|
|
74
|
+
wrapper->Wrap(obj);
|
|
75
|
+
auto signals_obj = Signals::New(handle, runtime, TransformSignal, runtime);
|
|
76
|
+
|
|
77
|
+
Nan::Set(obj, Nan::New("signals").ToLocalChecked(), signals_obj);
|
|
78
|
+
|
|
79
|
+
auto signals_wrapper = ObjectWrap::Unwrap<Signals>(signals_obj);
|
|
80
|
+
signals_wrapper->SetConnectCallback(OnConnect, wrapper);
|
|
81
|
+
|
|
82
|
+
info.GetReturnValue().Set(obj);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
namespace {
|
|
86
|
+
|
|
87
|
+
class ActivateOperation : public Operation<FridaService> {
|
|
88
|
+
protected:
|
|
89
|
+
void Begin() {
|
|
90
|
+
frida_service_activate(handle_, cancellable_, OnReady, this);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
void End(GAsyncResult* result, GError** error) {
|
|
94
|
+
frida_service_activate_finish(handle_, result, error);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
Local<Value> Result(Isolate* isolate) {
|
|
98
|
+
return Nan::Undefined();
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
NAN_METHOD(Service::Activate) {
|
|
105
|
+
auto isolate = info.GetIsolate();
|
|
106
|
+
auto wrapper = ObjectWrap::Unwrap<Service>(info.Holder());
|
|
107
|
+
|
|
108
|
+
auto operation = new ActivateOperation();
|
|
109
|
+
operation->Schedule(isolate, wrapper, info);
|
|
110
|
+
|
|
111
|
+
info.GetReturnValue().Set(operation->GetPromise(isolate));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
namespace {
|
|
115
|
+
|
|
116
|
+
class CancelOperation : public Operation<FridaService> {
|
|
117
|
+
protected:
|
|
118
|
+
void Begin() {
|
|
119
|
+
frida_service_cancel(handle_, cancellable_, OnReady, this);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
void End(GAsyncResult* result, GError** error) {
|
|
123
|
+
frida_service_cancel_finish(handle_, result, error);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
Local<Value> Result(Isolate* isolate) {
|
|
127
|
+
return Nan::Undefined();
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
NAN_METHOD(Service::Cancel) {
|
|
134
|
+
auto isolate = info.GetIsolate();
|
|
135
|
+
auto wrapper = ObjectWrap::Unwrap<Service>(info.Holder());
|
|
136
|
+
|
|
137
|
+
auto operation = new CancelOperation();
|
|
138
|
+
operation->Schedule(isolate, wrapper, info);
|
|
139
|
+
|
|
140
|
+
info.GetReturnValue().Set(operation->GetPromise(isolate));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
namespace {
|
|
144
|
+
|
|
145
|
+
class RequestOperation : public Operation<FridaService> {
|
|
146
|
+
public:
|
|
147
|
+
RequestOperation(GVariant* parameters)
|
|
148
|
+
: parameters_(parameters) {
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
~RequestOperation() {
|
|
152
|
+
g_variant_unref(parameters_);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
protected:
|
|
156
|
+
void Begin() {
|
|
157
|
+
frida_service_request(handle_, parameters_, cancellable_, OnReady, this);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
void End(GAsyncResult* result, GError** error) {
|
|
161
|
+
response_ = frida_service_request_finish(handle_, result, error);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
Local<Value> Result(Isolate* isolate) {
|
|
165
|
+
auto wrapper = Runtime::ValueFromVariant(response_);
|
|
166
|
+
g_variant_unref(response_);
|
|
167
|
+
return wrapper;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private:
|
|
171
|
+
GVariant* parameters_;
|
|
172
|
+
GVariant* response_;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
NAN_METHOD(Service::Request) {
|
|
178
|
+
auto isolate = info.GetIsolate();
|
|
179
|
+
auto wrapper = ObjectWrap::Unwrap<Service>(info.Holder());
|
|
180
|
+
|
|
181
|
+
if (info.Length() == 0) {
|
|
182
|
+
Nan::ThrowTypeError("Expected a parameters value");
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
auto parameters = Runtime::ValueToVariant(info[0]);
|
|
187
|
+
if (parameters == NULL) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
auto operation = new RequestOperation(parameters);
|
|
192
|
+
operation->Schedule(isolate, wrapper, info);
|
|
193
|
+
|
|
194
|
+
info.GetReturnValue().Set(operation->GetPromise(isolate));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
Local<Value> Service::TransformSignal(const gchar* name, guint index,
|
|
198
|
+
const GValue* value, gpointer user_data) {
|
|
199
|
+
if (index != 0 || strcmp(name, "message") != 0)
|
|
200
|
+
return Local<Value>();
|
|
201
|
+
return Runtime::ValueFromVariant(g_value_get_variant(value));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
void Service::OnConnect(const gchar* name, gpointer user_data) {
|
|
205
|
+
auto wrapper = static_cast<Service*>(user_data);
|
|
206
|
+
|
|
207
|
+
if (ShouldStayAliveToEmit(name))
|
|
208
|
+
wrapper->EnsureUsageMonitorCreated();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
bool Service::ShouldStayAliveToEmit(const gchar* name) {
|
|
212
|
+
return strcmp(name, "close") == 0 || strcmp(name, "message") == 0;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
void Service::EnsureUsageMonitorCreated() {
|
|
216
|
+
if (!usage_monitor_created_) {
|
|
217
|
+
usage_monitor_created_ = true;
|
|
218
|
+
auto monitor =
|
|
219
|
+
new UsageMonitor<FridaService>(frida_service_is_closed, "close");
|
|
220
|
+
monitor->Enable(this);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
}
|
package/src/service.h
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#ifndef FRIDANODE_SERVICE_H
|
|
2
|
+
#define FRIDANODE_SERVICE_H
|
|
3
|
+
|
|
4
|
+
#include "glib_object.h"
|
|
5
|
+
|
|
6
|
+
#include <frida-core.h>
|
|
7
|
+
|
|
8
|
+
namespace frida {
|
|
9
|
+
|
|
10
|
+
class Service : public GLibObject {
|
|
11
|
+
public:
|
|
12
|
+
static void Init(v8::Local<v8::Object> exports, Runtime* runtime);
|
|
13
|
+
static v8::Local<v8::Object> New(gpointer handle, Runtime* runtime);
|
|
14
|
+
|
|
15
|
+
private:
|
|
16
|
+
explicit Service(FridaService* handle, Runtime* runtime);
|
|
17
|
+
~Service();
|
|
18
|
+
|
|
19
|
+
static NAN_METHOD(New);
|
|
20
|
+
|
|
21
|
+
static NAN_METHOD(Activate);
|
|
22
|
+
static NAN_METHOD(Cancel);
|
|
23
|
+
static NAN_METHOD(Request);
|
|
24
|
+
|
|
25
|
+
static v8::Local<v8::Value> TransformSignal(const gchar* name, guint index,
|
|
26
|
+
const GValue* value, gpointer user_data);
|
|
27
|
+
static void OnConnect(const gchar* signal, gpointer user_data);
|
|
28
|
+
static bool ShouldStayAliveToEmit(const gchar* signal);
|
|
29
|
+
void EnsureUsageMonitorCreated();
|
|
30
|
+
|
|
31
|
+
bool usage_monitor_created_;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#endif
|
package/test/script.ts
CHANGED
|
@@ -21,22 +21,30 @@ describe("Script", function () {
|
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
it("should support rpc", async () => {
|
|
24
|
-
const script = await session.createScript(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
24
|
+
const script = await session.createScript(`
|
|
25
|
+
rpc.exports = {
|
|
26
|
+
add(a, b) {
|
|
27
|
+
const result = a + b;
|
|
28
|
+
if (result < 0)
|
|
29
|
+
throw new Error('no');
|
|
30
|
+
return result;
|
|
31
|
+
},
|
|
32
|
+
sub(a, b) {
|
|
33
|
+
return a - b;
|
|
34
|
+
},
|
|
35
|
+
speak() {
|
|
36
|
+
const buf = Memory.allocUtf8String('Yo');
|
|
37
|
+
return Memory.readByteArray(buf, 2);
|
|
38
|
+
},
|
|
39
|
+
speakWithMetadata() {
|
|
40
|
+
const buf = Memory.allocUtf8String('Yo');
|
|
41
|
+
return ['soft', Memory.readByteArray(buf, 2)];
|
|
42
|
+
},
|
|
43
|
+
processData(val, data) {
|
|
44
|
+
return { val, dump: hexdump(data, { header: false }) };
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
`);
|
|
40
48
|
await script.load();
|
|
41
49
|
|
|
42
50
|
const agent = script.exports;
|
|
@@ -51,18 +59,27 @@ describe("Script", function () {
|
|
|
51
59
|
thrownException = e;
|
|
52
60
|
}
|
|
53
61
|
expect(thrownException).to.not.be.equal(null);
|
|
54
|
-
expect(thrownException.message).to.equal("
|
|
62
|
+
expect(thrownException.message).to.equal("no");
|
|
55
63
|
|
|
56
64
|
const buf = await agent.speak();
|
|
57
65
|
expect(buf.toJSON().data).to.deep.equal([0x59, 0x6f]);
|
|
66
|
+
|
|
67
|
+
const [meta, data] = await agent.speakWithMetadata();
|
|
68
|
+
expect(meta).to.equal("soft");
|
|
69
|
+
expect(data.toJSON().data).to.deep.equal([0x59, 0x6f]);
|
|
70
|
+
|
|
71
|
+
const result = await agent.processData(1337, Buffer.from([0x13, 0x37]));
|
|
72
|
+
expect(result.val).to.equal(1337);
|
|
73
|
+
expect(result.dump).to.equal("00000000 13 37 .7");
|
|
58
74
|
});
|
|
59
75
|
|
|
60
76
|
it("should fail rpc request if post() fails", async () => {
|
|
61
|
-
const script = await session.createScript(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
77
|
+
const script = await session.createScript(`
|
|
78
|
+
rpc.exports = {
|
|
79
|
+
init() {
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
`);
|
|
66
83
|
await script.load();
|
|
67
84
|
|
|
68
85
|
await session.detach();
|
|
@@ -78,12 +95,13 @@ describe("Script", function () {
|
|
|
78
95
|
});
|
|
79
96
|
|
|
80
97
|
it("should fail rpc request if script is unloaded mid-request", async () => {
|
|
81
|
-
const script = await session.createScript(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
98
|
+
const script = await session.createScript(`
|
|
99
|
+
rpc.exports = {
|
|
100
|
+
waitForever() {
|
|
101
|
+
return new Promise(() => {});
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
`);
|
|
87
105
|
await script.load();
|
|
88
106
|
|
|
89
107
|
setTimeout(() => script.unload(), 100);
|
|
@@ -99,12 +117,13 @@ describe("Script", function () {
|
|
|
99
117
|
});
|
|
100
118
|
|
|
101
119
|
it("should fail rpc request if session gets detached mid-request", async () => {
|
|
102
|
-
const script = await session.createScript(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
120
|
+
const script = await session.createScript(`
|
|
121
|
+
rpc.exports = {
|
|
122
|
+
waitForever() {
|
|
123
|
+
return new Promise(() => {});
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
`);
|
|
108
127
|
await script.load();
|
|
109
128
|
|
|
110
129
|
setTimeout(() => target.stop(), 100);
|
|
@@ -122,12 +141,13 @@ describe("Script", function () {
|
|
|
122
141
|
it("should fail rpc request if cancelled mid-request", async () => {
|
|
123
142
|
const cancellable = new frida.Cancellable();
|
|
124
143
|
|
|
125
|
-
const script = await session.createScript(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
144
|
+
const script = await session.createScript(`
|
|
145
|
+
rpc.exports = {
|
|
146
|
+
waitForever() {
|
|
147
|
+
return new Promise(() => {});
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
`, {}, cancellable);
|
|
131
151
|
await script.load(cancellable);
|
|
132
152
|
|
|
133
153
|
setTimeout(() => cancellable.cancel(), 100);
|
|
@@ -148,9 +168,10 @@ describe("Script", function () {
|
|
|
148
168
|
|
|
149
169
|
async function load(): Promise<frida.ScriptExports> {
|
|
150
170
|
const script = await session.createScript(`
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
171
|
+
rpc.exports.hello = function () {
|
|
172
|
+
return "Is it me you're looking for?";
|
|
173
|
+
};`
|
|
174
|
+
);
|
|
154
175
|
await script.load();
|
|
155
176
|
|
|
156
177
|
const api = script.exports;
|