@efffrida/rpc 0.0.14 → 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.
Files changed (63) hide show
  1. package/dist/frida/FridaRpcClient.d.ts +47 -0
  2. package/dist/frida/FridaRpcClient.d.ts.map +1 -0
  3. package/dist/frida/FridaRpcClient.js +115 -0
  4. package/dist/frida/FridaRpcClient.js.map +1 -0
  5. package/dist/frida/FridaRpcServer.d.ts +48 -0
  6. package/dist/frida/FridaRpcServer.d.ts.map +1 -0
  7. package/dist/frida/FridaRpcServer.js +119 -0
  8. package/dist/frida/FridaRpcServer.js.map +1 -0
  9. package/dist/frida/index.d.ts +18 -0
  10. package/dist/frida/index.d.ts.map +1 -0
  11. package/dist/{esm → frida}/index.js +5 -4
  12. package/dist/frida/index.js.map +1 -0
  13. package/dist/{dts → node}/FridaRpcClient.d.ts +4 -6
  14. package/dist/node/FridaRpcClient.d.ts.map +1 -0
  15. package/dist/node/FridaRpcClient.js +86 -0
  16. package/dist/node/FridaRpcClient.js.map +1 -0
  17. package/dist/node/FridaRpcServer.d.ts +16 -0
  18. package/dist/node/FridaRpcServer.d.ts.map +1 -0
  19. package/dist/node/FridaRpcServer.js +115 -0
  20. package/dist/node/FridaRpcServer.js.map +1 -0
  21. package/dist/node/index.d.ts +18 -0
  22. package/dist/node/index.d.ts.map +1 -0
  23. package/dist/{dts/index.d.ts → node/index.js} +6 -5
  24. package/dist/node/index.js.map +1 -0
  25. package/dist/shared/constants.d.ts +6 -0
  26. package/dist/shared/constants.d.ts.map +1 -0
  27. package/dist/shared/constants.js +7 -0
  28. package/dist/shared/constants.js.map +1 -0
  29. package/dist/shared/predicates.d.ts +10 -0
  30. package/dist/shared/predicates.d.ts.map +1 -0
  31. package/dist/shared/predicates.js +12 -0
  32. package/dist/shared/predicates.js.map +1 -0
  33. package/package.json +59 -45
  34. package/src/frida/FridaRpcClient.ts +180 -0
  35. package/src/frida/FridaRpcServer.ts +168 -0
  36. package/src/frida/index.ts +19 -0
  37. package/src/node/FridaRpcClient.ts +120 -0
  38. package/src/node/FridaRpcServer.ts +134 -0
  39. package/src/node/index.ts +19 -0
  40. package/src/shared/constants.ts +11 -0
  41. package/src/shared/predicates.ts +19 -0
  42. package/FridaRpcClient/package.json +0 -6
  43. package/FridaRpcServer/package.json +0 -6
  44. package/dist/cjs/FridaRpcClient.js +0 -77
  45. package/dist/cjs/FridaRpcClient.js.map +0 -1
  46. package/dist/cjs/FridaRpcServer.js +0 -135
  47. package/dist/cjs/FridaRpcServer.js.map +0 -1
  48. package/dist/cjs/index.js +0 -12
  49. package/dist/cjs/index.js.map +0 -1
  50. package/dist/dts/FridaRpcClient.d.ts.map +0 -1
  51. package/dist/dts/FridaRpcServer.d.ts +0 -36
  52. package/dist/dts/FridaRpcServer.d.ts.map +0 -1
  53. package/dist/dts/index.d.ts.map +0 -1
  54. package/dist/esm/FridaRpcClient.js +0 -67
  55. package/dist/esm/FridaRpcClient.js.map +0 -1
  56. package/dist/esm/FridaRpcServer.js +0 -125
  57. package/dist/esm/FridaRpcServer.js.map +0 -1
  58. package/dist/esm/index.js.map +0 -1
  59. package/dist/esm/package.json +0 -4
  60. package/index/package.json +0 -6
  61. package/src/FridaRpcClient.ts +0 -107
  62. package/src/FridaRpcServer.ts +0 -172
  63. package/src/index.ts +0 -17
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Implements a Frida RPC server protocol for effect using the frida script
3
+ * exports.
4
+ *
5
+ * @since 1.0.0
6
+ */
7
+ import * as RpcMessage from "@effect/rpc/RpcMessage";
8
+ import * as RpcSerialization from "@effect/rpc/RpcSerialization";
9
+ import * as RpcServer from "@effect/rpc/RpcServer";
10
+ import * as FridaScript from "@efffrida/frida-tools/FridaScript";
11
+ import * as Console from "effect/Console";
12
+ import * as Effect from "effect/Effect";
13
+ import * as Function from "effect/Function";
14
+ import * as Mailbox from "effect/Mailbox";
15
+ import * as Option from "effect/Option";
16
+ import * as Predicate from "effect/Predicate";
17
+ import * as Schema from "effect/Schema";
18
+ import * as Stream from "effect/Stream";
19
+ import * as String from "effect/String";
20
+ import * as Tuple from "effect/Tuple";
21
+ import * as constants from "../shared/constants.js";
22
+ import * as predicates from "../shared/predicates.js";
23
+ /**
24
+ * @since 1.0.0
25
+ * @category Protocol
26
+ */
27
+ export const makeProtocolFrida = () => Effect.gen(function* () {
28
+ const encoder = new TextEncoder();
29
+ const script = yield* FridaScript.FridaScript;
30
+ const serialization = yield* RpcSerialization.RpcSerialization;
31
+ let clientId = 0;
32
+ const clientIds = new Set();
33
+ const parser = serialization.unsafeMake();
34
+ const disconnects = yield* Mailbox.make();
35
+ let writeRequest;
36
+ // Listen for new clients
37
+ yield* script.stream.pipe(Stream.filterMap(({
38
+ message
39
+ }) => {
40
+ if (predicates.newClientPredicate(message)) {
41
+ const stripPrefix = String.replace(constants.nodeRpcClientConnectionRequestMessagePrefix, "");
42
+ return Option.some(stripPrefix(message));
43
+ } else {
44
+ return Option.none();
45
+ }
46
+ }), Stream.runForEach(exportName => {
47
+ const id = clientId++;
48
+ clientIds.add(id);
49
+ return script.callExport(exportName, Schema.Void)(clientId++);
50
+ }), Stream.tapError(Console.error), Stream.runDrain, Effect.forkScoped);
51
+ // Listen for messages from connected clients
52
+ yield* script.stream.pipe(Stream.filterMap(({
53
+ data: maybeData,
54
+ message
55
+ }) => {
56
+ if (predicates.isTaggedForAnyClient(message)) {
57
+ return Option.map(maybeData, data => Tuple.make(message.clientId, data));
58
+ } else {
59
+ return Option.none();
60
+ }
61
+ }), Stream.runForEach(([clientId, data]) => {
62
+ try {
63
+ const decoded = parser.decode(data);
64
+ if (decoded.length === 0) return Effect.void;
65
+ let i = 0;
66
+ return Effect.whileLoop({
67
+ while: () => i < decoded.length,
68
+ body() {
69
+ const message = decoded[i++];
70
+ return writeRequest(clientId, message);
71
+ },
72
+ step: Function.constVoid
73
+ });
74
+ } catch (cause) {
75
+ return Effect.sync(() => {
76
+ const encoded = parser.encode(RpcMessage.ResponseDefectEncoded(cause));
77
+ const transformed = typeof encoded === "string" ? encoder.encode(encoded) : encoded;
78
+ script.script.post({
79
+ clientId
80
+ }, Buffer.from(transformed));
81
+ });
82
+ }
83
+ }), Stream.tapError(Console.error), Stream.runDrain, Effect.forkScoped);
84
+ return yield* RpcServer.Protocol.make(_writeRequest => {
85
+ writeRequest = _writeRequest;
86
+ return Effect.succeed({
87
+ disconnects,
88
+ send: (clientId, response) => {
89
+ const writeRaw = data => {
90
+ const transformed = typeof data === "string" ? encoder.encode(data) : data;
91
+ script.script.post({
92
+ clientId
93
+ }, Buffer.from(transformed));
94
+ return Effect.void;
95
+ };
96
+ try {
97
+ const encoded = parser.encode(response);
98
+ if (Predicate.isNotUndefined(encoded)) return writeRaw(encoded);else return Effect.void;
99
+ } catch (cause) {
100
+ const encoded = parser.encode(RpcMessage.ResponseDefectEncoded(cause));
101
+ return writeRaw(encoded);
102
+ }
103
+ },
104
+ end(_clientId) {
105
+ return Effect.void;
106
+ },
107
+ clientIds: Effect.sync(() => clientIds),
108
+ initialMessage: Effect.succeedNone,
109
+ supportsAck: true,
110
+ supportsTransferables: false,
111
+ supportsSpanPropagation: true
112
+ });
113
+ });
114
+ });
115
+ //# sourceMappingURL=FridaRpcServer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FridaRpcServer.js","names":["RpcMessage","RpcSerialization","RpcServer","FridaScript","Console","Effect","Function","Mailbox","Option","Predicate","Schema","Stream","String","Tuple","constants","predicates","makeProtocolFrida","gen","encoder","TextEncoder","script","serialization","clientId","clientIds","Set","parser","unsafeMake","disconnects","make","writeRequest","stream","pipe","filterMap","message","newClientPredicate","stripPrefix","replace","nodeRpcClientConnectionRequestMessagePrefix","some","none","runForEach","exportName","id","add","callExport","Void","tapError","error","runDrain","forkScoped","data","maybeData","isTaggedForAnyClient","map","decoded","decode","length","void","i","whileLoop","while","body","step","constVoid","cause","sync","encoded","encode","ResponseDefectEncoded","transformed","post","Buffer","from","Protocol","_writeRequest","succeed","send","response","writeRaw","isNotUndefined","end","_clientId","initialMessage","succeedNone","supportsAck","supportsTransferables","supportsSpanPropagation"],"sources":["../../src/node/FridaRpcServer.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+ /**
5
+ * Implements a Frida RPC client protocol for effect using the frida script
6
+ * exports.
7
+ *
8
+ * @since 1.0.0
9
+ */
10
+ export * as FridaRpcClient from "./FridaRpcClient.ts";
11
+ /**
12
+ * Implements a Frida RPC server protocol for effect using the frida script
13
+ * exports.
14
+ *
15
+ * @since 1.0.0
16
+ */
17
+ export * as FridaRpcServer from "./FridaRpcServer.ts";
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/node/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;GAKG;AACH,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAA;AAErD;;;;;GAKG;AACH,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAA"}
@@ -1,17 +1,18 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
1
4
  /**
2
5
  * Implements a Frida RPC client protocol for effect using the frida script
3
- * exports. The reason we don't use the send/recv script channels is because
4
- * those are shared channels by everybody.
6
+ * exports.
5
7
  *
6
8
  * @since 1.0.0
7
9
  */
