js-confuser 1.6.0 → 1.7.0
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 +23 -0
- package/README.md +215 -170
- 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 +4 -1
- package/dist/transforms/extraction/duplicateLiteralsRemoval.js +89 -58
- 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 +139 -246
- 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 +37 -3
- package/dist/util/insert.js +24 -3
- package/docs/ControlFlowFlattening.md +595 -0
- package/{Countermeasures.md → docs/Countermeasures.md} +1 -15
- package/{Integrity.md → docs/Integrity.md} +2 -2
- package/docs/RGF.md +419 -0
- package/package.json +1 -1
- 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 +5 -1
- package/src/transforms/extraction/duplicateLiteralsRemoval.ts +130 -129
- 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 +214 -404
- 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 +43 -2
- package/src/util/insert.ts +26 -2
- package/test/code/ES6.src.js +24 -0
- package/test/transforms/flatten.test.ts +352 -88
- package/test/transforms/identifier/movedDeclarations.test.ts +37 -9
- package/test/transforms/identifier/renameVariables.test.ts +37 -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 +113 -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/templates/crash.ts
CHANGED
|
@@ -3,7 +3,7 @@ import Template from "./template";
|
|
|
3
3
|
export const CrashTemplate1 = Template(`
|
|
4
4
|
var {var} = "a";
|
|
5
5
|
while(1){
|
|
6
|
-
{var} = {var} += "a";
|
|
6
|
+
{var} = {var} += "a";
|
|
7
7
|
}
|
|
8
8
|
`);
|
|
9
9
|
|
|
@@ -40,16 +40,16 @@ try {
|
|
|
40
40
|
});
|
|
41
41
|
|
|
42
42
|
{$1}();
|
|
43
|
-
} catch ( e ) {
|
|
44
|
-
while(e ? e : !e){
|
|
45
|
-
var b;
|
|
46
|
-
var c = 0;
|
|
47
|
-
(e ? !e : e) ? (function(e){
|
|
48
|
-
c = e ? 0 : !e ? 1 : 0;
|
|
49
|
-
})(e) : b = 1;
|
|
43
|
+
} catch ( {$1}e ) {
|
|
44
|
+
while({$1}e ? {$1}e : !{$1}e){
|
|
45
|
+
var {$1}b;
|
|
46
|
+
var {$1}c = 0;
|
|
47
|
+
({$1}e ? !{$1}e : {$1}e) ? (function({$1}e){
|
|
48
|
+
{$1}c = {$1}e ? 0 : !{$1}e ? 1 : 0;
|
|
49
|
+
})({$1}e) : {$1}b = 1;
|
|
50
50
|
|
|
51
|
-
if(b&&c){break;}
|
|
52
|
-
if(b){continue;}
|
|
51
|
+
if({$1}b&&{$1}c){break;}
|
|
52
|
+
if({$1}b){continue;}
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
`);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import Template from "./template";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Helper function to set `function.length` property.
|
|
5
|
+
*/
|
|
6
|
+
export const FunctionLengthTemplate = Template(`
|
|
7
|
+
function {name}(functionObject, functionLength){
|
|
8
|
+
Object["defineProperty"](functionObject, "length", {
|
|
9
|
+
"value": functionLength,
|
|
10
|
+
"configurable": true
|
|
11
|
+
});
|
|
12
|
+
return functionObject;
|
|
13
|
+
}
|
|
14
|
+
`);
|
|
@@ -68,6 +68,8 @@ import Template from "../templates/template";
|
|
|
68
68
|
* 3. using `this`
|
|
69
69
|
*/
|
|
70
70
|
export default class Dispatcher extends Transform {
|
|
71
|
+
// Debug mode preserves function names
|
|
72
|
+
isDebug = false;
|
|
71
73
|
count: number;
|
|
72
74
|
|
|
73
75
|
constructor(o) {
|
|
@@ -196,7 +198,9 @@ export default class Dispatcher extends Transform {
|
|
|
196
198
|
// map original name->new game
|
|
197
199
|
var gen = this.getGenerator();
|
|
198
200
|
Object.keys(functionDeclarations).forEach((name) => {
|
|
199
|
-
newFnNames[name] =
|
|
201
|
+
newFnNames[name] = this.isDebug
|
|
202
|
+
? "_dispatcher_" + this.count + "_" + name
|
|
203
|
+
: gen.generate();
|
|
200
204
|
});
|
|
201
205
|
// set containing new name
|
|
202
206
|
var set = new Set(Object.keys(newFnNames));
|
|
@@ -13,15 +13,9 @@ import {
|
|
|
13
13
|
BinaryExpression,
|
|
14
14
|
FunctionDeclaration,
|
|
15
15
|
ThisExpression,
|
|
16
|
-
|
|
16
|
+
ConditionalExpression,
|
|
17
17
|
} from "../../util/gen";
|
|
18
|
-
import {
|
|
19
|
-
append,
|
|
20
|
-
clone,
|
|
21
|
-
getLexContext,
|
|
22
|
-
isLexContext,
|
|
23
|
-
prepend,
|
|
24
|
-
} from "../../util/insert";
|
|
18
|
+
import { append, clone, prepend } from "../../util/insert";
|
|
25
19
|
import { isDirective, isPrimitive } from "../../util/compare";
|
|
26
20
|
|
|
27
21
|
import { ObfuscateOrder } from "../../order";
|
|
@@ -29,6 +23,7 @@ import { isModuleSource } from "../string/stringConcealing";
|
|
|
29
23
|
import { ComputeProbabilityMap } from "../../probability";
|
|
30
24
|
import { ok } from "assert";
|
|
31
25
|
import { chance, choice, getRandomInteger } from "../../util/random";
|
|
26
|
+
import { getBlock } from "../../traverse";
|
|
32
27
|
|
|
33
28
|
/**
|
|
34
29
|
* [Duplicate Literals Removal](https://docs.jscrambler.com/code-integrity/documentation/transformations/duplicate-literals-removal) replaces duplicate literals with a variable name.
|
|
@@ -49,20 +44,25 @@ import { chance, choice, getRandomInteger } from "../../util/random";
|
|
|
49
44
|
* ```
|
|
50
45
|
*/
|
|
51
46
|
export default class DuplicateLiteralsRemoval extends Transform {
|
|
47
|
+
// The array holding all the duplicate literals
|
|
52
48
|
arrayName: string;
|
|
49
|
+
// The array expression node to be inserted into the program
|
|
53
50
|
arrayExpression: Node;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Literals in the array
|
|
54
|
+
*/
|
|
54
55
|
map: Map<string, number>;
|
|
55
|
-
first: Map<string, Location | null>;
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
*
|
|
58
|
+
* Literals are saved here the first time they are seen.
|
|
59
59
|
*/
|
|
60
|
-
|
|
60
|
+
first: Map<string, Location>;
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
|
-
*
|
|
63
|
+
* Block -> { functionName, indexShift }
|
|
64
64
|
*/
|
|
65
|
-
|
|
65
|
+
functions: Map<Node, { functionName: string; indexShift: number }>;
|
|
66
66
|
|
|
67
67
|
constructor(o) {
|
|
68
68
|
super(o, ObfuscateOrder.DuplicateLiteralsRemoval);
|
|
@@ -70,14 +70,14 @@ export default class DuplicateLiteralsRemoval extends Transform {
|
|
|
70
70
|
this.map = new Map();
|
|
71
71
|
this.first = new Map();
|
|
72
72
|
|
|
73
|
-
this.
|
|
74
|
-
this.fnGetters = new Map();
|
|
73
|
+
this.functions = new Map();
|
|
75
74
|
}
|
|
76
75
|
|
|
77
76
|
apply(tree) {
|
|
78
77
|
super.apply(tree);
|
|
79
78
|
|
|
80
|
-
if (this.arrayName && this.arrayExpression.elements.length) {
|
|
79
|
+
if (this.arrayName && this.arrayExpression.elements.length > 0) {
|
|
80
|
+
// This function simply returns the array
|
|
81
81
|
var getArrayFn = this.getPlaceholder();
|
|
82
82
|
append(
|
|
83
83
|
tree,
|
|
@@ -88,22 +88,79 @@ export default class DuplicateLiteralsRemoval extends Transform {
|
|
|
88
88
|
)
|
|
89
89
|
);
|
|
90
90
|
|
|
91
|
+
// This variable holds the array
|
|
91
92
|
prepend(
|
|
92
93
|
tree,
|
|
93
94
|
VariableDeclaration(
|
|
94
95
|
VariableDeclarator(
|
|
95
96
|
this.arrayName,
|
|
96
97
|
CallExpression(
|
|
97
|
-
MemberExpression(
|
|
98
|
-
Identifier(getArrayFn),
|
|
99
|
-
Identifier("call"),
|
|
100
|
-
false
|
|
101
|
-
),
|
|
98
|
+
MemberExpression(Identifier(getArrayFn), Literal("call"), true),
|
|
102
99
|
[ThisExpression()]
|
|
103
100
|
)
|
|
104
101
|
)
|
|
105
102
|
)
|
|
106
103
|
);
|
|
104
|
+
|
|
105
|
+
// Create all the functions needed
|
|
106
|
+
for (var blockNode of this.functions.keys()) {
|
|
107
|
+
var { functionName, indexShift } = this.functions.get(blockNode);
|
|
108
|
+
|
|
109
|
+
var propertyNode: Node = BinaryExpression(
|
|
110
|
+
"-",
|
|
111
|
+
Identifier("index_param"),
|
|
112
|
+
Literal(indexShift)
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
var indexRangeInclusive = [
|
|
116
|
+
0 + indexShift - 1,
|
|
117
|
+
this.map.size + indexShift,
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
// The function uses mangling to hide the index being accessed
|
|
121
|
+
var mangleCount = getRandomInteger(1, 10);
|
|
122
|
+
for (var i = 0; i < mangleCount; i++) {
|
|
123
|
+
var operator = choice([">", "<"]);
|
|
124
|
+
var compareValue = choice(indexRangeInclusive);
|
|
125
|
+
|
|
126
|
+
var test = BinaryExpression(
|
|
127
|
+
operator,
|
|
128
|
+
Identifier("index_param"),
|
|
129
|
+
Literal(compareValue)
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
var alternate = BinaryExpression(
|
|
133
|
+
"-",
|
|
134
|
+
Identifier("index_param"),
|
|
135
|
+
Literal(getRandomInteger(-100, 100))
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
var testValue =
|
|
139
|
+
(operator === ">" && compareValue === indexRangeInclusive[0]) ||
|
|
140
|
+
(operator === "<" && compareValue === indexRangeInclusive[1]);
|
|
141
|
+
|
|
142
|
+
propertyNode = ConditionalExpression(
|
|
143
|
+
test,
|
|
144
|
+
testValue ? propertyNode : alternate,
|
|
145
|
+
!testValue ? propertyNode : alternate
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
var returnArgument = MemberExpression(
|
|
150
|
+
Identifier(this.arrayName),
|
|
151
|
+
propertyNode,
|
|
152
|
+
true
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
prepend(
|
|
156
|
+
blockNode,
|
|
157
|
+
FunctionDeclaration(
|
|
158
|
+
functionName,
|
|
159
|
+
[Identifier("index_param")],
|
|
160
|
+
[ReturnStatement(returnArgument)]
|
|
161
|
+
)
|
|
162
|
+
);
|
|
163
|
+
}
|
|
107
164
|
}
|
|
108
165
|
}
|
|
109
166
|
|
|
@@ -122,104 +179,44 @@ export default class DuplicateLiteralsRemoval extends Transform {
|
|
|
122
179
|
* @param parents
|
|
123
180
|
* @param index
|
|
124
181
|
*/
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
// create one if none are available (or by random chance if none are here locally)
|
|
140
|
-
var shouldCreateNew =
|
|
141
|
-
!getterName || (!hasGetterHere && Math.random() > 0.9);
|
|
142
|
-
|
|
143
|
-
if (shouldCreateNew) {
|
|
144
|
-
ok(!this.fnGetters.has(lexContext));
|
|
145
|
-
|
|
146
|
-
var lexContextIndex = parents.findIndex(
|
|
147
|
-
(x) => x !== lexContext && isLexContext(x)
|
|
148
|
-
);
|
|
149
|
-
var basedOn =
|
|
150
|
-
lexContextIndex !== -1
|
|
151
|
-
? choice(
|
|
152
|
-
parents
|
|
153
|
-
.slice(lexContextIndex + 1)
|
|
154
|
-
.map((x) => this.fnGetters.get(x))
|
|
155
|
-
.filter((x) => x)
|
|
156
|
-
)
|
|
157
|
-
: null;
|
|
158
|
-
|
|
159
|
-
var body = [];
|
|
160
|
-
var thisShift = getRandomInteger(-250, 250);
|
|
161
|
-
// the name of the getter
|
|
162
|
-
getterName = this.getPlaceholder() + "_dLR_" + this.fnGetters.size;
|
|
163
|
-
|
|
164
|
-
if (basedOn) {
|
|
165
|
-
var shift = this.fnShifts.get(basedOn);
|
|
166
|
-
ok(typeof shift === "number");
|
|
167
|
-
|
|
168
|
-
body = [
|
|
169
|
-
ReturnStatement(
|
|
170
|
-
CallExpression(Identifier(basedOn), [
|
|
171
|
-
BinaryExpression("+", Identifier("index"), Literal(thisShift)),
|
|
172
|
-
])
|
|
173
|
-
),
|
|
174
|
-
];
|
|
175
|
-
|
|
176
|
-
this.fnShifts.set(getterName, shift + thisShift);
|
|
177
|
-
} else {
|
|
178
|
-
// from scratch
|
|
179
|
-
|
|
180
|
-
body = [
|
|
181
|
-
ReturnStatement(
|
|
182
|
-
MemberExpression(
|
|
183
|
-
Identifier(this.arrayName),
|
|
184
|
-
BinaryExpression("+", Identifier("index"), Literal(thisShift)),
|
|
185
|
-
true
|
|
186
|
-
)
|
|
187
|
-
),
|
|
188
|
-
];
|
|
182
|
+
transformLiteral(object: Node, parents: Node[], index: number) {
|
|
183
|
+
var blockNode = choice(parents.filter((x) => this.functions.has(x)));
|
|
184
|
+
|
|
185
|
+
// Create initial function if none exist
|
|
186
|
+
if (this.functions.size === 0) {
|
|
187
|
+
var root = parents[parents.length - 1];
|
|
188
|
+
var rootFunctionName = this.getPlaceholder() + "_dLR_0";
|
|
189
|
+
this.functions.set(root, {
|
|
190
|
+
functionName: rootFunctionName,
|
|
191
|
+
indexShift: getRandomInteger(-100, 100),
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
blockNode = root;
|
|
195
|
+
}
|
|
189
196
|
|
|
190
|
-
|
|
191
|
-
|
|
197
|
+
// If no function here exist, possibly create new chained function
|
|
198
|
+
var block = getBlock(object, parents);
|
|
199
|
+
if (!this.functions.has(block) && chance(50 - this.functions.size)) {
|
|
200
|
+
var newFunctionName =
|
|
201
|
+
this.getPlaceholder() + "_dLR_" + this.functions.size;
|
|
192
202
|
|
|
193
|
-
this.
|
|
203
|
+
this.functions.set(block, {
|
|
204
|
+
functionName: newFunctionName,
|
|
205
|
+
indexShift: getRandomInteger(-100, 100),
|
|
206
|
+
});
|
|
194
207
|
|
|
195
|
-
|
|
196
|
-
lexContext,
|
|
197
|
-
VariableDeclaration(
|
|
198
|
-
VariableDeclarator(
|
|
199
|
-
getterName,
|
|
200
|
-
CallExpression(
|
|
201
|
-
FunctionExpression(
|
|
202
|
-
[],
|
|
203
|
-
[
|
|
204
|
-
ReturnStatement(
|
|
205
|
-
FunctionExpression([Identifier("index")], body)
|
|
206
|
-
),
|
|
207
|
-
]
|
|
208
|
-
),
|
|
209
|
-
[]
|
|
210
|
-
)
|
|
211
|
-
)
|
|
212
|
-
)
|
|
213
|
-
);
|
|
208
|
+
blockNode = block;
|
|
214
209
|
}
|
|
215
210
|
|
|
216
|
-
|
|
211
|
+
// Derive the function to call from the selected blockNode
|
|
212
|
+
var { functionName, indexShift } = this.functions.get(blockNode);
|
|
217
213
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
214
|
+
// Call the function given it's indexShift
|
|
215
|
+
var callExpression = CallExpression(Identifier(functionName), [
|
|
216
|
+
Literal(index + indexShift),
|
|
217
|
+
]);
|
|
218
|
+
|
|
219
|
+
this.replaceIdentifierOrLiteral(object, callExpression, parents);
|
|
223
220
|
}
|
|
224
221
|
|
|
225
222
|
transform(object: Node, parents: Node[]) {
|
|
@@ -234,7 +231,7 @@ export default class DuplicateLiteralsRemoval extends Transform {
|
|
|
234
231
|
}
|
|
235
232
|
|
|
236
233
|
// HARD CODED LIMIT of 10,000 (after 1,000 elements)
|
|
237
|
-
if (this.map.size > 1000 &&
|
|
234
|
+
if (this.map.size > 1000 && chance(this.map.size / 100)) return;
|
|
238
235
|
|
|
239
236
|
if (
|
|
240
237
|
this.arrayName &&
|
|
@@ -244,11 +241,11 @@ export default class DuplicateLiteralsRemoval extends Transform {
|
|
|
244
241
|
return;
|
|
245
242
|
}
|
|
246
243
|
|
|
247
|
-
var
|
|
244
|
+
var stringValue;
|
|
248
245
|
if (object.type == "Literal") {
|
|
249
|
-
|
|
246
|
+
stringValue = typeof object.value + ":" + object.value;
|
|
250
247
|
if (object.value === null) {
|
|
251
|
-
|
|
248
|
+
stringValue = "null:null";
|
|
252
249
|
} else {
|
|
253
250
|
// Skip empty strings
|
|
254
251
|
if (typeof object.value === "string" && !object.value) {
|
|
@@ -256,42 +253,46 @@ export default class DuplicateLiteralsRemoval extends Transform {
|
|
|
256
253
|
}
|
|
257
254
|
}
|
|
258
255
|
} else if (object.type == "Identifier") {
|
|
259
|
-
|
|
256
|
+
stringValue = "identifier:" + object.name;
|
|
260
257
|
} else {
|
|
261
258
|
throw new Error("Unsupported primitive type: " + object.type);
|
|
262
259
|
}
|
|
263
260
|
|
|
264
|
-
ok(
|
|
261
|
+
ok(stringValue);
|
|
265
262
|
|
|
266
|
-
if (
|
|
267
|
-
|
|
268
|
-
} else {
|
|
263
|
+
if (this.map.has(stringValue) || this.first.has(stringValue)) {
|
|
264
|
+
// Create the array if not already made
|
|
269
265
|
if (!this.arrayName) {
|
|
270
266
|
this.arrayName = this.getPlaceholder();
|
|
271
267
|
this.arrayExpression = ArrayExpression([]);
|
|
272
268
|
}
|
|
273
269
|
|
|
274
|
-
|
|
270
|
+
// Delete with first location
|
|
271
|
+
var firstLocation = this.first.get(stringValue);
|
|
275
272
|
if (firstLocation) {
|
|
276
|
-
this.first.set(value, null);
|
|
277
273
|
var index = this.map.size;
|
|
278
274
|
|
|
279
|
-
ok(!this.map.has(
|
|
280
|
-
this.map.set(
|
|
275
|
+
ok(!this.map.has(stringValue));
|
|
276
|
+
this.map.set(stringValue, index);
|
|
277
|
+
this.first.delete(stringValue);
|
|
281
278
|
|
|
282
279
|
var pushing = clone(object);
|
|
283
280
|
this.arrayExpression.elements.push(pushing);
|
|
284
281
|
|
|
285
282
|
ok(this.arrayExpression.elements[index] === pushing);
|
|
286
283
|
|
|
287
|
-
this.
|
|
284
|
+
this.transformLiteral(firstLocation[0], firstLocation[1], index);
|
|
288
285
|
}
|
|
289
286
|
|
|
290
|
-
var index = this.map.get(
|
|
287
|
+
var index = this.map.get(stringValue);
|
|
291
288
|
ok(typeof index === "number");
|
|
292
289
|
|
|
293
|
-
this.
|
|
290
|
+
this.transformLiteral(object, parents, index);
|
|
291
|
+
return;
|
|
294
292
|
}
|
|
293
|
+
|
|
294
|
+
// Save this, maybe a duplicate will be found.
|
|
295
|
+
this.first.set(stringValue, [object, parents]);
|
|
295
296
|
};
|
|
296
297
|
}
|
|
297
298
|
}
|