@efffrida/frida-tools 0.0.27 → 0.0.28
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/FridaDevice.d.ts +9 -8
- package/dist/FridaDevice.d.ts.map +1 -1
- package/dist/FridaDevice.js.map +1 -1
- package/dist/FridaDeviceAcquisitionError.d.ts +3 -18
- package/dist/FridaDeviceAcquisitionError.d.ts.map +1 -1
- package/dist/FridaDeviceAcquisitionError.js +2 -13
- package/dist/FridaDeviceAcquisitionError.js.map +1 -1
- package/dist/FridaScript.d.ts +12 -13
- package/dist/FridaScript.d.ts.map +1 -1
- package/dist/FridaScript.js +10 -9
- package/dist/FridaScript.js.map +1 -1
- package/dist/FridaSession.d.ts +21 -7
- package/dist/FridaSession.d.ts.map +1 -1
- package/dist/FridaSession.js.map +1 -1
- package/dist/FridaSessionError.d.ts +1 -16
- package/dist/FridaSessionError.d.ts.map +1 -1
- package/dist/FridaSessionError.js +2 -13
- package/dist/FridaSessionError.js.map +1 -1
- package/dist/internal/compiler.d.ts +2 -0
- package/dist/internal/compiler.d.ts.map +1 -0
- package/dist/internal/compiler.js +82 -0
- package/dist/internal/compiler.js.map +1 -0
- package/dist/internal/device.js +33 -32
- package/dist/internal/device.js.map +1 -1
- package/dist/internal/script.js +56 -105
- package/dist/internal/script.js.map +1 -1
- package/dist/internal/session.js +90 -48
- package/dist/internal/session.js.map +1 -1
- package/package.json +9 -16
- package/src/FridaDevice.ts +11 -10
- package/src/FridaDeviceAcquisitionError.ts +4 -29
- package/src/FridaScript.ts +22 -23
- package/src/FridaSession.ts +25 -7
- package/src/FridaSessionError.ts +2 -24
- package/src/internal/compiler.ts +122 -0
- package/src/internal/device.ts +85 -67
- package/src/internal/script.ts +209 -292
- package/src/internal/session.ts +116 -52
package/src/internal/script.ts
CHANGED
|
@@ -1,27 +1,28 @@
|
|
|
1
1
|
import type * as Scope from "effect/Scope";
|
|
2
2
|
|
|
3
|
-
import * as FileSystem from "@effect/platform/FileSystem";
|
|
4
|
-
import * as Path from "@effect/platform/Path";
|
|
5
3
|
import * as Cause from "effect/Cause";
|
|
6
|
-
import * as Chunk from "effect/Chunk";
|
|
7
4
|
import * as Context from "effect/Context";
|
|
8
5
|
import * as Deferred from "effect/Deferred";
|
|
9
6
|
import * as Effect from "effect/Effect";
|
|
10
7
|
import * as Exit from "effect/Exit";
|
|
8
|
+
import * as FileSystem from "effect/FileSystem";
|
|
11
9
|
import * as Function from "effect/Function";
|
|
12
10
|
import * as Layer from "effect/Layer";
|
|
13
|
-
import * as Mailbox from "effect/Mailbox";
|
|
14
11
|
import * as Option from "effect/Option";
|
|
12
|
+
import * as Path from "effect/Path";
|
|
15
13
|
import * as Predicate from "effect/Predicate";
|
|
14
|
+
import * as Queue from "effect/Queue";
|
|
16
15
|
import * as Schema from "effect/Schema";
|
|
17
16
|
import * as Sink from "effect/Sink";
|
|
18
17
|
import * as Stream from "effect/Stream";
|
|
19
|
-
import * as Frida from "frida";
|
|
20
18
|
|
|
21
19
|
import type * as FridaScript from "../FridaScript.ts";
|
|
22
20
|
|
|
21
|
+
import * as Frida from "frida";
|
|
22
|
+
|
|
23
23
|
import * as FridaSession from "../FridaSession.ts";
|
|
24
24
|
import * as FridaSessionError from "../FridaSessionError.ts";
|
|
25
|
+
import * as internalCompiler from "./compiler.ts";
|
|
25
26
|
|
|
26
27
|
/** @internal */
|
|
27
28
|
export const FridaScriptTypeId: FridaScript.FridaScriptTypeId = Symbol.for(
|
|
@@ -29,112 +30,11 @@ export const FridaScriptTypeId: FridaScript.FridaScriptTypeId = Symbol.for(
|
|
|
29
30
|
) as FridaScript.FridaScriptTypeId;
|
|
30
31
|
|
|
31
32
|
/** @internal */
|
|
32
|
-
export const Tag = Context.
|
|
33
|
+
export const Tag = Context.Service<FridaScript.FridaScript>("@efffrida/frida-tools/FridaScript");
|
|
33
34
|
|
|
34
35
|
/** @internal */
|
|
35
36
|
export const isFridaScript = (u: unknown): u is FridaScript.FridaScript => Predicate.hasProperty(u, FridaScriptTypeId);
|
|
36
37
|
|
|
37
|
-
/** @internal */
|
|
38
|
-
export const compile = Function.dual<
|
|
39
|
-
(
|
|
40
|
-
options?: Frida.CompilerOptions | undefined
|
|
41
|
-
) => (path: string) => Effect.Effect<string, FridaSessionError.FridaSessionError, Scope.Scope>,
|
|
42
|
-
(
|
|
43
|
-
path: string,
|
|
44
|
-
options?: Frida.CompilerOptions | undefined
|
|
45
|
-
) => Effect.Effect<string, FridaSessionError.FridaSessionError, Scope.Scope>
|
|
46
|
-
>(
|
|
47
|
-
(arguments_) => Predicate.isString(arguments_[0]),
|
|
48
|
-
(path: string, options?: Frida.CompilerOptions | undefined) =>
|
|
49
|
-
Effect.asyncEffect<string, FridaSessionError.FridaSessionError, never, never, never, Scope.Scope>(
|
|
50
|
-
Effect.fnUntraced(function* (resume) {
|
|
51
|
-
// https://github.com/frida/frida-compile/blob/e81ae27369466c69868fc6ee36c0f227bbfe340c/src/cli.ts#L173-L182
|
|
52
|
-
interface Diagnostic {
|
|
53
|
-
category: string;
|
|
54
|
-
code: number;
|
|
55
|
-
text: string;
|
|
56
|
-
file?: {
|
|
57
|
-
path: string;
|
|
58
|
-
line: number;
|
|
59
|
-
character: number;
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const formatDiagnostic = (diagnostic: Diagnostic): FridaSessionError.FridaSessionError => {
|
|
64
|
-
const location = diagnostic.file
|
|
65
|
-
? `${diagnostic.file.path}:${diagnostic.file.line}:${diagnostic.file.character}`
|
|
66
|
-
: undefined;
|
|
67
|
-
const message = `TS${diagnostic.code}: ${diagnostic.text}`;
|
|
68
|
-
const cause = location ? `${location} - ${message}` : message;
|
|
69
|
-
return new FridaSessionError.FridaSessionError({ cause, when: "compile" });
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const compileErrors: Array<Diagnostic> = [];
|
|
73
|
-
const compiler = new Frida.Compiler();
|
|
74
|
-
|
|
75
|
-
const onOutput = (bundle: string) => resume(Effect.succeed(bundle));
|
|
76
|
-
yield* Effect.addFinalizer(() => Effect.sync(() => compiler.output.disconnect(onOutput)));
|
|
77
|
-
compiler.output.connect(onOutput);
|
|
78
|
-
|
|
79
|
-
const onDiagnostic = (diagnostic: Array<Diagnostic>) => {
|
|
80
|
-
for (const diag of diagnostic) {
|
|
81
|
-
if (diag.category === "error") {
|
|
82
|
-
compileErrors.push(diag);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
yield* Effect.addFinalizer(() => Effect.sync(() => compiler.diagnostics.disconnect(onDiagnostic)));
|
|
87
|
-
compiler.diagnostics.connect(onDiagnostic);
|
|
88
|
-
|
|
89
|
-
const onFinished = () => {
|
|
90
|
-
if (compileErrors.length > 0) {
|
|
91
|
-
resume(
|
|
92
|
-
Effect.failCauseSync(() => {
|
|
93
|
-
const [first, ...rest] = compileErrors;
|
|
94
|
-
let cause = Cause.fail(formatDiagnostic(first));
|
|
95
|
-
for (const diag of rest) {
|
|
96
|
-
cause = Cause.parallel(cause, Cause.fail(formatDiagnostic(diag)));
|
|
97
|
-
}
|
|
98
|
-
return cause;
|
|
99
|
-
})
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
yield* Effect.addFinalizer(() => Effect.sync(() => compiler.finished.disconnect(onFinished)));
|
|
104
|
-
compiler.finished.connect(onFinished);
|
|
105
|
-
|
|
106
|
-
const cancellable = new Frida.Cancellable();
|
|
107
|
-
compiler
|
|
108
|
-
.build(
|
|
109
|
-
path,
|
|
110
|
-
{
|
|
111
|
-
externals: options?.externals,
|
|
112
|
-
projectRoot: options?.projectRoot,
|
|
113
|
-
platform: options?.platform ?? Frida.JsPlatform.Gum,
|
|
114
|
-
typeCheck: options?.typeCheck ?? Frida.TypeCheckMode.Full,
|
|
115
|
-
sourceMaps: options?.sourceMaps ?? Frida.SourceMaps.Included,
|
|
116
|
-
compression: options?.compression ?? Frida.JsCompression.None,
|
|
117
|
-
bundleFormat: options?.bundleFormat ?? Frida.BundleFormat.Esm,
|
|
118
|
-
outputFormat: options?.outputFormat ?? Frida.OutputFormat.Unescaped,
|
|
119
|
-
},
|
|
120
|
-
cancellable
|
|
121
|
-
)
|
|
122
|
-
.catch((error) =>
|
|
123
|
-
resume(
|
|
124
|
-
Effect.fail(
|
|
125
|
-
new FridaSessionError.FridaSessionError({
|
|
126
|
-
cause: error,
|
|
127
|
-
when: "compile",
|
|
128
|
-
})
|
|
129
|
-
)
|
|
130
|
-
)
|
|
131
|
-
);
|
|
132
|
-
|
|
133
|
-
return Effect.sync(() => cancellable.cancel());
|
|
134
|
-
})
|
|
135
|
-
)
|
|
136
|
-
);
|
|
137
|
-
|
|
138
38
|
/** @internal */
|
|
139
39
|
export const load = Function.dual<
|
|
140
40
|
(
|
|
@@ -156,202 +56,219 @@ export const load = Function.dual<
|
|
|
156
56
|
>
|
|
157
57
|
>(
|
|
158
58
|
(arguments_) => Predicate.hasProperty(arguments_[0], "href"),
|
|
159
|
-
Effect.fnUntraced(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const { session } = yield* FridaSession.FridaSession;
|
|
59
|
+
Effect.fnUntraced(function* (entrypoint: URL, options?: FridaScript.LoadOptions | undefined) {
|
|
60
|
+
const path = yield* Path.Path;
|
|
61
|
+
const { session } = yield* FridaSession.FridaSession;
|
|
163
62
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
new FridaSessionError.FridaSessionError({
|
|
168
|
-
when: "compile",
|
|
169
|
-
cause,
|
|
170
|
-
})
|
|
171
|
-
),
|
|
172
|
-
Effect.map((p) => path.dirname(p))
|
|
173
|
-
);
|
|
174
|
-
|
|
175
|
-
const source = yield* path.fromFileUrl(entrypoint).pipe(
|
|
176
|
-
Effect.flatMap(
|
|
177
|
-
compile({
|
|
178
|
-
...options,
|
|
179
|
-
projectRoot: options?.projectRoot ?? projectRoot,
|
|
180
|
-
})
|
|
181
|
-
),
|
|
182
|
-
Effect.scoped,
|
|
183
|
-
Effect.timeoutFail({
|
|
184
|
-
duration: "1 minute",
|
|
185
|
-
onTimeout: () =>
|
|
186
|
-
new FridaSessionError.FridaSessionError({
|
|
187
|
-
when: "compile",
|
|
188
|
-
cause: "TypeScript compilation timed out",
|
|
189
|
-
}),
|
|
190
|
-
}),
|
|
191
|
-
Effect.catchTag(
|
|
192
|
-
"BadArgument",
|
|
193
|
-
(platformCause) =>
|
|
194
|
-
new FridaSessionError.FridaSessionError({
|
|
195
|
-
when: "compile",
|
|
196
|
-
cause: platformCause,
|
|
197
|
-
})
|
|
198
|
-
)
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
const script = yield* Effect.tryPromise({
|
|
202
|
-
try: (signal) => {
|
|
203
|
-
const cancellable = new Frida.Cancellable();
|
|
204
|
-
signal.onabort = () => cancellable.cancel();
|
|
205
|
-
return session.createScript(
|
|
206
|
-
source,
|
|
207
|
-
{
|
|
208
|
-
runtime: Frida.ScriptRuntime.V8,
|
|
209
|
-
...options,
|
|
210
|
-
},
|
|
211
|
-
cancellable
|
|
212
|
-
);
|
|
213
|
-
},
|
|
214
|
-
catch: (cause) =>
|
|
63
|
+
const projectRoot = yield* path.fromFileUrl(entrypoint).pipe(
|
|
64
|
+
Effect.mapError(
|
|
65
|
+
(cause) =>
|
|
215
66
|
new FridaSessionError.FridaSessionError({
|
|
67
|
+
when: "compile",
|
|
216
68
|
cause,
|
|
69
|
+
})
|
|
70
|
+
),
|
|
71
|
+
Effect.map((p) => path.dirname(p))
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const source = yield* path.fromFileUrl(entrypoint).pipe(
|
|
75
|
+
Effect.flatMap(
|
|
76
|
+
internalCompiler.compile({
|
|
77
|
+
...options,
|
|
78
|
+
projectRoot: options?.projectRoot ?? projectRoot,
|
|
79
|
+
})
|
|
80
|
+
),
|
|
81
|
+
Effect.timeoutOrElse({
|
|
82
|
+
duration: "1 minute",
|
|
83
|
+
orElse: () =>
|
|
84
|
+
new FridaSessionError.FridaSessionError({
|
|
85
|
+
cause: "Compilation timed out",
|
|
217
86
|
when: "compile",
|
|
218
87
|
}),
|
|
219
|
-
})
|
|
88
|
+
}),
|
|
89
|
+
Effect.catchTag(
|
|
90
|
+
"BadArgument",
|
|
91
|
+
(platformCause) =>
|
|
92
|
+
new FridaSessionError.FridaSessionError({
|
|
93
|
+
cause: platformCause,
|
|
94
|
+
when: "compile",
|
|
95
|
+
})
|
|
96
|
+
)
|
|
97
|
+
);
|
|
220
98
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
Exit.fail(
|
|
233
|
-
new FridaSessionError.FridaSessionError({
|
|
234
|
-
cause: "Script destroyed",
|
|
235
|
-
when: "message",
|
|
236
|
-
})
|
|
237
|
-
)
|
|
99
|
+
const script = yield* Effect.tryPromise({
|
|
100
|
+
try: (signal) => {
|
|
101
|
+
const cancellable = new Frida.Cancellable();
|
|
102
|
+
signal.onabort = () => cancellable.cancel();
|
|
103
|
+
return session.createScript(
|
|
104
|
+
source,
|
|
105
|
+
{
|
|
106
|
+
runtime: Frida.ScriptRuntime.V8,
|
|
107
|
+
...options,
|
|
108
|
+
},
|
|
109
|
+
cancellable
|
|
238
110
|
);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const cause = new Error();
|
|
247
|
-
cause.name = "FridaScriptDefect";
|
|
248
|
-
cause.stack = message.stack ?? "";
|
|
249
|
-
cause.message = message.description.replace(/^\w{0,}: /, "") ?? "";
|
|
250
|
-
Deferred.unsafeDone(scriptError, Exit.succeed(cause));
|
|
251
|
-
mailbox.unsafeDone(
|
|
252
|
-
Exit.fail(
|
|
253
|
-
new FridaSessionError.FridaSessionError({
|
|
254
|
-
when: "message",
|
|
255
|
-
cause,
|
|
256
|
-
})
|
|
257
|
-
)
|
|
258
|
-
);
|
|
259
|
-
break;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
case Frida.MessageType.Send: {
|
|
263
|
-
mailbox.unsafeOffer({
|
|
264
|
-
message: message.payload,
|
|
265
|
-
data: Option.fromNullable(data),
|
|
266
|
-
});
|
|
267
|
-
break;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
default: {
|
|
271
|
-
return Function.absurd(message);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
};
|
|
275
|
-
yield* Effect.addFinalizer(() => Effect.sync(() => script.message.disconnect(messageHandler)));
|
|
276
|
-
script.message.connect(messageHandler);
|
|
277
|
-
|
|
278
|
-
const failIfScriptError = (when: FridaSessionError.FridaSessionError["when"]) =>
|
|
279
|
-
Effect.if(Deferred.isDone(scriptError), {
|
|
280
|
-
onFalse: () => Effect.void,
|
|
281
|
-
onTrue: () =>
|
|
282
|
-
Effect.flatMap(
|
|
283
|
-
Deferred.await(scriptError),
|
|
284
|
-
(cause) => new FridaSessionError.FridaSessionError({ cause, when })
|
|
285
|
-
),
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
const failIfDestroyed = (when: FridaSessionError.FridaSessionError["when"]) =>
|
|
289
|
-
Effect.if(Deferred.isDone(destroyed), {
|
|
290
|
-
onFalse: () => Effect.void,
|
|
291
|
-
onTrue: () => new FridaSessionError.FridaSessionError({ cause: "Script is destroyed", when }),
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
const stream = yield* Stream.share(
|
|
295
|
-
Mailbox.toStream(mailbox),
|
|
296
|
-
options?.streamShareOptions ?? {
|
|
297
|
-
replay: 100,
|
|
298
|
-
capacity: "unbounded",
|
|
299
|
-
}
|
|
300
|
-
);
|
|
111
|
+
},
|
|
112
|
+
catch: (cause) =>
|
|
113
|
+
new FridaSessionError.FridaSessionError({
|
|
114
|
+
when: "compile",
|
|
115
|
+
cause,
|
|
116
|
+
}),
|
|
117
|
+
});
|
|
301
118
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
119
|
+
yield* Effect.addFinalizer(() =>
|
|
120
|
+
Effect.promise((signal) => {
|
|
121
|
+
const cancellable = new Frida.Cancellable();
|
|
122
|
+
signal.onabort = () => cancellable.cancel();
|
|
123
|
+
return script.unload(cancellable);
|
|
124
|
+
})
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const scriptError = yield* Deferred.make<unknown, never>();
|
|
128
|
+
const destroyed = yield* Deferred.make<void, never>();
|
|
129
|
+
|
|
130
|
+
const queue = yield* Queue.make<
|
|
131
|
+
{ message: unknown; data: Option.Option<Buffer> },
|
|
132
|
+
FridaSessionError.FridaSessionError
|
|
133
|
+
>(options?.messageMailboxCapacity);
|
|
134
|
+
yield* Effect.addFinalizer(() => Queue.shutdown(queue));
|
|
135
|
+
|
|
136
|
+
const destroyedHandler: Frida.ScriptDestroyedHandler = () => {
|
|
137
|
+
Deferred.doneUnsafe(destroyed, Exit.void);
|
|
138
|
+
Queue.failCauseUnsafe(
|
|
139
|
+
queue,
|
|
140
|
+
Cause.fail(
|
|
141
|
+
new FridaSessionError.FridaSessionError({
|
|
142
|
+
cause: "Script destroyed",
|
|
143
|
+
when: "message",
|
|
144
|
+
})
|
|
145
|
+
)
|
|
313
146
|
);
|
|
147
|
+
};
|
|
148
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => script.destroyed.disconnect(destroyedHandler)));
|
|
149
|
+
script.destroyed.connect(destroyedHandler);
|
|
150
|
+
|
|
151
|
+
const messageHandler: Frida.ScriptMessageHandler = (message: Frida.Message, data: Buffer | null): void => {
|
|
152
|
+
switch (message.type) {
|
|
153
|
+
case Frida.MessageType.Error: {
|
|
154
|
+
const cause = new Error();
|
|
155
|
+
cause.name = "FridaScriptDefect";
|
|
156
|
+
cause.stack = message.stack ?? "";
|
|
157
|
+
cause.message = message.description.replace(/^\w{0,}: /, "") ?? "";
|
|
158
|
+
Deferred.doneUnsafe(scriptError, Exit.succeed(cause));
|
|
159
|
+
Queue.failCauseUnsafe(
|
|
160
|
+
queue,
|
|
161
|
+
Cause.fail(
|
|
162
|
+
new FridaSessionError.FridaSessionError({
|
|
163
|
+
when: "message",
|
|
164
|
+
cause,
|
|
165
|
+
})
|
|
166
|
+
)
|
|
167
|
+
);
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
314
170
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
yield* failIfScriptError("rpcCall");
|
|
320
|
-
const result = yield* Effect.tryPromise({
|
|
321
|
-
try: () => script.exports[exportName](...args) as Promise<unknown>,
|
|
322
|
-
catch: (cause) => new FridaSessionError.FridaSessionError({ when: "rpcCall", cause }),
|
|
171
|
+
case Frida.MessageType.Send: {
|
|
172
|
+
Queue.offerUnsafe(queue, {
|
|
173
|
+
message: message.payload,
|
|
174
|
+
data: Option.fromNullishOr(data),
|
|
323
175
|
});
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
});
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
327
178
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
179
|
+
default: {
|
|
180
|
+
return Function.absurd(message);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => script.message.disconnect(messageHandler)));
|
|
185
|
+
script.message.connect(messageHandler);
|
|
186
|
+
|
|
187
|
+
const failIfScriptError = Effect.fnUntraced(function* (when: FridaSessionError.FridaSessionError["when"]) {
|
|
188
|
+
const isDone = yield* Deferred.isDone(scriptError);
|
|
189
|
+
if (!isDone) return yield* Effect.void;
|
|
190
|
+
const cause = yield* Deferred.await(scriptError);
|
|
191
|
+
return yield* new FridaSessionError.FridaSessionError({
|
|
192
|
+
cause,
|
|
193
|
+
when,
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const failIfDestroyed = Effect.fnUntraced(function* (when: FridaSessionError.FridaSessionError["when"]) {
|
|
198
|
+
const isDone = yield* Deferred.isDone(destroyed);
|
|
199
|
+
if (!isDone) return yield* Effect.void;
|
|
200
|
+
return yield* new FridaSessionError.FridaSessionError({
|
|
201
|
+
cause: "Script is destroyed",
|
|
202
|
+
when,
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const stream = yield* Stream.share(
|
|
207
|
+
Stream.fromQueue(queue),
|
|
208
|
+
options?.streamShareOptions ?? {
|
|
209
|
+
strategy: "suspend",
|
|
210
|
+
capacity: 100,
|
|
211
|
+
replay: 100,
|
|
212
|
+
}
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
const sink = Sink.forEach<
|
|
216
|
+
{ message: unknown; data: Option.Option<Buffer> },
|
|
217
|
+
void,
|
|
218
|
+
FridaSessionError.FridaSessionError,
|
|
219
|
+
never
|
|
220
|
+
>(
|
|
221
|
+
Effect.fnUntraced(function* ({ data, message }) {
|
|
222
|
+
yield* failIfDestroyed("message");
|
|
223
|
+
yield* failIfScriptError("message");
|
|
224
|
+
script.post(message, Option.getOrNull(data));
|
|
225
|
+
})
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
const callExport = <A = unknown, R = never>(exportName: string, schema?: Schema.Decoder<A, R> | undefined) =>
|
|
229
|
+
Effect.fn(`call frida export ${exportName}`)(function* (
|
|
230
|
+
...args: Array<any>
|
|
231
|
+
): Effect.fn.Return<A, FridaSessionError.FridaSessionError | Schema.SchemaError, R> {
|
|
232
|
+
yield* Effect.annotateCurrentSpan("args", args);
|
|
233
|
+
yield* failIfDestroyed("rpcCall");
|
|
234
|
+
yield* failIfScriptError("rpcCall");
|
|
235
|
+
const result = yield* Effect.tryPromise({
|
|
236
|
+
try: () => script.exports[exportName](...args),
|
|
237
|
+
catch: (cause) =>
|
|
238
|
+
new FridaSessionError.FridaSessionError({
|
|
239
|
+
when: "rpcCall",
|
|
240
|
+
cause,
|
|
241
|
+
}),
|
|
242
|
+
});
|
|
243
|
+
yield* Effect.annotateCurrentSpan("result", result);
|
|
244
|
+
const decoder = Schema.decodeEffect(schema ?? Schema.Unknown);
|
|
245
|
+
const decoded = yield* decoder(result);
|
|
246
|
+
return decoded as A;
|
|
335
247
|
});
|
|
336
248
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
stream,
|
|
340
|
-
script,
|
|
341
|
-
destroyed,
|
|
342
|
-
callExport,
|
|
343
|
-
scriptError,
|
|
344
|
-
[FridaScriptTypeId]: FridaScriptTypeId,
|
|
345
|
-
} as const;
|
|
346
|
-
},
|
|
347
|
-
Effect.acquireRelease(({ script }: FridaScript.FridaScript) =>
|
|
348
|
-
Effect.promise((signal) => {
|
|
249
|
+
yield* Effect.tryPromise({
|
|
250
|
+
try: (signal) => {
|
|
349
251
|
const cancellable = new Frida.Cancellable();
|
|
350
252
|
signal.onabort = () => cancellable.cancel();
|
|
351
|
-
return script.
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
|
|
253
|
+
return script.load(cancellable);
|
|
254
|
+
},
|
|
255
|
+
catch: (cause) =>
|
|
256
|
+
new FridaSessionError.FridaSessionError({
|
|
257
|
+
when: "load",
|
|
258
|
+
cause,
|
|
259
|
+
}),
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
sink,
|
|
264
|
+
stream,
|
|
265
|
+
script,
|
|
266
|
+
destroyed,
|
|
267
|
+
callExport,
|
|
268
|
+
scriptError,
|
|
269
|
+
[FridaScriptTypeId]: FridaScriptTypeId,
|
|
270
|
+
} as const;
|
|
271
|
+
})
|
|
355
272
|
);
|
|
356
273
|
|
|
357
274
|
/** @internal */
|
|
@@ -368,7 +285,7 @@ export const layer = Function.dual<
|
|
|
368
285
|
>(
|
|
369
286
|
(arguments_) => Predicate.hasProperty(arguments_[0], "href"),
|
|
370
287
|
(entrypoint: URL, options?: FridaScript.LoadOptions | undefined) =>
|
|
371
|
-
Layer.
|
|
288
|
+
Layer.effect(Tag, load(entrypoint, options)).pipe(Layer.provide(Path.layer))
|
|
372
289
|
);
|
|
373
290
|
|
|
374
291
|
/** @internal */
|
|
@@ -422,7 +339,7 @@ export const watch = Function.dual<
|
|
|
422
339
|
|
|
423
340
|
return fileSystem.watch(pathString).pipe(
|
|
424
341
|
Stream.filter((event) => event._tag === "Update"),
|
|
425
|
-
Stream.prepend(
|
|
342
|
+
Stream.prepend([{ _tag: "Update" as const, path: pathString }]),
|
|
426
343
|
Stream.debounce("2 second"),
|
|
427
344
|
Stream.mapError(
|
|
428
345
|
(cause) =>
|
|
@@ -444,7 +361,7 @@ export const watch = Function.dual<
|
|
|
444
361
|
);
|
|
445
362
|
},
|
|
446
363
|
Stream.unwrap,
|
|
447
|
-
Stream.
|
|
364
|
+
Stream.provide(Path.layer)
|
|
448
365
|
)
|
|
449
366
|
);
|
|
450
367
|
|
|
@@ -462,12 +379,12 @@ export const logWatchErrors = <A, E1, E2, R>(
|
|
|
462
379
|
const cause = exit.cause;
|
|
463
380
|
|
|
464
381
|
// Interruption only
|
|
465
|
-
if (Cause.
|
|
382
|
+
if (Cause.hasInterruptsOnly(cause)) {
|
|
466
383
|
return Effect.logDebug("Script interrupted with no errors");
|
|
467
384
|
}
|
|
468
385
|
|
|
469
386
|
// Defect
|
|
470
|
-
if (Cause.
|
|
387
|
+
if (Cause.hasDies(cause)) {
|
|
471
388
|
return Effect.logError(cause);
|
|
472
389
|
}
|
|
473
390
|
|