expose-kit 0.2.6 → 0.2.8

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 CHANGED
@@ -1,99 +1,167 @@
1
1
  # Expose Kit
2
2
  ![release workflow](https://github.com/evex-dev/linejs/actions/workflows/release.yml/badge.svg)
3
- [![](https://dcbadge.limes.pink/api/server/evex)](https://discord.gg/evex)
3
+ [![](https://dcbadge.limes.pink/api/server/evex)](https://discord.gg/evex)
4
4
 
5
- > A universal toolkit for deobfuscating JavaScript
6
- ---
5
+ > A universal toolkit for JavaScript deobfuscation
7
6
 
8
- ##### <center>❓ Question: Join our [Discord community](https://evex.land)</center>
9
7
  ---
10
8
 
11
- ## Concept
12
- JavaScript deobfuscation tools are *everywhere*.
13
- <img width="145.2" height="113.5" alt="image" src="https://github.com/relative/synchrony/blob/master/.github/hm.png?raw=true" />
14
-
15
-
16
- But many of them are **too aggressive**, rewriting code until it breaks.
17
-
18
- <img width="654" height="24" alt="image" src="https://github.com/user-attachments/assets/fd11d250-0163-4cd2-b36c-5514137fe087" />
9
+ ## What is this?
19
10
 
20
- Expose Kit takes *a different path*.
11
+ JavaScript deobfuscation tools are everywhere.
12
+ But many of them are **too aggressive**, rewriting code until it breaks.
21
13
 
22
- Instead of brute force, it works **step by step**.
14
+ Expose Kit takes a **different approach**.
23
15
 
24
- Alongside deobfuscation, Expose Kit includes a collection of practical utilities.
16
+ - No brute force
17
+ - Step-by-step, verifiable transforms
18
+ - Designed to *not* break your code silently
25
19
 
26
- Everything you need is documented right here in this [README](README.md).
20
+ Each transformation is meant to be **checked and validated**, so you always know *when* something goes wrong.
27
21
 
28
- ---
22
+ Alongside deobfuscation, Expose Kit also provides a set of **practical utilities** for working with obfuscated JavaScript.
29
23
 
30
- ##### If the feature you’re looking for doesn’t exist, please create an [issue](https://github.com/EdamAme-x/expose-kit/issues).
31
- ##### If you know what you want to do but aren’t sure which feature to use, join our [Discord community](https://evex.land) and ask for help.
32
24
  ---
33
25
 
34
26
  ## Installation
35
- *Just one step*
36
- <!-- For Highlight -->
37
- ```regex
27
+
28
+ Just one step:
29
+
30
+ ```bash
38
31
  npm i -g expose-kit
39
32
  # or
40
33
  bun i -g expose-kit
41
34
  ```
42
35
 
43
- <!-- For Highlight -->
44
- ```regex
36
+ ```bash
45
37
  expose --help
46
38
  expose parsable sample.js
47
39
  ```
48
40
 
49
- ## Docs
50
- By default, the first argument should be the file name (alternatively, `--file` or `--input` can be used).
41
+ ---
42
+
43
+ ## Usage Notes
44
+
45
+ ### Default arguments
46
+
47
+ - The first argument is the input file
48
+ (`--file` / `--input` can also be used)
49
+ - If required options are missing, Expose Kit will **prompt you**
50
+ - A timeout is enabled by default to avoid hangs
51
+ Use `--unlimited` for long-running execution
52
+
53
+ ---
54
+
55
+ ## Recommended Workflow
56
+
57
+ First, an important premise:
58
+
59
+ > It is **impossible** to create a static deobfuscation tool that *never* breaks.
60
+
61
+ Reasons include:
62
+ - Unpredictable execution (`eval`, dynamic code)
63
+ - Bugs or edge cases in AST manipulation
51
64
 
52
- If no options are provided, this tool will prompt you for the required values.
65
+ Because of this, you should **verify the code at every step**.
53
66
 
54
- To avoid memory leaks and hung processes, a reasonable timeout is set by default.
55
- When long-running execution is expected, the timeout can be disabled with `--unlimited`.
67
+ ### 1. Always verify with `parsable`
68
+
69
+ After each transformation, run:
70
+
71
+ ```bash
72
+ expose parsable file.js
73
+ ```
74
+
75
+ This ensures the syntax is still valid.
56
76
 
57
- ### Commands
58
77
  ---
59
78
 
60
- #### `expose parsable`
79
+ ### 2. Make scopes safe first
80
+
81
+ One of the most common causes of breakage is **variable name confusion**.
82
+
83
+ If you try to write your own deobfuscation logic (e.g. in Python), you’ll quickly realize how painful it is to track scopes correctly.
84
+
85
+ That’s why you should **always start with**:
86
+
87
+ ```bash
88
+ expose safe-scope input.js
89
+ ```
90
+
91
+ This renames bindings per scope, producing code like:
92
+
93
+ ```js
94
+ Before: var x = 810;((x) => console.log(x))(114514);
95
+ After: var x = 810;((_x) => console.log(_x))(114514);
96
+ ```
97
+
98
+ With this alone:
99
+ - The code becomes far more resistant to breakage
100
+ - Writing custom deobfuscation logic becomes much easier
101
+ - You no longer need to worry about scope collisions
102
+
103
+ ---
104
+
105
+ ### 3. Apply transforms step by step
106
+
107
+ After `safe-scope`, combine common techniques like:
108
+ - `expand-array` and more
109
+ - legacy obfuscator-specific commands
110
+
111
+ After **each step**, run `parsable` again.
112
+
113
+ Expose Kit will also clearly indicate whether a **diff** exists, making inspection easy.
114
+
115
+ Repeat this process, and the original code will gradually reveal itself.
116
+
117
+ ---
118
+
119
+ ## Commands
120
+
121
+ ### `expose parsable`
122
+
123
+ Check whether a file is syntactically valid.
61
124
 
62
- Check if the file is parsable
63
125
  ```js
64
126
  parsable: const x = 810;
65
127
  not parsable: cons x; = 810;
66
128
  ```
67
129
 
68
- ##### Example
69
130
  ```bash
70
131
  expose parsable path/to/file.js
71
132
  ```
72
133
 
73
- ##### Args
74
- - *Only default args*
134
+ Args:
135
+ - Default args only
75
136
 
76
137
  ---
77
- #### `expose scope-safe`
78
138
 
79
- Rename bindings per scope for safer transforms
80
- ```js
81
- Before: var x = 810;((x) => console.log(x))(114514);
82
- After: var x = 810;((_x) => console.log(_x))(114514);
83
- ```
139
+ ### `expose safe-scope`
140
+
141
+ Rename bindings per scope for safer transformations.
84
142
 
85
- ##### Example
86
143
  ```bash
87
- expose scope-safe path/to/file.js --output path/to/file.scope-safe.js
144
+ expose safe-scope path/to/file.js --output path/to/file.safe-scope.js
88
145
  ```
89
146
 
90
- ##### Args
91
- - `--o, --output <file>`: Output file path
92
- If the input has no extension, `path/to/file.scope-safe.js` is used.
93
- Otherwise, `path/to/file.scope-safe.<ext>` is used (same directory).
147
+ Args:
148
+ - `--o, --output <file>`
149
+ Output file path
150
+ - No extension `file.safe-scope.js`
151
+ - With extension → `file.safe-scope.<ext>`
152
+
153
+ ---
154
+
155
+ ## Community & Support
156
+
157
+ - Missing a feature? → [Create an issue](https://github.com/EdamAme-x/expose-kit/issues)
158
+ - Not sure which command to use? → Join our [Discord](https://evex.land)
159
+
160
+ ---
161
+
162
+ ## Author
94
163
 
95
- ## Authors
96
164
  - [EdamAme-x](https://github.com/EdamAme-x)
97
165
 
98
166
  Built for research, not abuse.
99
- Want stronger obfuscation? Then make something this tool can’t reverse.
167
+ Want stronger obfuscation? Then build something this tool can’t reverse.
package/dist/index.js CHANGED
@@ -59,13 +59,14 @@ var createParseOptions = (filename) => {
59
59
  };
60
60
 
61
61
  // utils/common/timeout.ts
62
+ var MAX_TIMEOUT = 2147483647;
62
63
  var timeout = (fn, ms) => {
63
64
  let aborted = false;
64
65
  const { resolve, reject, promise } = Promise.withResolvers();
65
66
  const timer = setTimeout(() => {
66
67
  aborted = true;
67
68
  reject(new Error("Hang detected, please report to the developer"));
68
- }, ms);
69
+ }, ms ?? MAX_TIMEOUT);
69
70
  const finish = () => {
70
71
  clearTimeout(timer);
71
72
  resolve();
@@ -118,13 +119,13 @@ var parsable_default = createCommand((program2) => {
118
119
  return finish();
119
120
  }
120
121
  },
121
- options.unlimited ? Infinity : 30 * 1e3
122
+ options.unlimited ? null : 30 * 1e3
122
123
  );
123
124
  }
124
125
  );
125
126
  });
126
127
 
127
- // commands/scope-safe/index.ts
128
+ // commands/safe-scope/index.ts
128
129
  import { readFileSync as readFileSync2, writeFileSync } from "fs";
129
130
  import { basename, dirname, extname, join } from "path";
130
131
  import { parse as parse2 } from "@babel/parser";
@@ -134,17 +135,34 @@ import loading2 from "loading-cli";
134
135
 
135
136
  // utils/babel/patchDefault.ts
136
137
  var patchDefault = (babelFn) => {
138
+ if (typeof babelFn === "function") {
139
+ return babelFn;
140
+ }
137
141
  return babelFn.default;
138
142
  };
139
143
 
140
- // commands/scope-safe/index.ts
144
+ // utils/common/diff.ts
145
+ function diff(before, after) {
146
+ const beforeLines = before.split(/\r?\n/);
147
+ const afterLines = after.split(/\r?\n/);
148
+ const changed = [];
149
+ const max = Math.max(beforeLines.length, afterLines.length);
150
+ for (let i = 0; i < max; i++) {
151
+ if (beforeLines[i] !== afterLines[i]) {
152
+ changed.push(i + 1);
153
+ }
154
+ }
155
+ return changed;
156
+ }
157
+
158
+ // commands/safe-scope/index.ts
141
159
  var createDefaultOutputPath = (inputPath) => {
142
160
  const ext = extname(inputPath);
143
161
  if (!ext) {
144
- return `${inputPath}.scope-safe.js`;
162
+ return `${inputPath}.safe-scope.js`;
145
163
  }
146
164
  const base = basename(inputPath, ext);
147
- return join(dirname(inputPath), `${base}.scope-safe${ext}`);
165
+ return join(dirname(inputPath), `${base}.safe-scope${ext}`);
148
166
  };
149
167
  var renameBindingsByScope = (code, filename) => {
150
168
  const ast = parse2(code, createParseOptions(filename));
@@ -170,8 +188,8 @@ var renameBindingsByScope = (code, filename) => {
170
188
  });
171
189
  return patchDefault(generate)(ast).code;
172
190
  };
173
- var scope_safe_default = createCommand((program2) => {
174
- program2.command("scope-safe").description("Rename bindings per scope for safer transforms").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
191
+ var safe_scope_default = createCommand((program2) => {
192
+ program2.command("safe-scope").description("Rename bindings per scope for safer transforms").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
175
193
  async (fileArgument, options) => {
176
194
  await timeout(
177
195
  async ({ finish }) => {
@@ -183,15 +201,21 @@ var scope_safe_default = createCommand((program2) => {
183
201
  try {
184
202
  const fileContent = readFileSync2(filename, "utf8");
185
203
  const defaultOutputPath = createDefaultOutputPath(filename);
186
- const outputPath = options.output ?? (await createPrompt("Enter the output file path:") || "").trim() ?? defaultOutputPath;
204
+ let outputPath = options.output;
205
+ if (!outputPath) {
206
+ const promptPath = (await createPrompt("Enter the output file path:"))?.trim();
207
+ outputPath = promptPath || defaultOutputPath;
208
+ }
187
209
  const loader = loading2("Renaming variables by scope...").start();
188
210
  try {
189
211
  const output = renameBindingsByScope(fileContent, filename);
190
212
  writeFileSync(outputPath, output, "utf8");
191
- loader.succeed(`Saved scope-safe file to: ${outputPath}`);
213
+ loader.succeed(
214
+ `Saved safe-scope file to: ${outputPath} (${diff(fileContent, output).length} lines changed)`
215
+ );
192
216
  return finish();
193
217
  } catch (error) {
194
- loader.fail("Failed to apply scope-safe transform");
218
+ loader.fail("Failed to apply safe-scope transform");
195
219
  showError(
196
220
  `Error transforming file '${filename}': ${error instanceof Error ? error.message : "Unknown error"}`
197
221
  );
@@ -204,7 +228,7 @@ var scope_safe_default = createCommand((program2) => {
204
228
  return finish();
205
229
  }
206
230
  },
207
- options.unlimited ? Infinity : 120 * 1e3
231
+ options.unlimited ? null : 120 * 1e3
208
232
  );
209
233
  }
210
234
  );
@@ -273,7 +297,7 @@ program.name("expose").description("CLI for Deobfuscating").version(
273
297
  "-v, --version",
274
298
  "display version number"
275
299
  );
276
- var commands = [parsable_default, scope_safe_default];
300
+ var commands = [parsable_default, safe_scope_default];
277
301
  for (const command of commands) {
278
302
  command(program);
279
303
  }
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expose-kit",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "author": "EdamAmex <edame8080@gmail.com> (https://github.com/EdamAme-x)",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expose-kit",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "author": "EdamAmex <edame8080@gmail.com> (https://github.com/EdamAme-x)",