8
10
  export * as FridaRpcClient from "./FridaRpcClient.js";
9
11
  /**
10
12
  * Implements a Frida RPC server protocol for effect using the frida script
11
- * exports. The reason we don't use the send/recv script channels is because
12
- * those are shared channels by everybody.
13
+ * exports.
13
14
  *
14
15
  * @since 1.0.0
15
16
  */
16
17
  export * as FridaRpcServer from "./FridaRpcServer.js";
17
- //# sourceMappingURL=index.d.ts.map
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["FridaRpcClient","FridaRpcServer"],"sources":["../../src/node/index.ts"],"sourcesContent":[null],"mappings":"AAAA;;;AAIA;;;;;;AAMA,OAAO,KAAKA,cAAc,MAAM,qBAAqB;AAErD;;;;;;AAMA,OAAO,KAAKC,cAAc,MAAM,qBAAqB","ignoreList":[]}
@@ -0,0 +1,6 @@
1
+ export declare const defaultServerMainExportName = "rpc";
2
+ export declare const generateServerExportNameForClient: (clientId: number) => string;
3
+ export declare const generateClientCallbackExportNameForServer: () => string;
4
+ export declare const nodeRpcClientConnectionRequestMessagePrefix = "client id request message";
5
+ export declare const nodeRpcClientMakeConnectionRequestForServer: (exportName: string) => string;
6
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/shared/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,2BAA2B,QAAQ,CAAC;AACjD,eAAO,MAAM,iCAAiC,GAAI,UAAU,MAAM,KAAG,MACN,CAAC;AAGhE,eAAO,MAAM,yCAAyC,QAAO,MACY,CAAC;AAE1E,eAAO,MAAM,2CAA2C,8BAA8B,CAAC;AACvF,eAAO,MAAM,2CAA2C,GAAI,YAAY,MAAM,WACZ,CAAC"}
@@ -0,0 +1,7 @@
1
+ export const defaultServerMainExportName = "rpc";
2
+ export const generateServerExportNameForClient = clientId => `@efffrida/rpc/FridaServerRpcListenerForClient/${clientId}`;
3
+ let exportIdForClientCallback = 0;
4
+ export const generateClientCallbackExportNameForServer = () => `@efffrida/rpc/FridaClientRpcCallback/${exportIdForClientCallback++}`;
5
+ export const nodeRpcClientConnectionRequestMessagePrefix = "client id request message";
6
+ export const nodeRpcClientMakeConnectionRequestForServer = exportName => `${nodeRpcClientConnectionRequestMessagePrefix}:${exportName}`;
7
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","names":["defaultServerMainExportName","generateServerExportNameForClient","clientId","exportIdForClientCallback","generateClientCallbackExportNameForServer","nodeRpcClientConnectionRequestMessagePrefix","nodeRpcClientMakeConnectionRequestForServer","exportName"],"sources":["../../src/shared/constants.ts"],"sourcesContent":[null],"mappings":"AAAA,OAAO,MAAMA,2BAA2B,GAAG,KAAK;AAChD,OAAO,MAAMC,iCAAiC,GAAIC,QAAgB,IAC9D,iDAAiDA,QAAQ,EAAE;AAE/D,IAAIC,yBAAyB,GAAW,CAAC;AACzC,OAAO,MAAMC,yCAAyC,GAAGA,CAAA,KACrD,wCAAwCD,yBAAyB,EAAE,EAAE;AAEzE,OAAO,MAAME,2CAA2C,GAAG,2BAA2B;AACtF,OAAO,MAAMC,2CAA2C,GAAIC,UAAkB,IAC1E,GAAGF,2CAA2C,IAAIE,UAAU,EAAE","ignoreList":[]}
@@ -0,0 +1,10 @@
1
+ import * as Predicate from "effect/Predicate";
2
+ export declare const isClientId: (clientId: number) => Predicate.Refinement<unknown, number>;
3
+ export declare const isTaggedForClient: (clientId: number) => Predicate.Refinement<unknown, {
4
+ readonly clientId: number;
5
+ }>;
6
+ export declare const isTaggedForAnyClient: Predicate.Refinement<unknown, {
7
+ readonly clientId: number;
8
+ }>;
9
+ export declare const newClientPredicate: Predicate.Refinement<unknown, string>;
10
+ //# sourceMappingURL=predicates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"predicates.d.ts","sourceRoot":"","sources":["../../src/shared/predicates.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,kBAAkB,CAAC;AAK9C,eAAO,MAAM,UAAU,GAAI,UAAU,MAAM,0CAAmE,CAAC;AAE/G,eAAO,MAAM,iBAAiB,GAAI,UAAU,MAAM;;EAC8C,CAAC;AAEjG,eAAO,MAAM,oBAAoB;;EAGhC,CAAC;AAEF,eAAO,MAAM,kBAAkB,uCAG9B,CAAC"}
@@ -0,0 +1,12 @@
1
+ import * as Predicate from "effect/Predicate";
2
+ import * as String from "effect/String";
3
+ import * as constants from "./constants.js";
4
+ export const isClientId = clientId => Predicate.compose(Predicate.isNumber, id => id === clientId);
5
+ export const isTaggedForClient = clientId => Predicate.compose(Predicate.isUnknown, Predicate.struct({
6
+ clientId: isClientId(clientId)
7
+ }));
8
+ export const isTaggedForAnyClient = /*#__PURE__*/Predicate.compose(Predicate.isUnknown, /*#__PURE__*/Predicate.struct({
9
+ clientId: Predicate.isNumber
10
+ }));
11
+ export const newClientPredicate = /*#__PURE__*/Predicate.compose(Predicate.isString, /*#__PURE__*/String.startsWith(constants.nodeRpcClientConnectionRequestMessagePrefix));
12
+ //# sourceMappingURL=predicates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"predicates.js","names":["Predicate","String","constants","isClientId","clientId","compose","isNumber","id","isTaggedForClient","isUnknown","struct","isTaggedForAnyClient","newClientPredicate","isString","startsWith","nodeRpcClientConnectionRequestMessagePrefix"],"sources":["../../src/shared/predicates.ts"],"sourcesContent":[null],"mappings":"AAAA,OAAO,KAAKA,SAAS,MAAM,kBAAkB;AAC7C,OAAO,KAAKC,MAAM,MAAM,eAAe;AAEvC,OAAO,KAAKC,SAAS,MAAM,gBAAgB;AAE3C,OAAO,MAAMC,UAAU,GAAIC,QAAgB,IAAKJ,SAAS,CAACK,OAAO,CAACL,SAAS,CAACM,QAAQ,EAAGC,EAAE,IAAKA,EAAE,KAAKH,QAAQ,CAAC;AAE9G,OAAO,MAAMI,iBAAiB,GAAIJ,QAAgB,IAC9CJ,SAAS,CAACK,OAAO,CAACL,SAAS,CAACS,SAAS,EAAET,SAAS,CAACU,MAAM,CAAC;EAAEN,QAAQ,EAAED,UAAU,CAACC,QAAQ;AAAC,CAAE,CAAC,CAAC;AAEhG,OAAO,MAAMO,oBAAoB,gBAAGX,SAAS,CAACK,OAAO,CACjDL,SAAS,CAACS,SAAS,eACnBT,SAAS,CAACU,MAAM,CAAC;EAAEN,QAAQ,EAAEJ,SAAS,CAACM;AAAQ,CAAE,CAAC,CACrD;AAED,OAAO,MAAMM,kBAAkB,gBAAGZ,SAAS,CAACK,OAAO,CAC/CL,SAAS,CAACa,QAAQ,eAClBZ,MAAM,CAACa,UAAU,CAACZ,SAAS,CAACa,2CAA2C,CAAC,CAC3E","ignoreList":[]}
package/package.json CHANGED
@@ -1,61 +1,75 @@
1
1
  {
2
2
  "name": "@efffrida/rpc",
3
- "version": "0.0.14",
3
+ "version": "0.0.15",
4
4
  "description": "effect rpc-frida",
5
- "license": "GPL-3.0-only",
5
+ "keywords": [
6
+ "frida.re",
7
+ "reverse engineering",
8
+ "effect-ts"
9
+ ],
10
+ "homepage": "https://github.com/leonitousconforti/efffrida",
6
11
  "repository": {
7
12
  "type": "git",
8
13
  "url": "https://github.com/leonitousconforti/efffrida.git",
9
14
  "directory": "packages/rpc"
10
15
  },
11
- "sideEffects": [],
16
+ "license": "GPL-3.0-only",
12
17
  "author": "Leo Conforti <leo@leoconforti.us> (https://leoconforti.us)",
13
- "homepage": "https://github.com/leonitousconforti/efffrida",
18
+ "sideEffects": [],
19
+ "type": "module",
20
+ "exports": {
21
+ "./package.json": "./package.json",
22
+ "./node": "./dist/node/index.ts",
23
+ "./frida": "./dist/frida/index.ts",
24
+ "./*": "./dist/*.ts",
25
+ "./internal/*": null,
26
+ "./shared/*": null,
27
+ "./*/index": null
28
+ },
29
+ "files": [
30
+ "src/**/*.ts",
31
+ "dist/**/*.js",
32
+ "dist/**/*.js.map",
33
+ "dist/**/*.d.ts",
34
+ "dist/**/*.d.ts.map"
35
+ ],
14
36
  "dependencies": {
15
- "@efffrida/frida-tools": "^0.0.14"
37
+ "@efffrida/frida-tools": "^0.0.15"
38
+ },
39
+ "devDependencies": {
40
+ "@effect/cluster": "0.52.1",
41
+ "@effect/experimental": "0.57.0",
42
+ "@effect/platform": "0.93.0",
43
+ "@effect/platform-node": "0.100.0",
44
+ "@effect/rpc": "0.72.1",
45
+ "@effect/sql": "0.48.0",
46
+ "@effect/vitest": "0.27.0",
47
+ "@effect/workflow": "0.12.1",
48
+ "@types/frida-gum": "19.0.1",
49
+ "@types/node": "24.10.0",
50
+ "core-js": "3.46.0",
51
+ "effect": "3.19.0",
52
+ "vitest": "4.0.7",
53
+ "@efffrida/platform": "0.0.13"
16
54
  },
17
55
  "peerDependencies": {
18
- "@effect/platform": "0.81.1",
19
- "@effect/rpc": "0.57.1",
20
- "effect": "3.14.22"
56
+ "@effect/platform": "0.93.0",
57
+ "@effect/rpc": "0.72.1",
58
+ "effect": "3.19.0"
21
59
  },
22
- "main": "./dist/cjs/index.js",
23
- "module": "./dist/esm/index.js",
24
- "types": "./dist/dts/index.d.ts",
25
- "exports": {
26
- "./package.json": "./package.json",
27
- ".": {
28
- "types": "./dist/dts/index.d.ts",
29
- "import": "./dist/esm/index.js",
30
- "default": "./dist/cjs/index.js"
31
- },
32
- "./FridaRpcClient": {
33
- "types": "./dist/dts/FridaRpcClient.d.ts",
34
- "import": "./dist/esm/FridaRpcClient.js",
35
- "default": "./dist/cjs/FridaRpcClient.js"
36
- },
37
- "./FridaRpcServer": {
38
- "types": "./dist/dts/FridaRpcServer.d.ts",
39
- "import": "./dist/esm/FridaRpcServer.js",
40
- "default": "./dist/cjs/FridaRpcServer.js"
41
- },
42
- "./index": {
43
- "types": "./dist/dts/index.d.ts",
44
- "import": "./dist/esm/index.js",
45
- "default": "./dist/cjs/index.js"
46
- }
60
+ "publishConfig": {
61
+ "access": "public"
47
62
  },
48
- "typesVersions": {
49
- "*": {
50
- "FridaRpcClient": [
51
- "./dist/dts/FridaRpcClient.d.ts"
52
- ],
53
- "FridaRpcServer": [
54
- "./dist/dts/FridaRpcServer.d.ts"
55
- ],
56
- "index": [
57
- "./dist/dts/index.d.ts"
58
- ]
59
- }
63
+ "tags": [
64
+ "frida.re",
65
+ "reverse engineering",
66
+ "effect-ts"
67
+ ],
68
+ "scripts": {
69
+ "build": "tsc -b tsconfig.build.json && babel dist --plugins annotate-pure-calls --out-dir dist --source-maps",
70
+ "check": "tsc -b tsconfig.json",
71
+ "codegen": "build-utils prepare-v4",
72
+ "coverage": "vitest --coverage",
73
+ "test": "vitest"
60
74
  }
61
75
  }
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Implements a Frida RPC client protocol for effect using the frida script
3
+ * exports.
4
+ *
5
+ * @since 1.0.0
6
+ */
7
+
8
+ import type * as RpcMessage from "@effect/rpc/RpcMessage";
9
+ import type * as Duration from "effect/Duration";
10
+ import type * as Scope from "effect/Scope";
11
+
12
+ import * as RpcClient from "@effect/rpc/RpcClient";
13
+ import * as RpcSerialization from "@effect/rpc/RpcSerialization";
14
+ import * as FridaStream from "@efffrida/platform/Stream";
15
+ import * as Cause from "effect/Cause";
16
+ import * as Deferred from "effect/Deferred";
17
+ import * as Effect from "effect/Effect";
18
+ import * as Function from "effect/Function";
19
+ import * as Layer from "effect/Layer";
20
+ import * as Option from "effect/Option";
21
+ import * as ParseResult from "effect/ParseResult";
22
+ import * as Predicate from "effect/Predicate";
23
+ import * as Schema from "effect/Schema";
24
+ import * as Stream from "effect/Stream";
25
+
26
+ import * as constants from "../shared/constants.ts";
27
+ import * as sharedPredicates from "../shared/predicates.ts";
28
+
29
+ /**
30
+ * @since 1.0.0
31
+ * @category Protocol
32
+ */
33
+ export const makeProtocolFrida = (
34
+ options?:
35
+ | {
36
+ readonly generateExportName?: (() => string) | undefined;
37
+ readonly receivingStreamShareOptions?:
38
+ | {
39
+ readonly capacity: "unbounded";
40
+ readonly replay?: number | undefined;
41
+ readonly idleTimeToLive?: Duration.DurationInput | undefined;
42
+ }
43
+ | {
44
+ readonly capacity: number;
45
+ readonly strategy?: "sliding" | "dropping" | "suspend" | undefined;
46
+ readonly replay?: number | undefined;
47
+ readonly idleTimeToLive?: Duration.DurationInput | undefined;
48
+ }
49
+ | undefined;
50
+ }
51
+ | undefined
52
+ ): Effect.Effect<RpcClient.Protocol["Type"], never, RpcSerialization.RpcSerialization | Scope.Scope> =>
53
+ RpcClient.Protocol.make(
54
+ Effect.fnUntraced(function* (writeResponse) {
55
+ const serialization = yield* RpcSerialization.RpcSerialization;
56
+
57
+ const encoder = new TextEncoder();
58
+ const parser = serialization.unsafeMake();
59
+
60
+ /**
61
+ * Once the server has acknowledged our initiation request and sent
62
+ * use a client id to identify ourselves, this deferred will
63
+ * complete.
64
+ */
65
+ const clientIdDeferred = yield* Deferred.make<number, ParseResult.ParseError>();
66
+
67
+ /**
68
+ * Setup the receiving handler for the client id. We receive the
69
+ * client id over a script export instead of over the receiving
70
+ * stream since there could be multiple clients in this frida script
71
+ * and multiple could request client ids at the same time. This
72
+ * could be mitigated with a nonce of some kind in the request
73
+ * message, but receiving over a script export is full-proof so long
74
+ * as the exports are unique.
75
+ */
76
+ const exportName = options?.generateExportName?.() ?? constants.generateClientCallbackExportNameForServer();
77
+ yield* Effect.addFinalizer(() => Effect.sync(() => delete rpc.exports[exportName]));
78
+ rpc.exports[exportName] = (maybeIncomingClientId: unknown): void => {
79
+ const parsedClientId = Schema.decodeUnknown(Schema.Number)(maybeIncomingClientId);
80
+ Deferred.unsafeDone(clientIdDeferred, parsedClientId);
81
+ };
82
+
83
+ /**
84
+ * Request a client id from the node side and wait for the response,
85
+ * handling any transient errors that might occur by retrying with
86
+ * the retry policy.
87
+ */
88
+ const connectionRequest = constants.nodeRpcClientMakeConnectionRequestForServer(exportName);
89
+ const connectionRequestTimeout: Duration.DurationInput = "1 second";
90
+ const clientId = yield* Effect.sync(() => send(connectionRequest)).pipe(
91
+ Effect.flatMap(() => Deferred.await(clientIdDeferred)),
92
+ Effect.timeout(connectionRequestTimeout),
93
+ Effect.catchIf(ParseResult.isParseError, () => Effect.dieMessage("Failed to parse client ID")),
94
+ Effect.catchIf(Cause.isTimeoutException, () => Effect.dieMessage("Timed out too many times"))
95
+ );
96
+
97
+ /**
98
+ * Attach to the receiving stream, where all future messages will
99
+ * come in at, and filter for only our messages.
100
+ */
101
+ const receivingPredicate = sharedPredicates.isTaggedForClient(clientId);
102
+ const receiveStream = yield* FridaStream.receiveStream(
103
+ options?.receivingStreamShareOptions ?? {
104
+ replay: 100,
105
+ capacity: "unbounded",
106
+ }
107
+ );
108
+
109
+ /**
110
+ * For every response message received, decode it and send it the
111
+ * response back to the implementation.
112
+ */
113
+ yield* receiveStream.pipe(
114
+ Stream.filterMap((unfiltered) => {
115
+ if (receivingPredicate(unfiltered.message)) return Option.fromNullable(unfiltered.data);
116
+ else return Option.none<Uint8Array<ArrayBufferLike>>();
117
+ }),
118
+ Stream.runForEach((filtered) => {
119
+ try {
120
+ const responses = parser.decode(filtered) as Array<RpcMessage.FromServerEncoded>;
121
+ if (responses.length === 0) return Effect.void;
122
+ let i = 0;
123
+ return Effect.whileLoop({
124
+ while: () => i < responses.length,
125
+ body: () => writeResponse(responses[i++]),
126
+ step: Function.constVoid,
127
+ });
128
+ } catch (defect) {
129
+ return writeResponse({ _tag: "Defect", defect });
130
+ }
131
+ }),
132
+ Effect.interruptible,
133
+ Effect.forkScoped
134
+ );
135
+
136
+ /**
137
+ * Sending messages is as simple as encoding them, then sending them
138
+ * to the node side tagged with our client id so it knows where to
139
+ * send them back to.
140
+ */
141
+ const sendHelper = Effect.fnUntraced(function* (message: RpcMessage.FromClientEncoded) {
142
+ const encoded = parser.encode(message);
143
+ if (Predicate.isUndefined(encoded)) return;
144
+ const transformed = typeof encoded === "string" ? encoder.encode(encoded) : encoded;
145
+ send({ clientId }, (transformed as Uint8Array<ArrayBuffer>).buffer);
146
+ });
147
+
148
+ return {
149
+ send: sendHelper,
150
+ supportsAck: true,
151
+ supportsTransferables: false,
152
+ };
153
+ })
154
+ );
155
+
156
+ /**
157
+ * @since 1.0.0
158
+ * @category Layers
159
+ */
160
+ export const layerProtocolFrida = (
161
+ options?:
162
+ | {
163
+ readonly generateExportName?: (() => string) | undefined;
164
+ readonly receivingStreamShareOptions?:
165
+ | {
166
+ readonly capacity: "unbounded";
167
+ readonly replay?: number | undefined;
168
+ readonly idleTimeToLive?: Duration.DurationInput | undefined;
169
+ }
170
+ | {
171
+ readonly capacity: number;
172
+ readonly strategy?: "sliding" | "dropping" | "suspend" | undefined;
173
+ readonly replay?: number | undefined;
174
+ readonly idleTimeToLive?: Duration.DurationInput | undefined;
175
+ }
176
+ | undefined;
177
+ }
178
+ | undefined
179
+ ): Layer.Layer<RpcClient.Protocol, never, RpcSerialization.RpcSerialization> =>
180
+ Layer.scoped(RpcClient.Protocol, makeProtocolFrida(options));