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 +117 -49
- package/dist/index.js +37 -13
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,99 +1,167 @@
|
|
|
1
1
|
# Expose Kit
|
|
2
2
|

|
|
3
|
-
[](https://discord.gg/evex)
|
|
3
|
+
[](https://discord.gg/evex)
|
|
4
4
|
|
|
5
|
-
> A universal toolkit for
|
|
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
|
-
##
|
|
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
|
-
|
|
11
|
+
JavaScript deobfuscation tools are everywhere.
|
|
12
|
+
But many of them are **too aggressive**, rewriting code until it breaks.
|
|
21
13
|
|
|
22
|
-
|
|
14
|
+
Expose Kit takes a **different approach**.
|
|
23
15
|
|
|
24
|
-
|
|
16
|
+
- No brute force
|
|
17
|
+
- Step-by-step, verifiable transforms
|
|
18
|
+
- Designed to *not* break your code silently
|
|
25
19
|
|
|
26
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
44
|
-
```regex
|
|
36
|
+
```bash
|
|
45
37
|
expose --help
|
|
46
38
|
expose parsable sample.js
|
|
47
39
|
```
|
|
48
40
|
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
65
|
+
Because of this, you should **verify the code at every step**.
|
|
53
66
|
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
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
|
-
|
|
74
|
-
-
|
|
134
|
+
Args:
|
|
135
|
+
- Default args only
|
|
75
136
|
|
|
76
137
|
---
|
|
77
|
-
#### `expose scope-safe`
|
|
78
138
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
|
144
|
+
expose safe-scope path/to/file.js --output path/to/file.safe-scope.js
|
|
88
145
|
```
|
|
89
146
|
|
|
90
|
-
|
|
91
|
-
- `--o, --output <file
|
|
92
|
-
|
|
93
|
-
|
|
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
|
|
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 ?
|
|
122
|
+
options.unlimited ? null : 30 * 1e3
|
|
122
123
|
);
|
|
123
124
|
}
|
|
124
125
|
);
|
|
125
126
|
});
|
|
126
127
|
|
|
127
|
-
// commands/scope
|
|
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
|
-
//
|
|
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
|
|
162
|
+
return `${inputPath}.safe-scope.js`;
|
|
145
163
|
}
|
|
146
164
|
const base = basename(inputPath, ext);
|
|
147
|
-
return join(dirname(inputPath), `${base}.scope
|
|
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
|
|
174
|
-
program2.command("scope
|
|
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
|
-
|
|
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(
|
|
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
|
|
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 ?
|
|
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,
|
|
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