Skip to content

Commit 37bd018

Browse files
authored
Merge pull request javascript-obfuscator#315 from javascript-obfuscator/function-obfuscation-bugs
Function obfuscation bugs
2 parents 6eeb1cb + af5b356 commit 37bd018

File tree

10 files changed

+150
-101
lines changed

10 files changed

+150
-101
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Change Log
33
v0.18.0
44
---
55
* **New option:** `reservedStrings` disables transformation of string literals, which being matched by passed RegExp patterns
6+
* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/313
67
* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/309
78
* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/307
89

dist/index.browser.js

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

dist/index.cli.js

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

dist/index.js

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

src/node-transformers/obfuscating-transformers/FunctionTransformer.ts

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -123,28 +123,27 @@ export class FunctionTransformer extends AbstractNodeTransformer {
123123
* @param {TNodeWithLexicalScope} lexicalScopeNode
124124
*/
125125
private storeFunctionParams (functionNode: ESTree.Function, lexicalScopeNode: TNodeWithLexicalScope): void {
126-
functionNode.params
127-
.forEach((paramsNode: ESTree.Node) => {
128-
estraverse.traverse(paramsNode, {
129-
enter: (node: ESTree.Node, parentNode: ESTree.Node | null): estraverse.VisitorOption | void => {
130-
// Should check with identifier as first argument,
131-
// because prohibited identifier can be easily ignored
132-
if (FunctionTransformer.isProhibitedIdentifierOfPropertyNode(node, parentNode)) {
133-
return;
134-
}
126+
const visitor: estraverse.Visitor = {
127+
enter: (node: ESTree.Node, parentNode: ESTree.Node | null): estraverse.VisitorOption | void => {
128+
// Should check with identifier as first argument,
129+
// because prohibited identifier can be easily ignored
130+
if (FunctionTransformer.isProhibitedIdentifierOfPropertyNode(node, parentNode)) {
131+
return;
132+
}
135133

136-
if (NodeGuards.isAssignmentPatternNode(node) && NodeGuards.isIdentifierNode(node.left)) {
137-
this.identifierObfuscatingReplacer.storeLocalName(node.left.name, lexicalScopeNode);
134+
if (NodeGuards.isAssignmentPatternNode(node) && NodeGuards.isIdentifierNode(node.left)) {
135+
this.identifierObfuscatingReplacer.storeLocalName(node.left.name, lexicalScopeNode);
138136

139-
return estraverse.VisitorOption.Skip;
140-
}
137+
return estraverse.VisitorOption.Skip;
138+
}
141139

142-
if (NodeGuards.isIdentifierNode(node)) {
143-
this.identifierObfuscatingReplacer.storeLocalName(node.name, lexicalScopeNode);
144-
}
145-
}
146-
});
147-
});
140+
if (NodeGuards.isIdentifierNode(node)) {
141+
this.identifierObfuscatingReplacer.storeLocalName(node.name, lexicalScopeNode);
142+
}
143+
}
144+
};
145+
146+
functionNode.params.forEach((paramsNode: ESTree.Node) => estraverse.traverse(paramsNode, visitor));
148147
}
149148

150149
/**
@@ -157,19 +156,12 @@ export class FunctionTransformer extends AbstractNodeTransformer {
157156
lexicalScopeNode: TNodeWithLexicalScope,
158157
ignoredIdentifierNamesSet: Set <string> = new Set()
159158
): void {
160-
const replaceVisitor: estraverse.Visitor = {
159+
const visitor: estraverse.Visitor = {
161160
enter: (node: ESTree.Node, parentNode: ESTree.Node | null): void | estraverse.VisitorOption => {
162-
/**
163-
* Should skip function node itself
164-
*/
165-
if (node === functionNode) {
166-
return;
167-
}
168-
169161
/**
170162
* Should process nested functions in different traverse loop to avoid wrong code generation
171163
*/
172-
if (NodeGuards.isFunctionNode(node)) {
164+
if (NodeGuards.isFunctionNode(node) && node !== functionNode) {
173165
this.replaceFunctionParams(node, lexicalScopeNode, new Set(ignoredIdentifierNamesSet));
174166

175167
return estraverse.VisitorOption.Skip;
@@ -187,6 +179,7 @@ export class FunctionTransformer extends AbstractNodeTransformer {
187179
if (
188180
parentNode
189181
&& NodeGuards.isReplaceableIdentifierNode(node, parentNode)
182+
&& !NodeMetadata.isRenamedIdentifier(node)
190183
&& !ignoredIdentifierNamesSet.has(node.name)
191184
) {
192185
const newIdentifier: ESTree.Identifier = this.identifierObfuscatingReplacer
@@ -201,6 +194,6 @@ export class FunctionTransformer extends AbstractNodeTransformer {
201194
}
202195
};
203196

204-
estraverse.replace(functionNode, replaceVisitor)
197+
estraverse.replace(functionNode, visitor)
205198
}
206199
}

test/dev/dev.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,18 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
88
`
99
(function(foo){
1010
function foo () {
11-
11+
1212
}
13-
13+
1414
return new foo();
1515
})();
16+
1617
`,
1718
{
1819
...NO_ADDITIONAL_NODES_PRESET,
1920
compact: false,
20-
transformObjectKeys: true
21+
transformObjectKeys: true,
22+
seed: 1
2123
}
2224
).getObfuscatedCode();
2325

test/functional-tests/node-transformers/obfuscating-transformers/function-transformer/FunctionTransformer.spec.ts

Lines changed: 96 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -46,59 +46,107 @@ describe('FunctionTransformer', () => {
4646
});
4747

4848
describe('function id name obfuscation', () => {
49-
const functionExpressionParamIdentifierRegExp: RegExp = /\(function *\((_0x[a-f0-9]{4,6})\) *\{/;
50-
const functionParamIdentifierRegExp: RegExp = /function *(_0x[a-f0-9]{4,6}) *\(\) *\{/;
51-
const functionObjectIdentifierRegExp: RegExp = /return new (_0x[a-f0-9]{4,6}) *\(\);/;
49+
describe('Variant #1', () => {
50+
const functionExpressionParamIdentifierRegExp: RegExp = /\(function *\((_0x[a-f0-9]{4,6})\) *\{/;
51+
const functionParamIdentifierRegExp: RegExp = /function *(_0x[a-f0-9]{4,6}) *\(\) *\{/;
52+
const functionObjectIdentifierRegExp: RegExp = /return new (_0x[a-f0-9]{4,6}) *\(\);/;
5253

53-
let obfuscatedCode: string,
54-
functionExpressionParamIdentifierName: string,
55-
functionParamIdentifierName: string,
56-
functionObjectIdentifierName: string;
54+
let obfuscatedCode: string,
55+
functionExpressionParamIdentifierName: string,
56+
functionParamIdentifierName: string,
57+
functionObjectIdentifierName: string;
5758

58-
before(() => {
59-
const code: string = readFileAsString(__dirname + '/fixtures/function-id-name.js');
59+
before(() => {
60+
const code: string = readFileAsString(__dirname + '/fixtures/function-id-name-1.js');
6061

61-
obfuscatedCode = JavaScriptObfuscator.obfuscate(
62-
code,
63-
{
64-
...NO_ADDITIONAL_NODES_PRESET
65-
}
66-
).getObfuscatedCode();
62+
obfuscatedCode = JavaScriptObfuscator.obfuscate(
63+
code,
64+
{
65+
...NO_ADDITIONAL_NODES_PRESET
66+
}
67+
).getObfuscatedCode();
6768

68-
const functionExpressionParamIdentifierMatch: RegExpMatchArray|null = obfuscatedCode
69-
.match(functionExpressionParamIdentifierRegExp);
70-
const functionParamIdentifierMatch: RegExpMatchArray|null = obfuscatedCode
71-
.match(functionParamIdentifierRegExp);
72-
const functionObjectIdentifierMatch: RegExpMatchArray|null = obfuscatedCode
73-
.match(functionObjectIdentifierRegExp);
69+
const functionExpressionParamIdentifierMatch: RegExpMatchArray|null = obfuscatedCode
70+
.match(functionExpressionParamIdentifierRegExp);
71+
const functionParamIdentifierMatch: RegExpMatchArray|null = obfuscatedCode
72+
.match(functionParamIdentifierRegExp);
73+
const functionObjectIdentifierMatch: RegExpMatchArray|null = obfuscatedCode
74+
.match(functionObjectIdentifierRegExp);
7475

75-
functionParamIdentifierName = (<RegExpMatchArray>functionParamIdentifierMatch)[1];
76-
functionExpressionParamIdentifierName = (<RegExpMatchArray>functionExpressionParamIdentifierMatch)[1];
77-
functionObjectIdentifierName = (<RegExpMatchArray>functionObjectIdentifierMatch)[1];
78-
});
76+
functionParamIdentifierName = (<RegExpMatchArray>functionParamIdentifierMatch)[1];
77+
functionExpressionParamIdentifierName = (<RegExpMatchArray>functionExpressionParamIdentifierMatch)[1];
78+
functionObjectIdentifierName = (<RegExpMatchArray>functionObjectIdentifierMatch)[1];
79+
});
7980

80-
it('should correctly transform function expression parameter identifier', () => {
81-
assert.match(obfuscatedCode, functionExpressionParamIdentifierRegExp);
82-
});
81+
it('should correctly transform function expression parameter identifier', () => {
82+
assert.match(obfuscatedCode, functionExpressionParamIdentifierRegExp);
83+
});
8384

84-
it('should correctly transform function parameter identifier', () => {
85-
assert.match(obfuscatedCode, functionParamIdentifierRegExp);
86-
});
85+
it('should correctly transform function parameter identifier', () => {
86+
assert.match(obfuscatedCode, functionParamIdentifierRegExp);
87+
});
8788

88-
it('should correctly transform function object parameter identifier', () => {
89-
assert.match(obfuscatedCode, functionObjectIdentifierRegExp);
90-
});
89+
it('should correctly transform function object parameter identifier', () => {
90+
assert.match(obfuscatedCode, functionObjectIdentifierRegExp);
91+
});
9192

92-
it('should generate same names for function parameter and function object identifiers', () => {
93-
assert.equal(functionParamIdentifierName, functionObjectIdentifierName);
94-
});
93+
it('should generate same names for function parameter and function object identifiers', () => {
94+
assert.equal(functionParamIdentifierName, functionObjectIdentifierName);
95+
});
96+
97+
it('should generate same names for function parameter identifiers', () => {
98+
assert.equal(functionExpressionParamIdentifierName, functionParamIdentifierName);
99+
});
95100

96-
it('should generate same names for function parameter identifiers', () => {
97-
assert.equal(functionExpressionParamIdentifierName, functionParamIdentifierName);
101+
it('should generate same names for function expression parameter and function object identifiers', () => {
102+
assert.equal(functionExpressionParamIdentifierName, functionObjectIdentifierName);
103+
});
98104
});
99105

100-
it('should generate same names for function expression parameter and function object identifiers', () => {
101-
assert.equal(functionExpressionParamIdentifierName, functionObjectIdentifierName);
106+
describe('Variant #2', () => {
107+
const functionIdentifiersRegExp: RegExp = /function *(_0x[a-f0-9]{4,6}) *\((_0x[a-f0-9]{4,6})\) *\{/;
108+
const functionObjectIdentifierRegExp: RegExp = /return new (_0x[a-f0-9]{4,6}) *\(\);/;
109+
110+
let obfuscatedCode: string,
111+
functionIdentifierName: string,
112+
functionParamIdentifierName: string,
113+
functionObjectIdentifierName: string;
114+
115+
before(() => {
116+
const code: string = readFileAsString(__dirname + '/fixtures/function-id-name-2.js');
117+
118+
obfuscatedCode = JavaScriptObfuscator.obfuscate(
119+
code,
120+
{
121+
...NO_ADDITIONAL_NODES_PRESET
122+
}
123+
).getObfuscatedCode();
124+
125+
const functionIdentifiersMatch: RegExpMatchArray|null = obfuscatedCode
126+
.match(functionIdentifiersRegExp);
127+
const functionObjectIdentifierMatch: RegExpMatchArray|null = obfuscatedCode
128+
.match(functionObjectIdentifierRegExp);
129+
130+
functionIdentifierName = (<RegExpMatchArray>functionIdentifiersMatch)[1];
131+
functionParamIdentifierName = (<RegExpMatchArray>functionIdentifiersMatch)[2];
132+
functionObjectIdentifierName = (<RegExpMatchArray>functionObjectIdentifierMatch)[1];
133+
});
134+
135+
it('should correctly transform function identifiers', () => {
136+
assert.match(obfuscatedCode, functionIdentifiersRegExp);
137+
});
138+
139+
it('should correctly transform function object parameter identifier', () => {
140+
assert.match(obfuscatedCode, functionObjectIdentifierRegExp);
141+
});
142+
143+
it('should generate same names for function parameter and function object identifiers', () => {
144+
assert.equal(functionIdentifierName, functionObjectIdentifierName);
145+
});
146+
147+
it('should generate same names for function id and parameter identifiers', () => {
148+
assert.equal(functionIdentifierName, functionParamIdentifierName);
149+
});
102150
});
103151
});
104152

@@ -356,27 +404,27 @@ describe('FunctionTransformer', () => {
356404
assert.match(obfuscatedCode, functionBodyRegExp);
357405
});
358406

359-
it('equal #1: shouldn\'t keep same names for variable declaration identifier and function parameters identifiers', () => {
360-
assert.notEqual(variableDeclarationIdentifierName, functionParameterIdentifierName);
407+
it('equal #1: should keep same names for variable declaration identifier and function parameters identifiers', () => {
408+
assert.equal(variableDeclarationIdentifierName, functionParameterIdentifierName);
361409
});
362410

363411
it('equal #2: shouldn\'t keep same names for variable declaration identifier and function parameters identifiers', () => {
364412
assert.notEqual(variableDeclarationIdentifierName, functionDefaultParameterIdentifierName1);
365413
});
366414

367-
it('equal #3: shouldn\'t keep same names for variable declaration identifier and function parameters identifiers', () => {
368-
assert.notEqual(variableDeclarationIdentifierName, functionDefaultParameterIdentifierName2);
415+
it('equal #3: should keep same names for variable declaration identifier and function parameters identifiers', () => {
416+
assert.equal(variableDeclarationIdentifierName, functionDefaultParameterIdentifierName2);
369417
});
370418

371-
it('should keep same names for identifier in first function parameter and default value identifier of second function parameter', () => {
419+
it('equal #4: should keep same names for identifier in first function parameter and default value identifier of second function parameter', () => {
372420
assert.equal(functionParameterIdentifierName, functionDefaultParameterIdentifierName2);
373421
});
374422

375-
it('equal #1: should keep same names for identifiers in function params and function body', () => {
423+
it('equal #5: should keep same names for identifiers in function params and function body', () => {
376424
assert.equal(functionParameterIdentifierName, functionBodyIdentifierName1);
377425
});
378426

379-
it('equal #2: should keep same names for identifiers in function params and function body', () => {
427+
it('equal #6: should keep same names for identifiers in function params and function body', () => {
380428
assert.equal(functionDefaultParameterIdentifierName1, functionBodyIdentifierName2);
381429
});
382430
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
(function () {
2+
function foo (foo) {}
3+
4+
return new foo();
5+
})();

0 commit comments

Comments
 (0)