@efffrida/frida-tools 0.0.13 → 0.0.15
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/README.md +6 -10
- package/dist/{dts/FridaDevice.d.ts → FridaDevice.d.ts} +48 -4
- package/dist/FridaDevice.d.ts.map +1 -0
- package/dist/{esm/FridaDevice.js → FridaDevice.js} +22 -2
- package/dist/FridaDevice.js.map +1 -0
- package/dist/{dts/FridaDeviceAcquisitionError.d.ts → FridaDeviceAcquisitionError.d.ts} +2 -1
- package/dist/FridaDeviceAcquisitionError.d.ts.map +1 -0
- package/dist/{esm/FridaDeviceAcquisitionError.js → FridaDeviceAcquisitionError.js} +5 -1
- package/dist/FridaDeviceAcquisitionError.js.map +1 -0
- package/dist/FridaScript.d.ts +96 -0
- package/dist/FridaScript.d.ts.map +1 -0
- package/dist/FridaScript.js.map +1 -0
- package/dist/FridaSession.d.ts +72 -0
- package/dist/FridaSession.d.ts.map +1 -0
- package/dist/{esm/FridaSession.js → FridaSession.js} +10 -0
- package/dist/FridaSession.js.map +1 -0
- package/dist/FridaSessionError.d.ts.map +1 -0
- package/dist/FridaSessionError.js.map +1 -0
- package/dist/{dts/index.d.ts → index.d.ts} +8 -5
- package/dist/index.d.ts.map +1 -0
- package/dist/{esm/index.js → index.js} +3 -0
- package/dist/index.js.map +1 -0
- package/dist/{dts/internal → internal}/device.d.ts.map +1 -1
- package/dist/internal/device.js +189 -0
- package/dist/internal/device.js.map +1 -0
- package/dist/{dts/internal → internal}/script.d.ts.map +1 -1
- package/dist/internal/script.js +221 -0
- package/dist/internal/script.js.map +1 -0
- package/dist/{dts/internal → internal}/session.d.ts.map +1 -1
- package/dist/internal/session.js +124 -0
- package/dist/internal/session.js.map +1 -0
- package/package.json +54 -68
- package/src/FridaDevice.ts +92 -8
- package/src/FridaDeviceAcquisitionError.ts +6 -2
- package/src/FridaScript.ts +53 -31
- package/src/FridaSession.ts +31 -5
- package/src/FridaSessionError.ts +1 -1
- package/src/index.ts +9 -5
- package/src/internal/device.ts +311 -32
- package/src/internal/script.ts +265 -118
- package/src/internal/session.ts +135 -27
- package/FridaDevice/package.json +0 -6
- package/FridaDeviceAcquisitionError/package.json +0 -6
- package/FridaScript/package.json +0 -6
- package/FridaSession/package.json +0 -6
- package/FridaSessionError/package.json +0 -6
- package/dist/cjs/FridaDevice.js +0 -60
- package/dist/cjs/FridaDevice.js.map +0 -1
- package/dist/cjs/FridaDeviceAcquisitionError.js +0 -33
- package/dist/cjs/FridaDeviceAcquisitionError.js.map +0 -1
- package/dist/cjs/FridaScript.js +0 -40
- package/dist/cjs/FridaScript.js.map +0 -1
- package/dist/cjs/FridaSession.js +0 -45
- package/dist/cjs/FridaSession.js.map +0 -1
- package/dist/cjs/FridaSessionError.js +0 -37
- package/dist/cjs/FridaSessionError.js.map +0 -1
- package/dist/cjs/index.js +0 -18
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/internal/device.js +0 -68
- package/dist/cjs/internal/device.js.map +0 -1
- package/dist/cjs/internal/script.js +0 -163
- package/dist/cjs/internal/script.js.map +0 -1
- package/dist/cjs/internal/session.js +0 -55
- package/dist/cjs/internal/session.js.map +0 -1
- package/dist/dts/FridaDevice.d.ts.map +0 -1
- package/dist/dts/FridaDeviceAcquisitionError.d.ts.map +0 -1
- package/dist/dts/FridaScript.d.ts +0 -80
- package/dist/dts/FridaScript.d.ts.map +0 -1
- package/dist/dts/FridaSession.d.ts +0 -56
- package/dist/dts/FridaSession.d.ts.map +0 -1
- package/dist/dts/FridaSessionError.d.ts.map +0 -1
- package/dist/dts/index.d.ts.map +0 -1
- package/dist/esm/FridaDevice.js.map +0 -1
- package/dist/esm/FridaDeviceAcquisitionError.js.map +0 -1
- package/dist/esm/FridaScript.js.map +0 -1
- package/dist/esm/FridaSession.js.map +0 -1
- package/dist/esm/FridaSessionError.js.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/internal/device.js +0 -55
- package/dist/esm/internal/device.js.map +0 -1
- package/dist/esm/internal/script.js +0 -155
- package/dist/esm/internal/script.js.map +0 -1
- package/dist/esm/internal/session.js +0 -44
- package/dist/esm/internal/session.js.map +0 -1
- package/dist/esm/package.json +0 -4
- package/index/package.json +0 -6
- /package/dist/{esm/FridaScript.js → FridaScript.js} +0 -0
- /package/dist/{dts/FridaSessionError.d.ts → FridaSessionError.d.ts} +0 -0
- /package/dist/{esm/FridaSessionError.js → FridaSessionError.js} +0 -0
- /package/dist/{dts/internal → internal}/device.d.ts +0 -0
- /package/dist/{dts/internal → internal}/script.d.ts +0 -0
- /package/dist/{dts/internal → internal}/session.d.ts +0 -0
package/src/internal/script.ts
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import type * as Scope from "effect/Scope";
|
|
2
|
-
import type * as FridaScript from "../FridaScript.
|
|
2
|
+
import type * as FridaScript from "../FridaScript.ts";
|
|
3
3
|
|
|
4
|
+
import * as Path from "@effect/platform/Path";
|
|
5
|
+
import * as Cause from "effect/Cause";
|
|
4
6
|
import * as Context from "effect/Context";
|
|
5
7
|
import * as Deferred from "effect/Deferred";
|
|
6
8
|
import * as Effect from "effect/Effect";
|
|
9
|
+
import * as Exit from "effect/Exit";
|
|
7
10
|
import * as Function from "effect/Function";
|
|
8
11
|
import * as Layer from "effect/Layer";
|
|
12
|
+
import * as Mailbox from "effect/Mailbox";
|
|
9
13
|
import * as Option from "effect/Option";
|
|
10
14
|
import * as Predicate from "effect/Predicate";
|
|
11
|
-
import * as
|
|
15
|
+
import * as Schema from "effect/Schema";
|
|
12
16
|
import * as Sink from "effect/Sink";
|
|
13
17
|
import * as Stream from "effect/Stream";
|
|
14
|
-
import * as Take from "effect/Take";
|
|
15
18
|
import * as Frida from "frida";
|
|
16
|
-
|
|
17
|
-
import * as FridaSession from "../FridaSession.
|
|
18
|
-
import * as FridaSessionError from "../FridaSessionError.
|
|
19
|
+
|
|
20
|
+
import * as FridaSession from "../FridaSession.ts";
|
|
21
|
+
import * as FridaSessionError from "../FridaSessionError.ts";
|
|
19
22
|
|
|
20
23
|
/** @internal */
|
|
21
24
|
export const FridaScriptTypeId: FridaScript.FridaScriptTypeId = Symbol.for(
|
|
@@ -28,182 +31,326 @@ export const Tag = Context.GenericTag<FridaScript.FridaScript>("@efffrida/frida-
|
|
|
28
31
|
/** @internal */
|
|
29
32
|
export const isFridaScript = (u: unknown): u is FridaScript.FridaScript => Predicate.hasProperty(u, FridaScriptTypeId);
|
|
30
33
|
|
|
34
|
+
/** @internal */
|
|
35
|
+
export const compile = Function.dual<
|
|
36
|
+
(
|
|
37
|
+
options?: Frida.CompilerOptions | undefined
|
|
38
|
+
) => (path: string) => Effect.Effect<string, FridaSessionError.FridaSessionError, Scope.Scope>,
|
|
39
|
+
(
|
|
40
|
+
path: string,
|
|
41
|
+
options?: Frida.CompilerOptions | undefined
|
|
42
|
+
) => Effect.Effect<string, FridaSessionError.FridaSessionError, Scope.Scope>
|
|
43
|
+
>(
|
|
44
|
+
(arguments_) => Predicate.isString(arguments_[0]),
|
|
45
|
+
(path: string, options?: Frida.CompilerOptions | undefined) =>
|
|
46
|
+
Effect.asyncEffect<string, FridaSessionError.FridaSessionError, never, never, never, Scope.Scope>(
|
|
47
|
+
Effect.fnUntraced(function* (resume) {
|
|
48
|
+
// https://github.com/frida/frida-compile/blob/e81ae27369466c69868fc6ee36c0f227bbfe340c/src/cli.ts#L173-L182
|
|
49
|
+
interface Diagnostic {
|
|
50
|
+
category: string;
|
|
51
|
+
code: number;
|
|
52
|
+
text: string;
|
|
53
|
+
file?: {
|
|
54
|
+
path: string;
|
|
55
|
+
line: number;
|
|
56
|
+
character: number;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const formatDiagnostic = (diagnostic: Diagnostic): FridaSessionError.FridaSessionError => {
|
|
61
|
+
const location = diagnostic.file
|
|
62
|
+
? `${diagnostic.file.path}:${diagnostic.file.line}:${diagnostic.file.character}`
|
|
63
|
+
: undefined;
|
|
64
|
+
const message = `TS${diagnostic.code}: ${diagnostic.text}`;
|
|
65
|
+
const cause = location ? `${location} - ${message}` : message;
|
|
66
|
+
return new FridaSessionError.FridaSessionError({ cause, when: "compile" });
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const compileErrors: Array<Diagnostic> = [];
|
|
70
|
+
const compiler = new Frida.Compiler();
|
|
71
|
+
|
|
72
|
+
const onOutput = (bundle: string) => resume(Effect.succeed(bundle));
|
|
73
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => compiler.output.disconnect(onOutput)));
|
|
74
|
+
compiler.output.connect(onOutput);
|
|
75
|
+
|
|
76
|
+
const onDiagnostic = (diagnostic: Array<Diagnostic>) => {
|
|
77
|
+
for (const diag of diagnostic) {
|
|
78
|
+
if (diag.category === "error") {
|
|
79
|
+
compileErrors.push(diag);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => compiler.diagnostics.disconnect(onDiagnostic)));
|
|
84
|
+
compiler.diagnostics.connect(onDiagnostic);
|
|
85
|
+
|
|
86
|
+
const onFinished = () => {
|
|
87
|
+
if (compileErrors.length > 0) {
|
|
88
|
+
resume(
|
|
89
|
+
Effect.failCauseSync(() => {
|
|
90
|
+
const [first, ...rest] = compileErrors;
|
|
91
|
+
let cause = Cause.fail(formatDiagnostic(first));
|
|
92
|
+
for (const diag of rest) {
|
|
93
|
+
cause = Cause.parallel(cause, Cause.fail(formatDiagnostic(diag)));
|
|
94
|
+
}
|
|
95
|
+
return cause;
|
|
96
|
+
})
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => compiler.finished.disconnect(onFinished)));
|
|
101
|
+
compiler.finished.connect(onFinished);
|
|
102
|
+
|
|
103
|
+
const cancellable = new Frida.Cancellable();
|
|
104
|
+
compiler
|
|
105
|
+
.build(
|
|
106
|
+
path,
|
|
107
|
+
{
|
|
108
|
+
externals: options?.externals,
|
|
109
|
+
platform: options?.platform ?? Frida.JsPlatform.Gum,
|
|
110
|
+
typeCheck: options?.typeCheck ?? Frida.TypeCheckMode.Full,
|
|
111
|
+
sourceMaps: options?.sourceMaps ?? Frida.SourceMaps.Included,
|
|
112
|
+
compression: options?.compression ?? Frida.JsCompression.None,
|
|
113
|
+
bundleFormat: options?.bundleFormat ?? Frida.BundleFormat.Esm,
|
|
114
|
+
outputFormat: options?.outputFormat ?? Frida.OutputFormat.Unescaped,
|
|
115
|
+
},
|
|
116
|
+
cancellable
|
|
117
|
+
)
|
|
118
|
+
.catch((error) =>
|
|
119
|
+
resume(
|
|
120
|
+
Effect.fail(
|
|
121
|
+
new FridaSessionError.FridaSessionError({
|
|
122
|
+
cause: error,
|
|
123
|
+
when: "compile",
|
|
124
|
+
})
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
return Effect.sync(() => cancellable.cancel());
|
|
130
|
+
})
|
|
131
|
+
)
|
|
132
|
+
);
|
|
133
|
+
|
|
31
134
|
/** @internal */
|
|
32
135
|
export const load = Function.dual<
|
|
33
136
|
(
|
|
34
|
-
options?:
|
|
137
|
+
options?: FridaScript.LoadOptions | undefined
|
|
35
138
|
) => (
|
|
36
|
-
|
|
139
|
+
entrypoint: URL
|
|
37
140
|
) => Effect.Effect<
|
|
38
141
|
FridaScript.FridaScript,
|
|
39
142
|
FridaSessionError.FridaSessionError,
|
|
40
|
-
|
|
143
|
+
Path.Path | FridaSession.FridaSession | Scope.Scope
|
|
41
144
|
>,
|
|
42
145
|
(
|
|
43
|
-
|
|
44
|
-
options?:
|
|
146
|
+
entrypoint: URL,
|
|
147
|
+
options?: FridaScript.LoadOptions | undefined
|
|
45
148
|
) => Effect.Effect<
|
|
46
149
|
FridaScript.FridaScript,
|
|
47
150
|
FridaSessionError.FridaSessionError,
|
|
48
|
-
|
|
151
|
+
Path.Path | FridaSession.FridaSession | Scope.Scope
|
|
49
152
|
>
|
|
50
153
|
>(
|
|
51
|
-
(arguments_) => Predicate.
|
|
154
|
+
(arguments_) => Predicate.hasProperty(arguments_[0], "href"),
|
|
52
155
|
Effect.fnUntraced(
|
|
53
|
-
function* (
|
|
54
|
-
|
|
55
|
-
options?: (Frida.ScriptOptions & { readonly resume?: boolean | undefined }) | undefined
|
|
56
|
-
) {
|
|
57
|
-
const { device } = yield* FridaDevice.FridaDevice;
|
|
156
|
+
function* (entrypoint: URL, options?: FridaScript.LoadOptions | undefined) {
|
|
157
|
+
const path = yield* Path.Path;
|
|
58
158
|
const { session } = yield* FridaSession.FridaSession;
|
|
59
159
|
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
160
|
+
const source = yield* path
|
|
161
|
+
.fromFileUrl(entrypoint)
|
|
162
|
+
.pipe(Effect.flatMap(compile(options)))
|
|
163
|
+
.pipe(Effect.scoped)
|
|
164
|
+
.pipe(
|
|
165
|
+
Effect.timeoutFail({
|
|
166
|
+
duration: "1 minute",
|
|
167
|
+
onTimeout: () =>
|
|
168
|
+
new FridaSessionError.FridaSessionError({
|
|
169
|
+
when: "compile",
|
|
170
|
+
cause: "TypeScript compilation timed out",
|
|
171
|
+
}),
|
|
172
|
+
})
|
|
173
|
+
)
|
|
174
|
+
.pipe(
|
|
175
|
+
Effect.catchTag(
|
|
176
|
+
"BadArgument",
|
|
177
|
+
(platformCause) =>
|
|
178
|
+
new FridaSessionError.FridaSessionError({
|
|
179
|
+
when: "compile",
|
|
180
|
+
cause: platformCause,
|
|
181
|
+
})
|
|
182
|
+
)
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
const script = yield* Effect.tryPromise({
|
|
186
|
+
try: (signal) => {
|
|
187
|
+
const cancellable = new Frida.Cancellable();
|
|
188
|
+
signal.onabort = () => cancellable.cancel();
|
|
189
|
+
return session.createScript(
|
|
190
|
+
source,
|
|
191
|
+
{
|
|
192
|
+
runtime: Frida.ScriptRuntime.V8,
|
|
193
|
+
...options,
|
|
194
|
+
},
|
|
195
|
+
cancellable
|
|
196
|
+
);
|
|
197
|
+
},
|
|
198
|
+
catch: (cause) =>
|
|
199
|
+
new FridaSessionError.FridaSessionError({
|
|
200
|
+
cause,
|
|
201
|
+
when: "compile",
|
|
202
|
+
}),
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
const destroyed = yield* Deferred.make<void, never>();
|
|
206
|
+
const scriptError = yield* Deferred.make<unknown, never>();
|
|
207
|
+
const mailbox = yield* Mailbox.make<
|
|
208
|
+
{ message: unknown; data: Option.Option<Buffer> },
|
|
209
|
+
FridaSessionError.FridaSessionError
|
|
210
|
+
>(options?.messageMailboxCapacity);
|
|
211
|
+
yield* Effect.addFinalizer(() => mailbox.shutdown); // TODO: is this needed?
|
|
212
|
+
|
|
213
|
+
const destroyedHandler: Frida.ScriptDestroyedHandler = () => {
|
|
214
|
+
Deferred.unsafeDone(destroyed, Effect.void);
|
|
215
|
+
mailbox.unsafeDone(
|
|
216
|
+
Exit.fail(
|
|
217
|
+
new FridaSessionError.FridaSessionError({
|
|
218
|
+
when: "message",
|
|
219
|
+
cause: "Script destroyed",
|
|
220
|
+
})
|
|
221
|
+
)
|
|
222
|
+
);
|
|
223
|
+
};
|
|
224
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => script.destroyed.disconnect(destroyedHandler)));
|
|
225
|
+
script.destroyed.connect(destroyedHandler);
|
|
226
|
+
|
|
76
227
|
const messageHandler: Frida.ScriptMessageHandler = (message: Frida.Message, data: Buffer | null): void => {
|
|
77
228
|
switch (message.type) {
|
|
78
229
|
case Frida.MessageType.Error: {
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
230
|
+
const cause = new Error();
|
|
231
|
+
cause.name = "FridaScriptDefect";
|
|
232
|
+
cause.stack = message.stack ?? "";
|
|
233
|
+
cause.message = message.description.replace(/^\w{0,}: /, "") ?? "";
|
|
234
|
+
Deferred.unsafeDone(scriptError, Exit.succeed(cause));
|
|
235
|
+
mailbox.unsafeDone(
|
|
236
|
+
Exit.fail(
|
|
237
|
+
new FridaSessionError.FridaSessionError({
|
|
238
|
+
when: "message",
|
|
239
|
+
cause,
|
|
240
|
+
})
|
|
241
|
+
)
|
|
242
|
+
);
|
|
90
243
|
break;
|
|
91
244
|
}
|
|
92
245
|
|
|
93
246
|
case Frida.MessageType.Send: {
|
|
94
|
-
|
|
95
|
-
|
|
247
|
+
mailbox.unsafeOffer({
|
|
248
|
+
message: message.payload,
|
|
249
|
+
data: Option.fromNullable(data),
|
|
250
|
+
});
|
|
96
251
|
break;
|
|
97
252
|
}
|
|
98
253
|
|
|
99
|
-
default:
|
|
100
|
-
Function.absurd(message);
|
|
254
|
+
default: {
|
|
255
|
+
return Function.absurd(message);
|
|
256
|
+
}
|
|
101
257
|
}
|
|
102
258
|
};
|
|
103
|
-
|
|
104
|
-
const disconnectMessageHandler = Effect.sync(() => script.message.disconnect(messageHandler));
|
|
105
|
-
yield* Effect.addFinalizer(() => Effect.flatMap(messageQueue.shutdown, () => disconnectMessageHandler));
|
|
259
|
+
yield* Effect.addFinalizer(() => Effect.sync(() => script.message.disconnect(messageHandler)));
|
|
106
260
|
script.message.connect(messageHandler);
|
|
107
261
|
|
|
108
|
-
const
|
|
262
|
+
const failIfScriptError = (when: FridaSessionError.FridaSessionError["when"]) =>
|
|
263
|
+
Effect.if(Deferred.isDone(scriptError), {
|
|
264
|
+
onFalse: () => Effect.void,
|
|
265
|
+
onTrue: () =>
|
|
266
|
+
Effect.flatMap(
|
|
267
|
+
Deferred.await(scriptError),
|
|
268
|
+
(cause) => new FridaSessionError.FridaSessionError({ cause, when })
|
|
269
|
+
),
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const failIfDestroyed = (when: FridaSessionError.FridaSessionError["when"]) =>
|
|
273
|
+
Effect.if(Deferred.isDone(destroyed), {
|
|
274
|
+
onFalse: () => Effect.void,
|
|
275
|
+
onTrue: () => new FridaSessionError.FridaSessionError({ cause: "Script is destroyed", when }),
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
const stream = yield* Stream.share(
|
|
279
|
+
Mailbox.toStream(mailbox),
|
|
280
|
+
options?.streamShareOptions ?? {
|
|
281
|
+
replay: 100,
|
|
282
|
+
capacity: "unbounded",
|
|
283
|
+
}
|
|
284
|
+
);
|
|
285
|
+
|
|
109
286
|
const sink = Sink.forEach<
|
|
110
|
-
{ message:
|
|
287
|
+
{ message: unknown; data: Option.Option<Buffer> },
|
|
111
288
|
void,
|
|
112
289
|
FridaSessionError.FridaSessionError,
|
|
113
290
|
never
|
|
114
|
-
>(
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
);
|
|
122
|
-
} else {
|
|
123
|
-
return Effect.sync(() => script.post(message, Option.getOrNull(data)));
|
|
124
|
-
}
|
|
125
|
-
});
|
|
291
|
+
>(
|
|
292
|
+
Effect.fnUntraced(function* ({ data, message }) {
|
|
293
|
+
yield* failIfDestroyed("message");
|
|
294
|
+
yield* failIfScriptError("message");
|
|
295
|
+
script.post(message, Option.getOrNull(data));
|
|
296
|
+
})
|
|
297
|
+
);
|
|
126
298
|
|
|
127
|
-
const
|
|
128
|
-
const destroyedHandler = () => Deferred.unsafeDone(destroyed, Effect.void);
|
|
129
|
-
script.destroyed.connect(destroyedHandler);
|
|
130
|
-
|
|
131
|
-
const callExport = (exportName: string) =>
|
|
299
|
+
const callExport = <A, I, R>(exportName: string, schema: Schema.Schema<A, I, R>) =>
|
|
132
300
|
Effect.fn(`call frida export ${exportName}`)(function* (...args: Array<any>) {
|
|
133
301
|
yield* Effect.annotateCurrentSpan("args", args);
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if (isDestroyed) {
|
|
137
|
-
return yield* new FridaSessionError.FridaSessionError({
|
|
138
|
-
when: "rpcCall",
|
|
139
|
-
cause: "Script is destroyed",
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (Predicate.isNotUndefined(scriptDefectCause)) {
|
|
144
|
-
return yield* new FridaSessionError.FridaSessionError({
|
|
145
|
-
when: "rpcCall",
|
|
146
|
-
cause: scriptDefectCause,
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
|
|
302
|
+
yield* failIfDestroyed("rpcCall");
|
|
303
|
+
yield* failIfScriptError("rpcCall");
|
|
150
304
|
const result = yield* Effect.tryPromise({
|
|
151
305
|
try: () => script.exports[exportName](...args) as Promise<unknown>,
|
|
152
306
|
catch: (cause) => new FridaSessionError.FridaSessionError({ when: "rpcCall", cause }),
|
|
153
307
|
});
|
|
154
|
-
|
|
155
308
|
yield* Effect.annotateCurrentSpan("result", result);
|
|
156
|
-
return result;
|
|
309
|
+
return yield* Schema.decodeUnknown(schema)(result);
|
|
157
310
|
});
|
|
158
311
|
|
|
159
312
|
yield* Effect.tryPromise({
|
|
160
|
-
try: () =>
|
|
313
|
+
try: (signal) => {
|
|
314
|
+
const cancellable = new Frida.Cancellable();
|
|
315
|
+
signal.onabort = () => cancellable.cancel();
|
|
316
|
+
return script.load(cancellable);
|
|
317
|
+
},
|
|
161
318
|
catch: (cause) => new FridaSessionError.FridaSessionError({ cause, when: "load" }),
|
|
162
319
|
});
|
|
163
320
|
|
|
164
|
-
if (options?.resume === true) {
|
|
165
|
-
yield* Effect.tryPromise({
|
|
166
|
-
try: () => device.resume(session.pid),
|
|
167
|
-
catch: (cause) => new FridaSessionError.FridaSessionError({ cause, when: "resume" }),
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
|
|
171
321
|
return {
|
|
172
322
|
sink,
|
|
173
|
-
script,
|
|
174
323
|
stream,
|
|
324
|
+
script,
|
|
175
325
|
destroyed,
|
|
176
326
|
callExport,
|
|
327
|
+
scriptError,
|
|
177
328
|
[FridaScriptTypeId]: FridaScriptTypeId,
|
|
178
329
|
} as const;
|
|
179
330
|
},
|
|
180
|
-
Effect.acquireRelease(({ script }: FridaScript.FridaScript) =>
|
|
331
|
+
Effect.acquireRelease(({ script }: FridaScript.FridaScript) =>
|
|
332
|
+
Effect.promise((signal) => {
|
|
333
|
+
const cancellable = new Frida.Cancellable();
|
|
334
|
+
signal.onabort = () => cancellable.cancel();
|
|
335
|
+
return script.unload(cancellable);
|
|
336
|
+
})
|
|
337
|
+
)
|
|
181
338
|
)
|
|
182
339
|
);
|
|
183
340
|
|
|
184
341
|
/** @internal */
|
|
185
342
|
export const layer = Function.dual<
|
|
186
343
|
(
|
|
187
|
-
options?:
|
|
344
|
+
options?: FridaScript.LoadOptions | undefined
|
|
188
345
|
) => (
|
|
189
|
-
|
|
190
|
-
) => Layer.Layer<
|
|
191
|
-
FridaScript.FridaScript,
|
|
192
|
-
FridaSessionError.FridaSessionError,
|
|
193
|
-
FridaSession.FridaSession | FridaDevice.FridaDevice
|
|
194
|
-
>,
|
|
346
|
+
entrypoint: URL
|
|
347
|
+
) => Layer.Layer<FridaScript.FridaScript, FridaSessionError.FridaSessionError, FridaSession.FridaSession>,
|
|
195
348
|
(
|
|
196
|
-
|
|
197
|
-
options?:
|
|
198
|
-
) => Layer.Layer<
|
|
199
|
-
FridaScript.FridaScript,
|
|
200
|
-
FridaSessionError.FridaSessionError,
|
|
201
|
-
FridaSession.FridaSession | FridaDevice.FridaDevice
|
|
202
|
-
>
|
|
349
|
+
entrypoint: URL,
|
|
350
|
+
options?: FridaScript.LoadOptions | undefined
|
|
351
|
+
) => Layer.Layer<FridaScript.FridaScript, FridaSessionError.FridaSessionError, FridaSession.FridaSession>
|
|
203
352
|
>(
|
|
204
|
-
(arguments_) => Predicate.
|
|
205
|
-
(
|
|
206
|
-
|
|
207
|
-
options?: (Frida.ScriptOptions & { readonly resume?: boolean | undefined }) | undefined
|
|
208
|
-
) => Layer.scoped(Tag, load(source, options))
|
|
353
|
+
(arguments_) => Predicate.hasProperty(arguments_[0], "href"),
|
|
354
|
+
(entrypoint: URL, options?: FridaScript.LoadOptions | undefined) =>
|
|
355
|
+
Layer.scoped(Tag, load(entrypoint, options)).pipe(Layer.provide(Path.layer))
|
|
209
356
|
);
|
package/src/internal/session.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import type * as Scope from "effect/Scope";
|
|
2
|
-
import type * as
|
|
3
|
-
import type * as FridaSession from "../FridaSession.js";
|
|
2
|
+
import type * as FridaSession from "../FridaSession.ts";
|
|
4
3
|
|
|
5
4
|
import * as Context from "effect/Context";
|
|
6
5
|
import * as Effect from "effect/Effect";
|
|
7
6
|
import * as Layer from "effect/Layer";
|
|
7
|
+
import * as Match from "effect/Match";
|
|
8
8
|
import * as Predicate from "effect/Predicate";
|
|
9
|
-
import * as
|
|
10
|
-
|
|
9
|
+
import * as Frida from "frida";
|
|
10
|
+
|
|
11
|
+
import * as FridaDevice from "../FridaDevice.ts";
|
|
12
|
+
import * as FridaSessionError from "../FridaSessionError.ts";
|
|
11
13
|
|
|
12
14
|
/** @internal */
|
|
13
15
|
export const FridaSessionTypeId: FridaSession.FridaSessionTypeId = Symbol.for(
|
|
@@ -21,21 +23,70 @@ export const Tag = Context.GenericTag<FridaSession.FridaSession>("@efffrida/frid
|
|
|
21
23
|
export const isFridaSession = (u: unknown): u is FridaSession.FridaSession =>
|
|
22
24
|
Predicate.hasProperty(u, FridaSessionTypeId);
|
|
23
25
|
|
|
26
|
+
/** @internal */
|
|
27
|
+
export const frontmost = (
|
|
28
|
+
options?: Frida.FrontmostQueryOptions | undefined
|
|
29
|
+
): Effect.Effect<Frida.Application, FridaSessionError.FridaSessionError, FridaDevice.FridaDevice> =>
|
|
30
|
+
Effect.flatMap(FridaDevice.FridaDevice, ({ device }) =>
|
|
31
|
+
Effect.tryPromise((signal) => {
|
|
32
|
+
const cancellable = new Frida.Cancellable();
|
|
33
|
+
signal.onabort = () => cancellable.cancel();
|
|
34
|
+
return device.getFrontmostApplication(options, cancellable);
|
|
35
|
+
})
|
|
36
|
+
)
|
|
37
|
+
.pipe(Effect.flatMap(Effect.fromNullable))
|
|
38
|
+
.pipe(Effect.mapError((cause) => new FridaSessionError.FridaSessionError({ cause, when: "attach" })));
|
|
39
|
+
|
|
24
40
|
/** @internal */
|
|
25
41
|
export const spawn = (
|
|
26
|
-
program: string |
|
|
42
|
+
program: string | ReadonlyArray<string>,
|
|
27
43
|
options?: Frida.SpawnOptions | undefined
|
|
28
|
-
): Effect.Effect<number, FridaSessionError.FridaSessionError, FridaDevice.FridaDevice | Scope.Scope> =>
|
|
29
|
-
Effect.flatMap(FridaDevice.FridaDevice, ({ device }) =>
|
|
30
|
-
Effect.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
44
|
+
): Effect.Effect<number, FridaSessionError.FridaSessionError, FridaDevice.FridaDevice | Scope.Scope> => {
|
|
45
|
+
const spawnEffect = Effect.flatMap(FridaDevice.FridaDevice, ({ device }) =>
|
|
46
|
+
Effect.tryPromise({
|
|
47
|
+
try: (signal) => {
|
|
48
|
+
const cancellable = new Frida.Cancellable();
|
|
49
|
+
signal.onabort = () => cancellable.cancel();
|
|
50
|
+
return device.spawn(program as string | Array<string>, options, cancellable);
|
|
51
|
+
},
|
|
52
|
+
catch: (cause) =>
|
|
53
|
+
new FridaSessionError.FridaSessionError({
|
|
54
|
+
cause,
|
|
55
|
+
when: "spawn",
|
|
56
|
+
}),
|
|
57
|
+
})
|
|
37
58
|
);
|
|
38
59
|
|
|
60
|
+
const resumeEffect = (pid: number) =>
|
|
61
|
+
Effect.flatMap(FridaDevice.FridaDevice, ({ device }) =>
|
|
62
|
+
Effect.tryPromise({
|
|
63
|
+
try: (signal) => {
|
|
64
|
+
const cancellable = new Frida.Cancellable();
|
|
65
|
+
signal.onabort = () => cancellable.cancel();
|
|
66
|
+
return device.resume(pid, cancellable);
|
|
67
|
+
},
|
|
68
|
+
catch: (cause) =>
|
|
69
|
+
new FridaSessionError.FridaSessionError({
|
|
70
|
+
cause,
|
|
71
|
+
when: "resume",
|
|
72
|
+
}),
|
|
73
|
+
})
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const release = (pid: number) =>
|
|
77
|
+
Effect.flatMap(FridaDevice.FridaDevice, ({ device }) =>
|
|
78
|
+
Effect.promise((signal) => {
|
|
79
|
+
const cancellable = new Frida.Cancellable();
|
|
80
|
+
signal.onabort = () => cancellable.cancel();
|
|
81
|
+
return device.kill(pid, cancellable);
|
|
82
|
+
})
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const acquire = Effect.tap(spawnEffect, resumeEffect);
|
|
86
|
+
const resource = Effect.acquireRelease(acquire, release);
|
|
87
|
+
return resource;
|
|
88
|
+
};
|
|
89
|
+
|
|
39
90
|
/** @internal */
|
|
40
91
|
export const attach = (
|
|
41
92
|
target: Frida.TargetProcess,
|
|
@@ -44,30 +95,87 @@ export const attach = (
|
|
|
44
95
|
FridaSession.FridaSession,
|
|
45
96
|
FridaSessionError.FridaSessionError,
|
|
46
97
|
FridaDevice.FridaDevice | Scope.Scope
|
|
47
|
-
> =>
|
|
48
|
-
Effect.flatMap(FridaDevice.FridaDevice, ({ device }) =>
|
|
49
|
-
Effect.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
98
|
+
> => {
|
|
99
|
+
const acquire = Effect.flatMap(FridaDevice.FridaDevice, ({ device }) =>
|
|
100
|
+
Effect.tryPromise({
|
|
101
|
+
try: (signal) => {
|
|
102
|
+
const cancellable = new Frida.Cancellable();
|
|
103
|
+
signal.onabort = () => cancellable.cancel();
|
|
104
|
+
return device.attach(target, options, cancellable);
|
|
105
|
+
},
|
|
106
|
+
catch: (cause) =>
|
|
107
|
+
new FridaSessionError.FridaSessionError({
|
|
108
|
+
cause,
|
|
109
|
+
when: "attach",
|
|
110
|
+
}),
|
|
111
|
+
})
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const release = (session: Frida.Session) =>
|
|
115
|
+
Effect.promise((signal) => {
|
|
116
|
+
const cancellable = new Frida.Cancellable();
|
|
117
|
+
signal.onabort = () => cancellable.cancel();
|
|
118
|
+
return session.detach(cancellable);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const resource = Effect.acquireRelease(acquire, release);
|
|
122
|
+
return Effect.map(
|
|
123
|
+
resource,
|
|
124
|
+
(session) =>
|
|
125
|
+
({
|
|
126
|
+
session,
|
|
127
|
+
resume: Effect.tryPromise((signal) => {
|
|
128
|
+
const cancellable = new Frida.Cancellable();
|
|
129
|
+
signal.onabort = () => cancellable.cancel();
|
|
130
|
+
return session.resume(cancellable);
|
|
131
|
+
}),
|
|
132
|
+
enableChildGating: Effect.tryPromise((signal) => {
|
|
133
|
+
const cancellable = new Frida.Cancellable();
|
|
134
|
+
signal.onabort = () => cancellable.cancel();
|
|
135
|
+
return session.enableChildGating(cancellable);
|
|
136
|
+
}),
|
|
137
|
+
disableChildGating: Effect.tryPromise((signal) => {
|
|
138
|
+
const cancellable = new Frida.Cancellable();
|
|
139
|
+
signal.onabort = () => cancellable.cancel();
|
|
140
|
+
return session.disableChildGating(cancellable);
|
|
54
141
|
}),
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
142
|
+
setupPeerConnection: (opts?: Frida.PeerOptions | undefined) =>
|
|
143
|
+
Effect.tryPromise((signal) => {
|
|
144
|
+
const cancellable = new Frida.Cancellable();
|
|
145
|
+
signal.onabort = () => cancellable.cancel();
|
|
146
|
+
return session.setupPeerConnection(opts, cancellable);
|
|
147
|
+
}),
|
|
148
|
+
joinPortal: (address: string, opts?: Frida.PortalOptions | undefined) =>
|
|
149
|
+
Effect.tryPromise((signal) => {
|
|
150
|
+
const cancellable = new Frida.Cancellable();
|
|
151
|
+
signal.onabort = () => cancellable.cancel();
|
|
152
|
+
return session.joinPortal(address, opts, cancellable);
|
|
153
|
+
}),
|
|
154
|
+
[FridaSessionTypeId]: FridaSessionTypeId,
|
|
155
|
+
}) as const
|
|
59
156
|
);
|
|
157
|
+
};
|
|
60
158
|
|
|
61
159
|
/** @internal */
|
|
62
160
|
export const layer = (
|
|
63
|
-
target: string
|
|
161
|
+
target: number | string | ReadonlyArray<string>,
|
|
64
162
|
options?: (Frida.SpawnOptions & Frida.SessionOptions) | undefined
|
|
65
163
|
): Layer.Layer<FridaSession.FridaSession, FridaSessionError.FridaSessionError, FridaDevice.FridaDevice> =>
|
|
66
164
|
Layer.scoped(
|
|
67
165
|
Tag,
|
|
68
166
|
Effect.gen(function* () {
|
|
69
|
-
const pid = yield*
|
|
167
|
+
const pid = yield* Match.value(target).pipe(
|
|
168
|
+
Match.when(Match.number, (pid) => Effect.succeed(pid)),
|
|
169
|
+
Match.when(Match.string, (proc) => spawn(proc)),
|
|
170
|
+
Match.orElse((proc) => spawn(proc))
|
|
171
|
+
);
|
|
70
172
|
const session = yield* attach(pid, options);
|
|
71
173
|
return session;
|
|
72
174
|
})
|
|
73
175
|
);
|
|
176
|
+
|
|
177
|
+
/** @internal */
|
|
178
|
+
export const layerFrontmost = (
|
|
179
|
+
options?: Frida.FrontmostQueryOptions | undefined
|
|
180
|
+
): Layer.Layer<FridaSession.FridaSession, FridaSessionError.FridaSessionError, FridaDevice.FridaDevice> =>
|
|
181
|
+
Layer.unwrapEffect(Effect.map(frontmost(options), (app) => layer(app.pid)));
|