@efffrida/frida-tools 0.0.1

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.
Files changed (72) hide show
  1. package/FridaDevice/package.json +6 -0
  2. package/FridaDeviceAcquisitionError/package.json +6 -0
  3. package/FridaScript/package.json +6 -0
  4. package/FridaSession/package.json +6 -0
  5. package/FridaSessionError/package.json +6 -0
  6. package/LICENSE +674 -0
  7. package/README.md +30 -0
  8. package/dist/cjs/FridaDevice.js +61 -0
  9. package/dist/cjs/FridaDevice.js.map +1 -0
  10. package/dist/cjs/FridaDeviceAcquisitionError.js +34 -0
  11. package/dist/cjs/FridaDeviceAcquisitionError.js.map +1 -0
  12. package/dist/cjs/FridaScript.js +41 -0
  13. package/dist/cjs/FridaScript.js.map +1 -0
  14. package/dist/cjs/FridaSession.js +46 -0
  15. package/dist/cjs/FridaSession.js.map +1 -0
  16. package/dist/cjs/FridaSessionError.js +38 -0
  17. package/dist/cjs/FridaSessionError.js.map +1 -0
  18. package/dist/cjs/index.js +19 -0
  19. package/dist/cjs/index.js.map +1 -0
  20. package/dist/cjs/internal/device.js +69 -0
  21. package/dist/cjs/internal/device.js.map +1 -0
  22. package/dist/cjs/internal/script.js +164 -0
  23. package/dist/cjs/internal/script.js.map +1 -0
  24. package/dist/cjs/internal/session.js +56 -0
  25. package/dist/cjs/internal/session.js.map +1 -0
  26. package/dist/dts/FridaDevice.d.ts +69 -0
  27. package/dist/dts/FridaDevice.d.ts.map +1 -0
  28. package/dist/dts/FridaDeviceAcquisitionError.d.ts +35 -0
  29. package/dist/dts/FridaDeviceAcquisitionError.d.ts.map +1 -0
  30. package/dist/dts/FridaScript.d.ts +80 -0
  31. package/dist/dts/FridaScript.d.ts.map +1 -0
  32. package/dist/dts/FridaSession.d.ts +56 -0
  33. package/dist/dts/FridaSession.d.ts.map +1 -0
  34. package/dist/dts/FridaSessionError.d.ts +35 -0
  35. package/dist/dts/FridaSessionError.d.ts.map +1 -0
  36. package/dist/dts/index.d.ts +31 -0
  37. package/dist/dts/index.d.ts.map +1 -0
  38. package/dist/dts/internal/device.d.ts +2 -0
  39. package/dist/dts/internal/device.d.ts.map +1 -0
  40. package/dist/dts/internal/script.d.ts +2 -0
  41. package/dist/dts/internal/script.d.ts.map +1 -0
  42. package/dist/dts/internal/session.d.ts +2 -0
  43. package/dist/dts/internal/session.d.ts.map +1 -0
  44. package/dist/esm/FridaDevice.js +52 -0
  45. package/dist/esm/FridaDevice.js.map +1 -0
  46. package/dist/esm/FridaDeviceAcquisitionError.js +23 -0
  47. package/dist/esm/FridaDeviceAcquisitionError.js.map +1 -0
  48. package/dist/esm/FridaScript.js +32 -0
  49. package/dist/esm/FridaScript.js.map +1 -0
  50. package/dist/esm/FridaSession.js +37 -0
  51. package/dist/esm/FridaSession.js.map +1 -0
  52. package/dist/esm/FridaSessionError.js +27 -0
  53. package/dist/esm/FridaSessionError.js.map +1 -0
  54. package/dist/esm/index.js +31 -0
  55. package/dist/esm/index.js.map +1 -0
  56. package/dist/esm/internal/device.js +55 -0
  57. package/dist/esm/internal/device.js.map +1 -0
  58. package/dist/esm/internal/script.js +155 -0
  59. package/dist/esm/internal/script.js.map +1 -0
  60. package/dist/esm/internal/session.js +44 -0
  61. package/dist/esm/internal/session.js.map +1 -0
  62. package/dist/esm/package.json +4 -0
  63. package/package.json +76 -0
  64. package/src/FridaDevice.ts +103 -0
  65. package/src/FridaDeviceAcquisitionError.ts +42 -0
  66. package/src/FridaScript.ts +117 -0
  67. package/src/FridaSession.ts +76 -0
  68. package/src/FridaSessionError.ts +42 -0
  69. package/src/index.ts +34 -0
  70. package/src/internal/device.ts +93 -0
  71. package/src/internal/script.ts +208 -0
  72. package/src/internal/session.ts +73 -0
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Frida sessions
3
+ *
4
+ * @since 1.0.0
5
+ */
6
+
7
+ import type * as Context from "effect/Context";
8
+ import type * as Effect from "effect/Effect";
9
+ import type * as Layer from "effect/Layer";
10
+ import type * as Scope from "effect/Scope";
11
+ import type * as Frida from "frida";
12
+ import type * as FridaDevice from "./FridaDevice.js";
13
+ import type * as FridaSessionError from "./FridaSessionError.js";
14
+
15
+ import * as internal from "./internal/session.js";
16
+
17
+ /**
18
+ * @since 1.0.0
19
+ * @category Type ids
20
+ */
21
+ export const FridaSessionTypeId: unique symbol = internal.FridaSessionTypeId;
22
+
23
+ /**
24
+ * @since 1.0.0
25
+ * @category Type ids
26
+ */
27
+ export type FridaSessionTypeId = typeof FridaSessionTypeId;
28
+
29
+ /**
30
+ * @since 1.0.0
31
+ * @category Models
32
+ */
33
+ export interface FridaSession {
34
+ readonly session: Frida.Session;
35
+ readonly [FridaSessionTypeId]: typeof FridaSessionTypeId;
36
+ }
37
+
38
+ /**
39
+ * @since 1.0.0
40
+ * @category Tags
41
+ */
42
+ export const FridaSession: Context.Tag<FridaSession, FridaSession> = internal.Tag;
43
+
44
+ /**
45
+ * @since 1.0.0
46
+ * @category Predicates
47
+ */
48
+ export const isFridaSession: (u: unknown) => u is FridaSession = internal.isFridaSession;
49
+
50
+ /**
51
+ * @since 1.0.0
52
+ * @category Frida
53
+ */
54
+ export const spawn: (
55
+ program: string | Array<string>,
56
+ options?: Frida.SpawnOptions | undefined
57
+ ) => Effect.Effect<number, FridaSessionError.FridaSessionError, FridaDevice.FridaDevice | Scope.Scope> = internal.spawn;
58
+
59
+ /**
60
+ * @since 1.0.0
61
+ * @category Frida
62
+ */
63
+ export const attach: (
64
+ target: Frida.TargetProcess,
65
+ options?: Frida.SessionOptions | undefined
66
+ ) => Effect.Effect<FridaSession, FridaSessionError.FridaSessionError, FridaDevice.FridaDevice | Scope.Scope> =
67
+ internal.attach;
68
+
69
+ /**
70
+ * @since 1.0.0
71
+ * @category Layers
72
+ */
73
+ export const layer: (
74
+ target: string,
75
+ options?: (Frida.SpawnOptions & Frida.SessionOptions) | undefined
76
+ ) => Layer.Layer<FridaSession, FridaSessionError.FridaSessionError, FridaDevice.FridaDevice> = internal.layer;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Frida session errors
3
+ *
4
+ * @since 1.0.0
5
+ */
6
+
7
+ import * as PlatformError from "@effect/platform/Error";
8
+ import * as Predicate from "effect/Predicate";
9
+
10
+ /**
11
+ * @since 1.0.0
12
+ * @category Errors
13
+ */
14
+ export const FridaSessionErrorTypeId: unique symbol = Symbol.for(
15
+ "@efffrida/FridaError/FridaSessionError"
16
+ ) as FridaSessionErrorTypeId;
17
+
18
+ /**
19
+ * @since 1.0.0
20
+ * @category Errors
21
+ */
22
+ export type FridaSessionErrorTypeId = typeof FridaSessionErrorTypeId;
23
+
24
+ /**
25
+ * @since 1.0.0
26
+ * @category Errors
27
+ */
28
+ export const isFridaSessionError = (u: unknown): u is FridaSessionError =>
29
+ Predicate.hasProperty(u, FridaSessionErrorTypeId);
30
+
31
+ /**
32
+ * @since 1.0.0
33
+ * @category Errors
34
+ */
35
+ export class FridaSessionError extends PlatformError.TypeIdError(FridaSessionErrorTypeId, "FridaSessionError")<{
36
+ cause: unknown;
37
+ when: "spawn" | "kill" | "attach" | "detach" | "compile" | "load" | "unload" | "resume" | "message" | "rpcCall";
38
+ }> {
39
+ get message() {
40
+ return `A Frida session error occurred on ${this.when}`;
41
+ }
42
+ }
package/src/index.ts ADDED
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Frida devices
3
+ *
4
+ * @since 1.0.0
5
+ */
6
+ export * as FridaDevice from "./FridaDevice.js"
7
+
8
+ /**
9
+ * Frida device acquisition errors
10
+ *
11
+ * @since 1.0.0
12
+ */
13
+ export * as FridaDeviceAcquisitionError from "./FridaDeviceAcquisitionError.js"
14
+
15
+ /**
16
+ * Frida scripts
17
+ *
18
+ * @since 1.0.0
19
+ */
20
+ export * as FridaScript from "./FridaScript.js"
21
+
22
+ /**
23
+ * Frida sessions
24
+ *
25
+ * @since 1.0.0
26
+ */
27
+ export * as FridaSession from "./FridaSession.js"
28
+
29
+ /**
30
+ * Frida session errors
31
+ *
32
+ * @since 1.0.0
33
+ */
34
+ export * as FridaSessionError from "./FridaSessionError.js"
@@ -0,0 +1,93 @@
1
+ import type * as FridaDevice from "../FridaDevice.js";
2
+
3
+ import * as Context from "effect/Context";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Layer from "effect/Layer";
6
+ import * as Predicate from "effect/Predicate";
7
+ import * as Frida from "frida";
8
+ import * as FridaDeviceAcquisitionError from "../FridaDeviceAcquisitionError.js";
9
+
10
+ /** @internal */
11
+ export const FridaDeviceTypeId: FridaDevice.FridaDeviceTypeId = Symbol.for(
12
+ "@efffrida/frida-tools/FridaDevice"
13
+ ) as FridaDevice.FridaDeviceTypeId;
14
+
15
+ /** @internal */
16
+ export const Tag = Context.GenericTag<FridaDevice.FridaDevice>("@efffrida/frida-tools/FridaDevice");
17
+
18
+ /** @internal */
19
+ export const isFridaDevice = (u: unknown): u is FridaDevice.FridaDevice => Predicate.hasProperty(u, FridaDeviceTypeId);
20
+
21
+ /** @internal */
22
+ export const acquireUsbDevice = (
23
+ options?: Frida.GetDeviceOptions | undefined
24
+ ): Effect.Effect<FridaDevice.FridaDevice, FridaDeviceAcquisitionError.FridaDeviceAcquisitionError, never> =>
25
+ Effect.map(
26
+ Effect.tryPromise({
27
+ try: () => Frida.getUsbDevice(options),
28
+ catch: (cause) =>
29
+ new FridaDeviceAcquisitionError.FridaDeviceAcquisitionError({
30
+ cause,
31
+ attempts: 1,
32
+ acquisitionMethod: "usb",
33
+ }),
34
+ }),
35
+ (device) => ({ device, [FridaDeviceTypeId]: FridaDeviceTypeId }) as const
36
+ );
37
+
38
+ /** @internal */
39
+ export const acquireRemoteDevice = (
40
+ address: string,
41
+ options?: Frida.RemoteDeviceOptions | undefined
42
+ ): Effect.Effect<FridaDevice.FridaDevice, FridaDeviceAcquisitionError.FridaDeviceAcquisitionError, never> =>
43
+ Effect.map(
44
+ Effect.tryPromise({
45
+ try: () => Frida.getDeviceManager().addRemoteDevice(address, options),
46
+ catch: (cause) =>
47
+ new FridaDeviceAcquisitionError.FridaDeviceAcquisitionError({
48
+ cause,
49
+ attempts: 1,
50
+ acquisitionMethod: "remote",
51
+ }),
52
+ }),
53
+ (device) => ({ device, [FridaDeviceTypeId]: FridaDeviceTypeId }) as const
54
+ );
55
+
56
+ /** @internal */
57
+ export const acquireLocalDevice = (): Effect.Effect<
58
+ FridaDevice.FridaDevice,
59
+ FridaDeviceAcquisitionError.FridaDeviceAcquisitionError,
60
+ never
61
+ > =>
62
+ Effect.map(
63
+ Effect.tryPromise({
64
+ try: () => Frida.getLocalDevice(),
65
+ catch: (cause) =>
66
+ new FridaDeviceAcquisitionError.FridaDeviceAcquisitionError({
67
+ cause,
68
+ attempts: 1,
69
+ acquisitionMethod: "local",
70
+ }),
71
+ }),
72
+ (device) => ({ device, [FridaDeviceTypeId]: FridaDeviceTypeId }) as const
73
+ );
74
+
75
+ /** @internal */
76
+ export const layerRemoteDevice = (
77
+ address: string,
78
+ options?: Frida.RemoteDeviceOptions | undefined
79
+ ): Layer.Layer<FridaDevice.FridaDevice, FridaDeviceAcquisitionError.FridaDeviceAcquisitionError, never> =>
80
+ Layer.effect(Tag, acquireRemoteDevice(address, options));
81
+
82
+ /** @internal */
83
+ export const layerUsbDevice = (
84
+ options?: Frida.GetDeviceOptions | undefined
85
+ ): Layer.Layer<FridaDevice.FridaDevice, FridaDeviceAcquisitionError.FridaDeviceAcquisitionError, never> =>
86
+ Layer.effect(Tag, acquireUsbDevice(options));
87
+
88
+ /** @internal */
89
+ export const layerLocalDevice: Layer.Layer<
90
+ FridaDevice.FridaDevice,
91
+ FridaDeviceAcquisitionError.FridaDeviceAcquisitionError,
92
+ never
93
+ > = Layer.effect(Tag, acquireLocalDevice());
@@ -0,0 +1,208 @@
1
+ import type * as Scope from "effect/Scope";
2
+ import type * as FridaScript from "../FridaScript.js";
3
+
4
+ import * as Context from "effect/Context";
5
+ import * as Deferred from "effect/Deferred";
6
+ import * as Effect from "effect/Effect";
7
+ import * as Function from "effect/Function";
8
+ import * as Layer from "effect/Layer";
9
+ import * as Option from "effect/Option";
10
+ import * as Predicate from "effect/Predicate";
11
+ import * as Queue from "effect/Queue";
12
+ import * as Sink from "effect/Sink";
13
+ import * as Stream from "effect/Stream";
14
+ import * as Take from "effect/Take";
15
+ import * as Frida from "frida";
16
+ import * as FridaDevice from "../FridaDevice.js";
17
+ import * as FridaSession from "../FridaSession.js";
18
+ import * as FridaSessionError from "../FridaSessionError.js";
19
+
20
+ /** @internal */
21
+ export const FridaScriptTypeId: FridaScript.FridaScriptTypeId = Symbol.for(
22
+ "@efffrida/frida-tools/FridaScript"
23
+ ) as FridaScript.FridaScriptTypeId;
24
+
25
+ /** @internal */
26
+ export const Tag = Context.GenericTag<FridaScript.FridaScript>("@efffrida/frida-tools/FridaScript");
27
+
28
+ /** @internal */
29
+ export const isFridaScript = (u: unknown): u is FridaScript.FridaScript => Predicate.hasProperty(u, FridaScriptTypeId);
30
+
31
+ /** @internal */
32
+ export const load = Function.dual<
33
+ (
34
+ options?: (Frida.ScriptOptions & { readonly resume?: boolean | undefined }) | undefined
35
+ ) => (
36
+ source: string | Buffer
37
+ ) => Effect.Effect<
38
+ FridaScript.FridaScript,
39
+ FridaSessionError.FridaSessionError,
40
+ FridaSession.FridaSession | FridaDevice.FridaDevice | Scope.Scope
41
+ >,
42
+ (
43
+ source: string | Buffer,
44
+ options?: (Frida.ScriptOptions & { readonly resume?: boolean | undefined }) | undefined
45
+ ) => Effect.Effect<
46
+ FridaScript.FridaScript,
47
+ FridaSessionError.FridaSessionError,
48
+ FridaSession.FridaSession | FridaDevice.FridaDevice | Scope.Scope
49
+ >
50
+ >(
51
+ (arguments_) => Predicate.isString(arguments_[0]) || Buffer.isBuffer(arguments_[0]),
52
+ Effect.fnUntraced(
53
+ function* (
54
+ source: string | Buffer,
55
+ options?: (Frida.ScriptOptions & { readonly resume?: boolean | undefined }) | undefined
56
+ ) {
57
+ const { device } = yield* FridaDevice.FridaDevice;
58
+ const { session } = yield* FridaSession.FridaSession;
59
+
60
+ const script = Predicate.isString(source)
61
+ ? yield* Effect.tryPromise({
62
+ try: () => session.createScript(source, { runtime: Frida.ScriptRuntime.V8, ...options }),
63
+ catch: (cause) => new FridaSessionError.FridaSessionError({ cause, when: "compile" }),
64
+ })
65
+ : yield* Effect.tryPromise({
66
+ try: () => session.createScriptFromBytes(source, { runtime: Frida.ScriptRuntime.V8, ...options }),
67
+ catch: (cause) => new FridaSessionError.FridaSessionError({ cause, when: "compile" }),
68
+ });
69
+
70
+ const messageQueue =
71
+ yield* Queue.unbounded<
72
+ Take.Take<{ message: any; data: Option.Option<Buffer> }, FridaSessionError.FridaSessionError>
73
+ >();
74
+
75
+ let scriptDefectCause: unknown = undefined;
76
+ const messageHandler: Frida.ScriptMessageHandler = (message: Frida.Message, data: Buffer | null): void => {
77
+ switch (message.type) {
78
+ case Frida.MessageType.Error: {
79
+ const error = new Error();
80
+ error.name = "FridaScriptDefect";
81
+ error.stack = message.stack ?? "";
82
+ error.message = message.description.replace(/Error: /, "") ?? "";
83
+ scriptDefectCause = error;
84
+ const sessionError = new FridaSessionError.FridaSessionError({
85
+ when: "message",
86
+ cause: error.cause,
87
+ });
88
+ const asTake = Take.fail(sessionError);
89
+ Queue.unsafeOffer(messageQueue, asTake);
90
+ break;
91
+ }
92
+
93
+ case Frida.MessageType.Send: {
94
+ const asTake = Take.of({ message: message.payload, data: Option.fromNullable(data) });
95
+ Queue.unsafeOffer(messageQueue, asTake);
96
+ break;
97
+ }
98
+
99
+ default:
100
+ Function.absurd(message);
101
+ }
102
+ };
103
+
104
+ const disconnectMessageHandler = Effect.sync(() => script.message.disconnect(messageHandler));
105
+ yield* Effect.addFinalizer(() => Effect.flatMap(messageQueue.shutdown, () => disconnectMessageHandler));
106
+ script.message.connect(messageHandler);
107
+
108
+ const stream = Stream.fromQueue(messageQueue).pipe(Stream.flattenTake);
109
+ const sink = Sink.forEach<
110
+ { message: any; data: Option.Option<Buffer> },
111
+ void,
112
+ FridaSessionError.FridaSessionError,
113
+ never
114
+ >(({ data, message }) => {
115
+ if (Predicate.isNotUndefined(scriptDefectCause)) {
116
+ return Effect.fail(
117
+ new FridaSessionError.FridaSessionError({
118
+ when: "rpcCall",
119
+ cause: scriptDefectCause,
120
+ })
121
+ );
122
+ } else {
123
+ return Effect.sync(() => script.post(message, Option.getOrNull(data)));
124
+ }
125
+ });
126
+
127
+ const destroyed = yield* Deferred.make<void, never>();
128
+ const destroyedHandler = () => Deferred.unsafeDone(destroyed, Effect.void);
129
+ script.destroyed.connect(destroyedHandler);
130
+
131
+ const callExport = (exportName: string) =>
132
+ Effect.fn(`call frida export ${exportName}`)(function* (...args: Array<any>) {
133
+ yield* Effect.annotateCurrentSpan("args", args);
134
+
135
+ const isDestroyed = yield* Deferred.isDone(destroyed);
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
+
150
+ const result = yield* Effect.tryPromise({
151
+ try: () => script.exports[exportName](...args) as Promise<unknown>,
152
+ catch: (cause) => new FridaSessionError.FridaSessionError({ when: "rpcCall", cause }),
153
+ });
154
+ yield* Effect.annotateCurrentSpan("result", result);
155
+ return result;
156
+ });
157
+
158
+ yield* Effect.tryPromise({
159
+ try: () => script.load(),
160
+ catch: (cause) => new FridaSessionError.FridaSessionError({ cause, when: "load" }),
161
+ });
162
+
163
+ if (options?.resume === true) {
164
+ yield* Effect.tryPromise({
165
+ try: () => device.resume(session.pid),
166
+ catch: (cause) => new FridaSessionError.FridaSessionError({ cause, when: "resume" }),
167
+ });
168
+ }
169
+
170
+ return {
171
+ script,
172
+ stream,
173
+ sink,
174
+ destroyed,
175
+ callExport,
176
+ [FridaScriptTypeId]: FridaScriptTypeId,
177
+ } as const;
178
+ },
179
+ Effect.acquireRelease(({ script }: FridaScript.FridaScript) => Effect.promise(() => script.unload()))
180
+ )
181
+ );
182
+
183
+ /** @internal */
184
+ export const layer = Function.dual<
185
+ (
186
+ options?: (Frida.ScriptOptions & { readonly resume?: boolean | undefined }) | undefined
187
+ ) => (
188
+ source: string | Buffer
189
+ ) => Layer.Layer<
190
+ FridaScript.FridaScript,
191
+ FridaSessionError.FridaSessionError,
192
+ FridaSession.FridaSession | FridaDevice.FridaDevice
193
+ >,
194
+ (
195
+ source: string | Buffer,
196
+ options?: (Frida.ScriptOptions & { readonly resume?: boolean | undefined }) | undefined
197
+ ) => Layer.Layer<
198
+ FridaScript.FridaScript,
199
+ FridaSessionError.FridaSessionError,
200
+ FridaSession.FridaSession | FridaDevice.FridaDevice
201
+ >
202
+ >(
203
+ (arguments_) => Predicate.isString(arguments_[0]),
204
+ (
205
+ source: string | Buffer,
206
+ options?: (Frida.ScriptOptions & { readonly resume?: boolean | undefined }) | undefined
207
+ ) => Layer.scoped(Tag, load(source, options))
208
+ );
@@ -0,0 +1,73 @@
1
+ import type * as Scope from "effect/Scope";
2
+ import type * as Frida from "frida";
3
+ import type * as FridaSession from "../FridaSession.js";
4
+
5
+ import * as Context from "effect/Context";
6
+ import * as Effect from "effect/Effect";
7
+ import * as Layer from "effect/Layer";
8
+ import * as Predicate from "effect/Predicate";
9
+ import * as FridaDevice from "../FridaDevice.js";
10
+ import * as FridaSessionError from "../FridaSessionError.js";
11
+
12
+ /** @internal */
13
+ export const FridaSessionTypeId: FridaSession.FridaSessionTypeId = Symbol.for(
14
+ "@efffrida/frida-tools/FridaSession"
15
+ ) as FridaSession.FridaSessionTypeId;
16
+
17
+ /** @internal */
18
+ export const Tag = Context.GenericTag<FridaSession.FridaSession>("@efffrida/frida-tools/FridaSession");
19
+
20
+ /** @internal */
21
+ export const isFridaSession = (u: unknown): u is FridaSession.FridaSession =>
22
+ Predicate.hasProperty(u, FridaSessionTypeId);
23
+
24
+ /** @internal */
25
+ export const spawn = (
26
+ program: string | Array<string>,
27
+ options?: Frida.SpawnOptions | undefined
28
+ ): Effect.Effect<number, FridaSessionError.FridaSessionError, FridaDevice.FridaDevice | Scope.Scope> =>
29
+ Effect.flatMap(FridaDevice.FridaDevice, ({ device }) =>
30
+ Effect.acquireRelease(
31
+ Effect.tryPromise({
32
+ try: () => device.spawn(program, options),
33
+ catch: (cause) => new FridaSessionError.FridaSessionError({ cause, when: "spawn" }),
34
+ }),
35
+ (pid) => Effect.promise(() => device.kill(pid))
36
+ )
37
+ );
38
+
39
+ /** @internal */
40
+ export const attach = (
41
+ target: Frida.TargetProcess,
42
+ options?: Frida.SessionOptions | undefined
43
+ ): Effect.Effect<
44
+ FridaSession.FridaSession,
45
+ FridaSessionError.FridaSessionError,
46
+ FridaDevice.FridaDevice | Scope.Scope
47
+ > =>
48
+ Effect.flatMap(FridaDevice.FridaDevice, ({ device }) =>
49
+ Effect.acquireRelease(
50
+ Effect.map(
51
+ Effect.tryPromise({
52
+ try: () => device.attach(target, options),
53
+ catch: (cause) => new FridaSessionError.FridaSessionError({ cause, when: "attach" }),
54
+ }),
55
+ (session) => ({ session, [FridaSessionTypeId]: FridaSessionTypeId }) as const
56
+ ),
57
+ ({ session }: FridaSession.FridaSession) => Effect.promise(() => session.detach())
58
+ )
59
+ );
60
+
61
+ /** @internal */
62
+ export const layer = (
63
+ target: string,
64
+ options?: (Frida.SpawnOptions & Frida.SessionOptions) | undefined
65
+ ): Layer.Layer<FridaSession.FridaSession, FridaSessionError.FridaSessionError, FridaDevice.FridaDevice> =>
66
+ Layer.scoped(
67
+ Tag,
68
+ Effect.gen(function* () {
69
+ const pid = yield* spawn(target, options);
70
+ const session = yield* attach(pid, options);
71
+ return session;
72
+ })
73
+ );