js-confuser 1.6.0 → 1.7.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.
- package/CHANGELOG.md +42 -0
- package/README.md +215 -172
- package/dist/constants.js +6 -2
- package/dist/obfuscator.js +0 -6
- package/dist/options.js +4 -4
- package/dist/presets.js +6 -7
- package/dist/templates/crash.js +2 -2
- package/dist/templates/functionLength.js +16 -0
- package/dist/transforms/dispatcher.js +10 -1
- package/dist/transforms/extraction/duplicateLiteralsRemoval.js +95 -59
- package/dist/transforms/extraction/objectExtraction.js +6 -1
- package/dist/transforms/flatten.js +224 -147
- package/dist/transforms/identifier/movedDeclarations.js +38 -85
- package/dist/transforms/identifier/renameVariables.js +94 -41
- package/dist/transforms/lock/lock.js +0 -37
- package/dist/transforms/minify.js +2 -2
- package/dist/transforms/rgf.js +145 -244
- package/dist/transforms/stack.js +42 -1
- package/dist/transforms/transform.js +1 -1
- package/dist/util/gen.js +2 -1
- package/dist/util/identifiers.js +38 -4
- package/dist/util/insert.js +24 -3
- package/docs/ControlFlowFlattening.md +595 -0
- package/{Countermeasures.md → docs/Countermeasures.md} +1 -15
- package/docs/ES5.md +197 -0
- package/{Integrity.md → docs/Integrity.md} +2 -2
- package/docs/RGF.md +419 -0
- package/package.json +2 -2
- package/src/constants.ts +3 -0
- package/src/obfuscator.ts +0 -4
- package/src/options.ts +9 -86
- package/src/presets.ts +6 -7
- package/src/templates/crash.ts +10 -10
- package/src/templates/functionLength.ts +14 -0
- package/src/transforms/dispatcher.ts +15 -1
- package/src/transforms/extraction/duplicateLiteralsRemoval.ts +135 -130
- package/src/transforms/extraction/objectExtraction.ts +4 -0
- package/src/transforms/flatten.ts +357 -290
- package/src/transforms/identifier/movedDeclarations.ts +50 -96
- package/src/transforms/identifier/renameVariables.ts +120 -56
- package/src/transforms/lock/lock.ts +1 -42
- package/src/transforms/minify.ts +11 -2
- package/src/transforms/rgf.ts +221 -402
- package/src/transforms/stack.ts +62 -0
- package/src/transforms/transform.ts +6 -2
- package/src/util/gen.ts +7 -2
- package/src/util/identifiers.ts +48 -4
- package/src/util/insert.ts +26 -2
- package/test/code/ES6.src.js +24 -0
- package/test/transforms/dispatcher.test.ts +27 -0
- package/test/transforms/extraction/duplicateLiteralsRemoval.test.ts +21 -8
- package/test/transforms/extraction/objectExtraction.test.ts +35 -15
- package/test/transforms/flatten.test.ts +352 -88
- package/test/transforms/identifier/globalConcealing.test.ts +23 -2
- package/test/transforms/identifier/movedDeclarations.test.ts +37 -9
- package/test/transforms/identifier/renameVariables.test.ts +37 -0
- package/test/transforms/lock/integrity.test.ts +24 -0
- package/test/transforms/lock/lock.test.ts +1 -48
- package/test/transforms/minify.test.ts +19 -0
- package/test/transforms/rgf.test.ts +262 -353
- package/test/transforms/stack.test.ts +52 -0
- package/test/util/identifiers.test.ts +134 -1
- package/test/util/insert.test.ts +57 -3
- package/src/transforms/eval.ts +0 -89
- package/src/transforms/identifier/nameRecycling.ts +0 -280
- package/test/transforms/eval.test.ts +0 -131
- package/test/transforms/identifier/nameRecycling.test.ts +0 -205
package/src/transforms/rgf.ts
CHANGED
|
@@ -3,36 +3,24 @@ import { reservedIdentifiers } from "../constants";
|
|
|
3
3
|
import Obfuscator from "../obfuscator";
|
|
4
4
|
import { ObfuscateOrder } from "../order";
|
|
5
5
|
import { ComputeProbabilityMap } from "../probability";
|
|
6
|
-
import
|
|
7
|
-
import traverse, { walk } from "../traverse";
|
|
6
|
+
import { walk } from "../traverse";
|
|
8
7
|
import {
|
|
9
8
|
ArrayExpression,
|
|
10
|
-
|
|
9
|
+
BlockStatement,
|
|
11
10
|
CallExpression,
|
|
12
|
-
ConditionalExpression,
|
|
13
|
-
ExpressionStatement,
|
|
14
|
-
FunctionExpression,
|
|
15
11
|
Identifier,
|
|
16
12
|
Literal,
|
|
17
|
-
Location,
|
|
18
13
|
MemberExpression,
|
|
19
14
|
NewExpression,
|
|
20
15
|
Node,
|
|
21
16
|
ReturnStatement,
|
|
22
|
-
|
|
17
|
+
ThisExpression,
|
|
23
18
|
VariableDeclaration,
|
|
24
19
|
VariableDeclarator,
|
|
25
20
|
} from "../util/gen";
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
|
|
29
|
-
isVarContext,
|
|
30
|
-
isFunction,
|
|
31
|
-
prepend,
|
|
32
|
-
getDefiningContext,
|
|
33
|
-
clone,
|
|
34
|
-
} from "../util/insert";
|
|
35
|
-
import { getRandomString } from "../util/random";
|
|
21
|
+
import { getIdentifierInfo } from "../util/identifiers";
|
|
22
|
+
import { prepend, getDefiningContext } from "../util/insert";
|
|
23
|
+
import Integrity from "./lock/integrity";
|
|
36
24
|
import Transform from "./transform";
|
|
37
25
|
|
|
38
26
|
/**
|
|
@@ -43,436 +31,267 @@ import Transform from "./transform";
|
|
|
43
31
|
* Rigorous checks are in place to only include pure functions.
|
|
44
32
|
*
|
|
45
33
|
* `flatten` can attempt to make function reference-less. Recommended to have flatten enabled with RGF.
|
|
46
|
-
*
|
|
47
|
-
* | Mode | Description |
|
|
48
|
-
* | --- | --- |
|
|
49
|
-
* | `"all"` | Applies to all scopes |
|
|
50
|
-
* | `true` | Applies to the top level only |
|
|
51
|
-
* | `false` | Feature disabled |
|
|
52
34
|
*/
|
|
53
35
|
export default class RGF extends Transform {
|
|
36
|
+
// Array of all the `new Function` calls
|
|
37
|
+
arrayExpressionElements: Node[];
|
|
38
|
+
// The name of the array holding all the `new Function` expressions
|
|
39
|
+
arrayExpressionName: string;
|
|
40
|
+
|
|
54
41
|
constructor(o) {
|
|
55
42
|
super(o, ObfuscateOrder.RGF);
|
|
43
|
+
|
|
44
|
+
this.arrayExpressionName = this.getPlaceholder() + "_rgf";
|
|
45
|
+
this.arrayExpressionElements = [];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
apply(tree: Node): void {
|
|
49
|
+
super.apply(tree);
|
|
50
|
+
|
|
51
|
+
// Only add the array if there were converted functions
|
|
52
|
+
if (this.arrayExpressionElements.length > 0) {
|
|
53
|
+
prepend(
|
|
54
|
+
tree,
|
|
55
|
+
VariableDeclaration(
|
|
56
|
+
VariableDeclarator(
|
|
57
|
+
Identifier(this.arrayExpressionName),
|
|
58
|
+
ArrayExpression(this.arrayExpressionElements)
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
);
|
|
62
|
+
}
|
|
56
63
|
}
|
|
57
64
|
|
|
58
65
|
match(object, parents) {
|
|
59
|
-
return
|
|
66
|
+
return (
|
|
67
|
+
(object.type === "FunctionDeclaration" ||
|
|
68
|
+
object.type === "FunctionExpression") && // Does not apply to Arrow functions
|
|
69
|
+
!object.async && // Does not apply to async/generator functions
|
|
70
|
+
!object.generator
|
|
71
|
+
);
|
|
60
72
|
}
|
|
61
73
|
|
|
62
|
-
transform(
|
|
63
|
-
|
|
64
|
-
|
|
74
|
+
transform(object: Node, parents: Node[]) {
|
|
75
|
+
// Discard getter/setter methods
|
|
76
|
+
if (parents[0].type === "Property" && parents[0].value === object) {
|
|
77
|
+
if (
|
|
78
|
+
parents[0].method ||
|
|
79
|
+
parents[0].kind === "get" ||
|
|
80
|
+
parents[0].kind === "set"
|
|
81
|
+
) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Discard class methods
|
|
87
|
+
if (parents[0].type === "MethodDefinition" && parents[0].value === object) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Avoid applying to the countermeasures function
|
|
92
|
+
if (typeof this.options.lock?.countermeasures === "string") {
|
|
93
|
+
// function countermeasures(){...}
|
|
94
|
+
if (
|
|
95
|
+
object.type === "FunctionDeclaration" &&
|
|
96
|
+
object.id.type === "Identifier" &&
|
|
97
|
+
object.id.name === this.options.lock.countermeasures
|
|
98
|
+
) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
65
101
|
|
|
66
|
-
var
|
|
67
|
-
if (
|
|
102
|
+
// var countermeasures = function(){...}
|
|
103
|
+
if (
|
|
104
|
+
parents[0].type === "VariableDeclarator" &&
|
|
105
|
+
parents[0].init === object &&
|
|
106
|
+
parents[0].id.type === "Identifier" &&
|
|
107
|
+
parents[0].id.name === this.options.lock.countermeasures
|
|
108
|
+
) {
|
|
68
109
|
return;
|
|
69
110
|
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Check user option
|
|
114
|
+
if (!ComputeProbabilityMap(this.options.rgf, (x) => x, object?.id?.name))
|
|
115
|
+
return;
|
|
116
|
+
|
|
117
|
+
// Discard functions that use 'eval' function
|
|
118
|
+
if (object.$requiresEval) return;
|
|
119
|
+
|
|
120
|
+
// Check for 'this', 'arguments' (not allowed!)
|
|
121
|
+
var isIllegal = false;
|
|
122
|
+
walk(object, parents, (o, p) => {
|
|
123
|
+
if (
|
|
124
|
+
o.type === "ThisExpression" ||
|
|
125
|
+
o.type === "Super" ||
|
|
126
|
+
(o.type === "Identifier" && o.name === "arguments")
|
|
127
|
+
) {
|
|
128
|
+
isIllegal = true;
|
|
129
|
+
return "EXIT";
|
|
130
|
+
}
|
|
131
|
+
});
|
|
70
132
|
|
|
71
|
-
|
|
72
|
-
location: Location;
|
|
73
|
-
references: Set<string>;
|
|
74
|
-
name?: string;
|
|
75
|
-
}[] = [];
|
|
76
|
-
var queue: Location[] = [];
|
|
77
|
-
var names = new Map<string, number>();
|
|
78
|
-
var referenceSignatures: { [name: string]: string } = {};
|
|
133
|
+
if (isIllegal) return;
|
|
79
134
|
|
|
80
|
-
|
|
135
|
+
return () => {
|
|
136
|
+
// Make sure function is 'reference-less'
|
|
137
|
+
var definedMap = new Map<Node, Set<string>>();
|
|
138
|
+
var isReferenceLess = true;
|
|
139
|
+
var identifierPreventingTransformation: string;
|
|
81
140
|
|
|
82
|
-
walk(
|
|
141
|
+
walk(object, parents, (o, p) => {
|
|
83
142
|
if (
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
!
|
|
87
|
-
!object.async &&
|
|
88
|
-
!object.generator &&
|
|
89
|
-
getVarContext(parents[0], parents.slice(1)) === contextObject
|
|
143
|
+
o.type === "Identifier" &&
|
|
144
|
+
!reservedIdentifiers.has(o.name) &&
|
|
145
|
+
!this.options.globalVariables.has(o.name)
|
|
90
146
|
) {
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
if (
|
|
94
|
-
parents[0].method ||
|
|
95
|
-
parents[0].kind === "get" ||
|
|
96
|
-
parents[0].kind === "set"
|
|
97
|
-
) {
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Discard class methods
|
|
103
|
-
if (
|
|
104
|
-
parents[0].type === "MethodDefinition" &&
|
|
105
|
-
parents[0].value === object
|
|
106
|
-
) {
|
|
147
|
+
var info = getIdentifierInfo(o, p);
|
|
148
|
+
if (!info.spec.isReferenced) {
|
|
107
149
|
return;
|
|
108
150
|
}
|
|
109
151
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if (
|
|
114
|
-
object.type === "FunctionDeclaration" &&
|
|
115
|
-
object.id.type === "Identifier" &&
|
|
116
|
-
object.id.name === this.options.lock.countermeasures
|
|
117
|
-
) {
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
152
|
+
if (info.spec.isDefined) {
|
|
153
|
+
// Add to defined map
|
|
154
|
+
var definingContext = getDefiningContext(o, p);
|
|
120
155
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
parents[0].id.type === "Identifier" &&
|
|
126
|
-
parents[0].id.name === this.options.lock.countermeasures
|
|
127
|
-
) {
|
|
128
|
-
return;
|
|
156
|
+
if (!definedMap.has(definingContext)) {
|
|
157
|
+
definedMap.set(definingContext, new Set([o.name]));
|
|
158
|
+
} else {
|
|
159
|
+
definedMap.get(definingContext).add(o.name);
|
|
129
160
|
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
*
|
|
140
|
-
* - Identify all the variables referenced and defined here
|
|
141
|
-
* - Identify is the 'this' keyword is used anywhere
|
|
142
|
-
*
|
|
143
|
-
* @param o
|
|
144
|
-
* @param p
|
|
145
|
-
* @returns
|
|
146
|
-
*/
|
|
147
|
-
const fnTraverser = (o, p) => {
|
|
148
|
-
if (
|
|
149
|
-
o.type == "Identifier" &&
|
|
150
|
-
!reservedIdentifiers.has(o.name) &&
|
|
151
|
-
!this.options.globalVariables.has(o.name)
|
|
152
|
-
) {
|
|
153
|
-
var info = getIdentifierInfo(o, p);
|
|
154
|
-
if (!info.spec.isReferenced) {
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
if (info.spec.isDefined && getDefiningContext(o, p) === object) {
|
|
158
|
-
defined.add(o.name);
|
|
159
|
-
} else {
|
|
160
|
-
referenced.add(o.name);
|
|
161
|
+
} else {
|
|
162
|
+
// This approach is dirty and does not account for hoisted FunctionDeclarations
|
|
163
|
+
var isDefinedAbove = false;
|
|
164
|
+
for (var pNode of p) {
|
|
165
|
+
if (definedMap.has(pNode)) {
|
|
166
|
+
if (definedMap.get(pNode).has(o.name)) {
|
|
167
|
+
isDefinedAbove = true;
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
161
170
|
}
|
|
162
171
|
}
|
|
163
172
|
|
|
164
|
-
if (
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
};
|
|
173
|
+
if (!isDefinedAbove) {
|
|
174
|
+
isReferenceLess = false;
|
|
175
|
+
identifierPreventingTransformation = o.name;
|
|
168
176
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if (!isBound) {
|
|
173
|
-
defined.forEach((identifier) => {
|
|
174
|
-
referenced.delete(identifier);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
object.params.forEach((param) => {
|
|
178
|
-
referenced.delete(param.name);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
collect.push({
|
|
182
|
-
location: [object, parents],
|
|
183
|
-
references: referenced,
|
|
184
|
-
name: object.id?.name,
|
|
185
|
-
});
|
|
177
|
+
return "EXIT";
|
|
178
|
+
}
|
|
186
179
|
}
|
|
187
180
|
}
|
|
188
181
|
});
|
|
189
182
|
|
|
190
|
-
|
|
183
|
+
// This function is not 'reference-less', cannot be RGF'd
|
|
184
|
+
if (!isReferenceLess) {
|
|
185
|
+
if (object.id) {
|
|
186
|
+
this.log(
|
|
187
|
+
`${object?.id?.name}() cannot be transformed because of ${identifierPreventingTransformation}`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
191
190
|
return;
|
|
192
191
|
}
|
|
193
192
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
if (!references1.size && name) {
|
|
203
|
-
collect.forEach((o) => {
|
|
204
|
-
if (
|
|
205
|
-
o.location[0] !== location1[0] &&
|
|
206
|
-
o.references.size &&
|
|
207
|
-
o.references.delete(name)
|
|
208
|
-
) {
|
|
209
|
-
// console.log(collect);
|
|
193
|
+
// Since `new Function` is completely isolated, create an entire new obfuscator and run remaining transformations.
|
|
194
|
+
// RGF runs early and needs completed code before converting to a string.
|
|
195
|
+
// (^ the variables haven't been renamed yet)
|
|
196
|
+
var obfuscator = new Obfuscator({
|
|
197
|
+
...this.options,
|
|
198
|
+
stringEncoding: false,
|
|
199
|
+
compact: true,
|
|
200
|
+
});
|
|
210
201
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
);
|
|
217
|
-
if (hit) {
|
|
218
|
-
miss = 0;
|
|
219
|
-
} else {
|
|
220
|
-
miss++;
|
|
221
|
-
}
|
|
202
|
+
if (obfuscator.options.lock) {
|
|
203
|
+
delete obfuscator.options.lock.countermeasures;
|
|
222
204
|
|
|
223
|
-
|
|
224
|
-
|
|
205
|
+
// Integrity will not recursively apply to RGF'd functions. This is intended.
|
|
206
|
+
var lockTransform = obfuscator.transforms["Lock"];
|
|
207
|
+
if (lockTransform) {
|
|
208
|
+
lockTransform.before = lockTransform.before.filter(
|
|
209
|
+
(beforeTransform) => !(beforeTransform instanceof Integrity)
|
|
210
|
+
);
|
|
225
211
|
}
|
|
226
212
|
}
|
|
227
213
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
214
|
+
var transforms = obfuscator.array.filter(
|
|
215
|
+
(x) => x.priority > this.priority
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
var embeddedFunctionName = this.getPlaceholder();
|
|
219
|
+
|
|
220
|
+
var embeddedFunction = {
|
|
221
|
+
type: "FunctionDeclaration",
|
|
222
|
+
id: Identifier(embeddedFunctionName),
|
|
223
|
+
body: BlockStatement([...object.body.body]),
|
|
224
|
+
params: object.params,
|
|
225
|
+
async: false,
|
|
226
|
+
generator: false,
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
var tree = {
|
|
230
|
+
type: "Program",
|
|
231
|
+
body: [
|
|
232
|
+
embeddedFunction,
|
|
233
|
+
ReturnStatement(
|
|
234
|
+
CallExpression(
|
|
235
|
+
MemberExpression(
|
|
236
|
+
Identifier(embeddedFunctionName),
|
|
237
|
+
Literal("apply"),
|
|
238
|
+
true
|
|
239
|
+
),
|
|
240
|
+
[ThisExpression(), Identifier("arguments")]
|
|
241
|
+
)
|
|
242
|
+
),
|
|
243
|
+
],
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
transforms.forEach((transform) => {
|
|
247
|
+
transform.apply(tree);
|
|
246
248
|
});
|
|
247
249
|
|
|
248
|
-
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// An array containing all the function declarations
|
|
253
|
-
var referenceArray = "_" + getRandomString(10);
|
|
254
|
-
|
|
255
|
-
walk(contextObject, contextParents, (o, p) => {
|
|
256
|
-
if (o.type == "Identifier" && !reservedIdentifiers.has(o.name)) {
|
|
257
|
-
var index = names.get(o.name);
|
|
258
|
-
if (typeof index === "number") {
|
|
259
|
-
var info = getIdentifierInfo(o, p);
|
|
260
|
-
if (info.spec.isReferenced && !info.spec.isDefined) {
|
|
261
|
-
var location = getDefiningIdentifier(o, p);
|
|
262
|
-
if (location) {
|
|
263
|
-
var pointingTo = location[0];
|
|
264
|
-
var shouldBe = definingNodes.get(o.name);
|
|
265
|
-
|
|
266
|
-
// console.log(pointingTo, shouldBe);
|
|
267
|
-
|
|
268
|
-
if (pointingTo == shouldBe) {
|
|
269
|
-
this.log(o.name, "->", `${referenceArray}[${index}]`);
|
|
270
|
-
|
|
271
|
-
var memberExpression = MemberExpression(
|
|
272
|
-
Identifier(referenceArray),
|
|
273
|
-
Literal(index),
|
|
274
|
-
true
|
|
275
|
-
);
|
|
276
|
-
|
|
277
|
-
// Allow re-assignment to the RGF function
|
|
278
|
-
if (
|
|
279
|
-
p[0] &&
|
|
280
|
-
p[0].type === "AssignmentExpression" &&
|
|
281
|
-
p[0].left === o
|
|
282
|
-
) {
|
|
283
|
-
// fn = ...
|
|
284
|
-
|
|
285
|
-
this.replace(o, memberExpression);
|
|
286
|
-
} else {
|
|
287
|
-
// fn()
|
|
288
|
-
// fn
|
|
289
|
-
|
|
290
|
-
// In most cases the identifier is being used like this (call expression, or referenced to be called later)
|
|
291
|
-
// Replace it with a simple wrapper function that will pass on the reference array
|
|
292
|
-
|
|
293
|
-
var conditionalExpression = ConditionalExpression(
|
|
294
|
-
Template(
|
|
295
|
-
`typeof ${referenceArray}[${index}] === "function" && ${referenceArray}[${index}]["${
|
|
296
|
-
referenceSignatures[index] || "_"
|
|
297
|
-
}"]`
|
|
298
|
-
).single().expression,
|
|
299
|
-
FunctionExpression(
|
|
300
|
-
[],
|
|
301
|
-
[
|
|
302
|
-
ReturnStatement(
|
|
303
|
-
// clone() is required!
|
|
304
|
-
CallExpression(clone(memberExpression), [
|
|
305
|
-
Identifier(referenceArray),
|
|
306
|
-
SpreadElement(Identifier("arguments")),
|
|
307
|
-
])
|
|
308
|
-
),
|
|
309
|
-
]
|
|
310
|
-
),
|
|
311
|
-
clone(memberExpression)
|
|
312
|
-
);
|
|
313
|
-
|
|
314
|
-
this.replace(o, conditionalExpression);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
});
|
|
250
|
+
var toString = compileJsSync(tree, obfuscator.options);
|
|
322
251
|
|
|
323
|
-
|
|
324
|
-
var
|
|
325
|
-
|
|
252
|
+
// new Function(code)
|
|
253
|
+
var newFunctionExpression = NewExpression(Identifier("Function"), [
|
|
254
|
+
Literal(toString),
|
|
326
255
|
]);
|
|
327
256
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
(x) => x.priority > this.priority
|
|
354
|
-
);
|
|
355
|
-
|
|
356
|
-
var embeddedFunction = {
|
|
357
|
-
...object,
|
|
358
|
-
type: "FunctionDeclaration",
|
|
359
|
-
id: Identifier(embeddedName),
|
|
360
|
-
};
|
|
361
|
-
|
|
362
|
-
var tree = {
|
|
363
|
-
type: "Program",
|
|
364
|
-
body: [
|
|
365
|
-
embeddedFunction,
|
|
366
|
-
ReturnStatement(
|
|
367
|
-
CallExpression(
|
|
368
|
-
MemberExpression(
|
|
369
|
-
Identifier(embeddedName),
|
|
370
|
-
Literal("call"),
|
|
371
|
-
true
|
|
372
|
-
),
|
|
373
|
-
[
|
|
374
|
-
Identifier("undefined"),
|
|
375
|
-
SpreadElement(
|
|
376
|
-
Template(
|
|
377
|
-
`Array.prototype.slice.call(arguments, 1)`
|
|
378
|
-
).single().expression
|
|
379
|
-
),
|
|
380
|
-
]
|
|
381
|
-
)
|
|
382
|
-
),
|
|
383
|
-
],
|
|
384
|
-
};
|
|
385
|
-
|
|
386
|
-
(tree as any).__hiddenDeclarations = VariableDeclaration(
|
|
387
|
-
VariableDeclarator(referenceArray)
|
|
388
|
-
);
|
|
389
|
-
(tree as any).__hiddenDeclarations.hidden = true;
|
|
390
|
-
(tree as any).__hiddenDeclarations.declarations[0].id.hidden = true;
|
|
391
|
-
|
|
392
|
-
transforms.forEach((transform) => {
|
|
393
|
-
transform.apply(tree);
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
// Find eval callbacks
|
|
397
|
-
traverse(tree, (o, p) => {
|
|
398
|
-
if (o.$eval) {
|
|
399
|
-
return () => {
|
|
400
|
-
o.$eval(o, p);
|
|
401
|
-
};
|
|
402
|
-
}
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
var toString = compileJsSync(tree, this.options);
|
|
406
|
-
|
|
407
|
-
var newFunction = NewExpression(Identifier("Function"), [
|
|
408
|
-
Literal(referenceArray),
|
|
409
|
-
Literal(toString),
|
|
257
|
+
// The index where this function is placed in the array
|
|
258
|
+
var newFunctionExpressionIndex = this.arrayExpressionElements.length;
|
|
259
|
+
|
|
260
|
+
// Add it to the array
|
|
261
|
+
this.arrayExpressionElements.push(newFunctionExpression);
|
|
262
|
+
|
|
263
|
+
// The member expression to retrieve this function
|
|
264
|
+
var memberExpression = MemberExpression(
|
|
265
|
+
Identifier(this.arrayExpressionName),
|
|
266
|
+
Literal(newFunctionExpressionIndex),
|
|
267
|
+
true
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
// Replace based on type
|
|
271
|
+
|
|
272
|
+
// (1) Function Declaration:
|
|
273
|
+
// - Replace body with call to new function
|
|
274
|
+
if (object.type === "FunctionDeclaration") {
|
|
275
|
+
object.body = BlockStatement([
|
|
276
|
+
ReturnStatement(
|
|
277
|
+
CallExpression(
|
|
278
|
+
MemberExpression(memberExpression, Literal("apply"), true),
|
|
279
|
+
[ThisExpression(), Identifier("arguments")]
|
|
280
|
+
)
|
|
281
|
+
),
|
|
410
282
|
]);
|
|
411
283
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
// This code marks the function object with a unique property
|
|
418
|
-
return CallExpression(
|
|
419
|
-
FunctionExpression(
|
|
420
|
-
[],
|
|
421
|
-
[
|
|
422
|
-
VariableDeclaration(VariableDeclarator("fn", fn)),
|
|
423
|
-
ExpressionStatement(
|
|
424
|
-
AssignmentExpression(
|
|
425
|
-
"=",
|
|
426
|
-
MemberExpression(
|
|
427
|
-
Identifier("fn"),
|
|
428
|
-
Literal(signature),
|
|
429
|
-
true
|
|
430
|
-
),
|
|
431
|
-
Literal(true)
|
|
432
|
-
)
|
|
433
|
-
),
|
|
434
|
-
ReturnStatement(Identifier("fn")),
|
|
435
|
-
]
|
|
436
|
-
),
|
|
437
|
-
[]
|
|
438
|
-
);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
if (object.type === "FunctionDeclaration") {
|
|
442
|
-
arrayExpression.elements[names.get(name)] =
|
|
443
|
-
applySignature(newFunction);
|
|
444
|
-
|
|
445
|
-
if (Array.isArray(parents[0])) {
|
|
446
|
-
parents[0].splice(parents[0].indexOf(object), 1);
|
|
447
|
-
} else {
|
|
448
|
-
this.error(
|
|
449
|
-
new Error(
|
|
450
|
-
"Error deleting function declaration: " +
|
|
451
|
-
parents.map((x) => x.type).join(",")
|
|
452
|
-
)
|
|
453
|
-
);
|
|
454
|
-
}
|
|
455
|
-
} else {
|
|
456
|
-
// The wrapper function passes the reference array around
|
|
457
|
-
var wrapperFunction = FunctionExpression(
|
|
458
|
-
[],
|
|
459
|
-
[
|
|
460
|
-
ReturnStatement(
|
|
461
|
-
CallExpression(
|
|
462
|
-
MemberExpression(newFunction, Literal("call"), true),
|
|
463
|
-
[
|
|
464
|
-
Identifier("undefined"),
|
|
465
|
-
Identifier(referenceArray),
|
|
466
|
-
SpreadElement(Identifier("arguments")),
|
|
467
|
-
]
|
|
468
|
-
)
|
|
469
|
-
),
|
|
470
|
-
]
|
|
471
|
-
);
|
|
284
|
+
// The parameters are no longer needed ('arguments' is used to capture them)
|
|
285
|
+
object.params = [];
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
472
288
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
289
|
+
// (2) Function Expression:
|
|
290
|
+
// - Replace expression with member expression pointing to new function
|
|
291
|
+
if (object.type === "FunctionExpression") {
|
|
292
|
+
this.replace(object, memberExpression);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
476
295
|
};
|
|
477
296
|
}
|
|
478
297
|
}
|