frida 16.2.4 → 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 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].concat(params));
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
- value = (data !== null) ? data : params[0];
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
- return rpcController.request("call", [property, args], cancellable);
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;
@@ -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/build.py CHANGED
@@ -5,12 +5,11 @@ import sys
5
5
 
6
6
 
7
7
  def main(argv: list[str]):
8
- outdir, privdir, npm, package_json, package_lock_json, tsconfig, \
9
- *sources = [Path(s) for s in argv[1:]]
8
+ outdir, privdir, npm, package_json, tsconfig, *sources = [Path(s) for s in argv[1:]]
10
9
 
11
10
  try:
12
11
  privdir.mkdir(exist_ok=True)
13
- for asset in [package_json, package_lock_json, tsconfig]:
12
+ for asset in [package_json, tsconfig]:
14
13
  shutil.copy(asset, privdir)
15
14
 
16
15
  libdir = privdir / "lib"
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
@@ -18,6 +18,7 @@ sources = [
18
18
  'process.ts',
19
19
  'relay.ts',
20
20
  'script.ts',
21
+ 'service.ts',
21
22
  'session.ts',
22
23
  'signals.ts',
23
24
  'socket_address.ts',
@@ -44,7 +45,6 @@ custom_target('js',
44
45
  '@PRIVATE_DIR@',
45
46
  npm,
46
47
  package_json,
47
- package_lock_json,
48
48
  tsconfig_json,
49
49
  '@INPUT@',
50
50
  ],
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].concat(params));
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
- value = (data !== null) ? data : params[0];
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
- return rpcController.request("call", [property, args], cancellable);
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/meson.build CHANGED
@@ -154,7 +154,6 @@ package_json = custom_target('package.json',
154
154
  install: true,
155
155
  install_dir: pkg_install_dir,
156
156
  )
157
- package_lock_json = files('package-lock.json')
158
157
  tsconfig_json = files('tsconfig.json')
159
158
 
160
159
  subdir('lib')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "frida",
3
- "version": "16.2.4",
3
+ "version": "16.3.0",
4
4
  "authors": [
5
5
  "Frida Developers"
6
6
  ],
package/releng/deps.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [dependencies]
2
- version = "20240514"
3
- bootstrap_version = "20240502"
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 = "8321b417f1d7907ef5b24e62e7f176e42be9cf8e"
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 = "a0f43f0ea5755ce0374613b0a157be33a9f7d82e"
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 = "lwip"
212
- version = "f669c58256feff196aeeecc712761ce01a0b2a56"
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 = "21a9ba1667985ad60e9acafc07af493555deaf53"
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 = "95d037b2acc432039da4f506d25b6a01577d8bc5"
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(raw_value));
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
@@ -8,6 +8,7 @@ frida_binding_sources = files(
8
8
  'child.cc',
9
9
  'crash.cc',
10
10
  'bus.cc',
11
+ 'service.cc',
11
12
  'session.cc',
12
13
  'script.cc',
13
14
  'relay.cc',
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> Runtime::ValueFromVariant(GVariant* v) {
255
- if (g_variant_is_of_type(v, G_VARIANT_TYPE_STRING))
256
- return Nan::New<String>(g_variant_get_string(v, NULL)).ToLocalChecked();
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 (g_variant_is_of_type(v, G_VARIANT_TYPE_INT64))
259
- return Nan::New<Number>(static_cast<double>(g_variant_get_int64(v)));
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 (g_variant_is_of_type(v, G_VARIANT_TYPE_BOOLEAN))
262
- return Nan::New<Boolean>(static_cast<bool>(g_variant_get_boolean(v)));
276
+ if (value->IsArray()) {
277
+ GVariantBuilder builder;
278
+ g_variant_builder_init(&builder, G_VARIANT_TYPE ("av"));
263
279
 
264
- if (g_variant_is_of_type(v, G_VARIANT_TYPE("ay"))) {
265
- gsize size;
266
- gconstpointer data = g_variant_get_fixed_array(v, &size, sizeof(guint8));
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 Nan::CopyBuffer(static_cast<const char*>(data), size)
269
- .ToLocalChecked();
287
+ return g_variant_builder_end(&builder);
270
288
  }
271
289
 
272
- if (g_variant_is_of_type(v, G_VARIANT_TYPE_VARDICT)) {
273
- auto dict = Nan::New<Object>();
290
+ if (value->IsObject()) {
291
+ GVariantBuilder builder;
292
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
274
293
 
275
- GVariantIter iter;
276
- gchar* raw_key;
277
- GVariant* raw_value;
294
+ auto isolate = Isolate::GetCurrent();
295
+ auto context = isolate->GetCurrentContext();
278
296
 
279
- g_variant_iter_init(&iter, v);
297
+ auto object = Local<Object>::Cast(value);
280
298
 
281
- while (g_variant_iter_next(&iter, "{sv}", &raw_key, &raw_value)) {
282
- char* canonicalized_key = ParameterNameFromC(raw_key);
299
+ Local<Array> names(object->GetOwnPropertyNames(context).ToLocalChecked());
300
+ uint32_t n = names->Length();
283
301
 
284
- Local<String> key = Nan::New(canonicalized_key).ToLocalChecked();
285
- Local<Value> value = ValueFromVariant(raw_value);
286
- Nan::Set(dict, key, value);
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
- g_free(canonicalized_key);
289
- g_variant_unref(raw_value);
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 dict;
310
+ return g_variant_builder_end(&builder);
294
311
  }
295
312
 
296
- if (g_variant_is_of_type(v, G_VARIANT_TYPE_ARRAY)) {
297
- GVariantIter iter;
298
- g_variant_iter_init(&iter, v);
313
+ Nan::ThrowTypeError("Bad argument, expected value serializable to GVariant");
314
+ return NULL;
315
+ }
299
316
 
300
- auto array = Nan::New<Array>(g_variant_iter_n_children(&iter));
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
- GVariant* child;
303
- for (int i = 0; (child = g_variant_iter_next_value(&iter)) != NULL; i++) {
304
- Nan::Set(array, i, ValueFromVariant(child));
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
- return array;
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
@@ -1,6 +1,6 @@
1
1
  [wrap-git]
2
2
  url = https://github.com/frida/frida-core.git
3
- revision = 16.2.4
3
+ revision = 16.3.0
4
4
  depth = 1
5
5
 
6
6
  [provide]
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
- "rpc.exports = {" +
26
- "add: function (a, b) {" +
27
- "var result = a + b;" +
28
- "if (result < 0)" +
29
- "throw new Error('No');" +
30
- "return result;" +
31
- "}," +
32
- "sub: function (a, b) {" +
33
- "return a - b;" +
34
- "}," +
35
- "speak: function () {" +
36
- "var buf = Memory.allocUtf8String('Yo');" +
37
- "return Memory.readByteArray(buf, 2);" +
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("No");
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
- "rpc.exports = {" +
63
- "init: function () {" +
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
- "rpc.exports = {" +
83
- "waitForever: function () {" +
84
- "return new Promise(function () {});" +
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
- "rpc.exports = {" +
104
- "waitForever: function () {" +
105
- "return new Promise(function () {});" +
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
- "rpc.exports = {" +
127
- "waitForever: function () {" +
128
- "return new Promise(function () {});" +
129
- "}" +
130
- "};", {}, cancellable);
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
- rpc.exports.hello = function () {
152
- return "Is it me you're looking for?";
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;