Skip to content

Commit 64a5f6a

Browse files
authored
Merge pull request #38 from MichaelXF/dev
1.5.1 (Fix #37)
2 parents c5245d0 + 94064ae commit 64a5f6a

File tree

10 files changed

+223
-39
lines changed

10 files changed

+223
-39
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
# `1.5.1`
2+
Object Extraction Fix
3+
4+
- Fixed [#37](https://github.com/MichaelXF/js-confuser/issues/37)
5+
- - Object Extraction was applying to objects with get/set methods, fixed in this version.
6+
7+
- Slight improvement to `Flatten`
8+
19
# `1.5.0`
210
Hexadecimal Numbers
311

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
JS-Confuser is a JavaScript obfuscation tool to make your programs _impossible_ to read. [Try the web version](https://jsconfuser.com).
44

5+
[![NPM](https://img.shields.io/badge/NPM-%23000000.svg?style=for-the-badge&logo=npm&logoColor=white)](https://npmjs.com/package/js-confuser) [![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/MichaelXF/js-confuser) [![Netlify](https://img.shields.io/badge/netlify-%23000000.svg?style=for-the-badge&logo=netlify&logoColor=#00C7B7)](https://jsconfuser.com)
6+
57
## Key features
68

79
- Variable renaming

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "js-confuser",
3-
"version": "1.5.0",
3+
"version": "1.5.1",
44
"description": "JavaScript Obfuscation Tool.",
55
"main": "dist/index.js",
66
"types": "index.d.ts",

src/transforms/extraction/objectExtraction.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { getIdentifierInfo } from "../../util/identifiers";
1919
import { isValidIdentifier } from "../../util/compare";
2020
import { ComputeProbabilityMap } from "../../probability";
2121
import { ok } from "assert";
22+
import { isStringLiteral } from "../../util/guard";
2223

2324
/**
2425
* Extracts keys out of an object if possible.
@@ -87,17 +88,22 @@ export default class ObjectExtraction extends Transform {
8788
}
8889

8990
// check for computed properties
91+
// Change String literals to non-computed
9092
object.properties.forEach((prop) => {
91-
if (prop.computed && prop.key.type == "Literal") {
93+
if (prop.computed && isStringLiteral(prop.key)) {
9294
prop.computed = false;
9395
}
9496
});
9597

96-
var computed = object.properties.find((x) => x.computed);
97-
if (computed) {
98+
var nonInitOrComputed = object.properties.find(
99+
(x) => x.kind !== "init" || x.computed
100+
);
101+
102+
if (nonInitOrComputed) {
98103
this.log(
99-
name + " has computed property: " + computed.key.name ||
100-
computed.key.value
104+
name +
105+
" has non-init/computed property: " +
106+
nonInitOrComputed.key.name || nonInitOrComputed.key.value
101107
);
102108
illegal.add(name);
103109
return;

src/transforms/flatten.ts

Lines changed: 166 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ import {
1616
Node,
1717
BlockStatement,
1818
ArrayPattern,
19+
FunctionExpression,
20+
ObjectExpression,
21+
Property,
22+
SpreadElement,
23+
Literal,
24+
IfStatement,
25+
ThrowStatement,
26+
NewExpression,
1927
} from "../util/gen";
2028
import { getIdentifierInfo } from "../util/identifiers";
2129
import {
@@ -25,6 +33,7 @@ import {
2533
prepend,
2634
clone,
2735
} from "../util/insert";
36+
import { shuffle } from "../util/random";
2837
import Transform from "./transform";
2938

3039
/**
@@ -41,10 +50,17 @@ import Transform from "./transform";
4150
export default class Flatten extends Transform {
4251
definedNames: Map<Node, Set<string>>;
4352

53+
flatMapName: string;
54+
flatNode: Node;
55+
gen: any;
56+
4457
constructor(o) {
4558
super(o, ObfuscateOrder.Flatten);
4659

4760
this.definedNames = new Map();
61+
this.flatMapName = null;
62+
this.flatNode = null;
63+
this.gen = this.getGenerator();
4864
}
4965

5066
apply(tree) {
@@ -200,11 +216,10 @@ export default class Flatten extends Transform {
200216

201217
var output = Array.from(modified);
202218

203-
var newName =
204-
"flatten" +
205-
this.getPlaceholder() +
206-
"_" +
207-
((object.id && object.id.name) || "fn");
219+
var newName = this.gen.generate();
220+
var valName = this.getPlaceholder();
221+
var resultName = this.getPlaceholder();
222+
var propName = this.gen.generate();
208223

209224
getBlockBody(object.body).push(ReturnStatement());
210225
walk(object.body, [object, ...parents], (o, p) => {
@@ -222,51 +237,100 @@ export default class Flatten extends Transform {
222237
}
223238

224239
o.argument = ArrayExpression(elements);
240+
241+
o.argument = AssignmentExpression(
242+
"=",
243+
MemberExpression(
244+
Identifier(resultName),
245+
Identifier(propName),
246+
false
247+
),
248+
o.argument
249+
);
225250
}
226251
};
227252
});
228253

229254
var newBody = getBlockBody(object.body);
230255

231-
if (input.length) {
232-
newBody.unshift(
256+
newBody.unshift(
257+
VariableDeclaration(
258+
VariableDeclarator(
259+
ArrayPattern([
260+
ArrayPattern(input.map(Identifier)),
261+
ArrayPattern(clone(object.params)),
262+
Identifier(resultName),
263+
]),
264+
265+
Identifier(valName)
266+
)
267+
)
268+
);
269+
270+
if (!this.flatMapName) {
271+
this.flatMapName = this.getPlaceholder();
272+
prepend(
273+
parents[parents.length - 1],
233274
VariableDeclaration(
234275
VariableDeclarator(
235-
ArrayPattern(input.map(Identifier)),
236-
ThisExpression()
276+
this.flatMapName,
277+
(this.flatNode = ObjectExpression([]))
237278
)
238279
)
239280
);
240281
}
241282

242-
var newFunctionDeclaration = FunctionDeclaration(
243-
newName,
244-
clone(object.params),
283+
var newFunctionExpression = FunctionExpression(
284+
[Identifier(valName)],
245285
newBody
246286
);
247-
newFunctionDeclaration.async = !!object.async;
248-
newFunctionDeclaration.generator = !!object.generator;
249287

250-
prepend(parents[parents.length - 1], newFunctionDeclaration);
288+
newFunctionExpression.async = !!object.async;
289+
newFunctionExpression.generator = !!object.generator;
290+
291+
var property = Property(
292+
Identifier(newName),
293+
newFunctionExpression,
294+
false
295+
);
296+
property.kind = "set";
297+
298+
this.flatNode.properties.push(property);
299+
300+
var identifier = MemberExpression(
301+
Identifier(this.flatMapName),
302+
Identifier(newName),
303+
false
304+
);
251305

252306
var newParamNodes = object.params.map(() =>
253307
Identifier(this.getPlaceholder())
254308
);
255309

256310
// var result = newFn.call([...refs], ...arguments)
257-
var call = VariableDeclaration(
311+
var call = VariableDeclaration([
312+
VariableDeclarator(resultName, ArrayExpression([])),
258313
VariableDeclarator(
259-
"result",
260-
CallExpression(
261-
MemberExpression(Identifier(newName), Identifier("call"), false),
262-
[ArrayExpression(input.map(Identifier)), ...newParamNodes]
314+
"_",
315+
AssignmentExpression(
316+
"=",
317+
identifier,
318+
ArrayExpression([
319+
ArrayExpression(input.map(Identifier)),
320+
ArrayExpression([...newParamNodes]),
321+
Identifier(resultName),
322+
])
263323
)
264-
)
265-
);
324+
),
325+
]);
266326

267327
// result.pop()
268328
var pop = CallExpression(
269-
MemberExpression(Identifier("result"), Identifier("pop"), false),
329+
MemberExpression(
330+
MemberExpression(Identifier(resultName), Identifier(propName), false),
331+
Identifier("pop"),
332+
false
333+
),
270334
[]
271335
);
272336

@@ -277,16 +341,90 @@ export default class Flatten extends Transform {
277341
//
278342
// return result.pop()
279343

280-
object.body = BlockStatement([
281-
call,
282-
...[...output].reverse().map((name) => {
344+
var newObjectBody: Node[] = [call];
345+
var outputReversed = [...output].reverse();
346+
347+
// DECOY STATEMENTS
348+
var decoyKey = this.gen.generate();
349+
var decoyNodes = [
350+
IfStatement(
351+
MemberExpression(
352+
Identifier(resultName),
353+
Identifier(this.gen.generate()),
354+
false
355+
),
356+
[
357+
ThrowStatement(
358+
NewExpression(Identifier("Error"), [
359+
Literal(this.getPlaceholder()),
360+
])
361+
),
362+
]
363+
),
364+
IfStatement(
365+
MemberExpression(
366+
Identifier(resultName),
367+
Identifier(this.gen.generate()),
368+
false
369+
),
370+
[ReturnStatement(Identifier(resultName))]
371+
),
372+
IfStatement(
373+
MemberExpression(
374+
Identifier(resultName),
375+
Identifier(this.gen.generate()),
376+
false
377+
),
378+
[ReturnStatement(Identifier(resultName))]
379+
),
380+
IfStatement(
381+
MemberExpression(Identifier(resultName), Identifier(decoyKey), false),
382+
[
383+
ReturnStatement(
384+
MemberExpression(
385+
Identifier(resultName),
386+
Identifier(decoyKey),
387+
false
388+
)
389+
),
390+
]
391+
),
392+
IfStatement(
393+
MemberExpression(
394+
Identifier(resultName),
395+
Identifier(this.gen.generate()),
396+
false
397+
),
398+
[
399+
ReturnStatement(
400+
MemberExpression(
401+
Identifier(resultName),
402+
Identifier(this.gen.generate()),
403+
false
404+
)
405+
),
406+
]
407+
),
408+
];
409+
410+
shuffle(decoyNodes);
411+
decoyNodes.forEach((decoyNode) => {
412+
if (Math.random() < 0.5) {
413+
newObjectBody.push(decoyNode);
414+
}
415+
});
416+
417+
newObjectBody.push(
418+
...outputReversed.map((name) => {
283419
return ExpressionStatement(
284420
AssignmentExpression("=", Identifier(name), clone(pop))
285421
);
286422
}),
287423

288-
ReturnStatement(clone(pop)),
289-
]);
424+
ReturnStatement(clone(pop))
425+
);
426+
427+
object.body = BlockStatement(newObjectBody);
290428

291429
object.params = newParamNodes;
292430
};

src/util/gen.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export type EvalCallback = {
5858
*/
5959
export type Chain = Node[];
6060

61-
export function Literal(value: string | number | boolean) {
61+
export function Literal(value: string | number | boolean): Node {
6262
if (typeof value === "undefined") {
6363
throw new Error("value is undefined");
6464
}

src/util/guard.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { Node } from "./gen";
2+
3+
export function isStringLiteral(node: Node) {
4+
return (
5+
node.type === "Literal" && typeof node.value === "string" && !node.regex
6+
);
7+
}

test/transforms/extraction/objectExtraction.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,3 +423,26 @@ it("should properly use custom callback to exclude certain names from being chan
423423

424424
eval(output);
425425
});
426+
427+
it("should not apply to objects with non-init properties (method, set, get)", async () => {
428+
var code = `
429+
430+
var realValue = 0;
431+
var TEST_OBJECT = {
432+
set key(newValue){
433+
realValue = newValue;
434+
},
435+
get key(){
436+
return realValue;
437+
}
438+
};
439+
`;
440+
441+
var output = await JsConfuser(code, {
442+
target: "node",
443+
objectExtraction: true,
444+
});
445+
446+
expect(output).toContain("TEST_OBJECT");
447+
expect(output).toContain("set ");
448+
});

0 commit comments

Comments
 (0)