mirror of
https://github.com/javascript-obfuscator/javascript-obfuscator
synced 2026-08-09 12:42:29 +00:00
Merge pull request #196 from javascript-obfuscator/dead-code-injection-improvements
Dead code injection improvements
This commit is contained in:
@@ -4,3 +4,4 @@
|
||||
coverage
|
||||
images
|
||||
test/fixtures/compile-performance.js
|
||||
test-tmp
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
Change Log
|
||||
===
|
||||
v0.14.3
|
||||
---
|
||||
* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/195
|
||||
|
||||
v0.14.2
|
||||
---
|
||||
* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/181
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "javascript-obfuscator",
|
||||
"version": "0.14.2",
|
||||
"version": "0.14.3",
|
||||
"description": "JavaScript obfuscator",
|
||||
"keywords": [
|
||||
"obfuscator",
|
||||
|
||||
+72
-17
@@ -6,6 +6,7 @@ import * as ESTree from 'estree';
|
||||
|
||||
import { TDeadNodeInjectionCustomNodeFactory } from '../../types/container/custom-nodes/TDeadNodeInjectionCustomNodeFactory';
|
||||
import { TNodeWithBlockScope } from '../../types/node/TNodeWithBlockScope';
|
||||
import { TNodeWithScope } from '../../types/node/TNodeWithScope';
|
||||
|
||||
import { ICustomNode } from '../../interfaces/custom-nodes/ICustomNode';
|
||||
import { IOptions } from '../../interfaces/options/IOptions';
|
||||
@@ -97,15 +98,62 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Node} blockStatementNode
|
||||
* @param {Node} targetNode
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private static isValidBlockStatementNode (blockStatementNode: ESTree.Node): boolean {
|
||||
const isProhibitedNode: (node: ESTree.Node) => boolean =
|
||||
(node: ESTree.Node): boolean => NodeGuards.isBreakStatementNode(node) ||
|
||||
NodeGuards.isContinueStatementNode(node) ||
|
||||
NodeGuards.isAwaitExpressionNode(node) ||
|
||||
NodeGuards.isSuperNode(node);
|
||||
private static isProhibitedNodeInsideCollectedBlockStatement (targetNode: ESTree.Node): boolean {
|
||||
return NodeGuards.isBreakStatementNode(targetNode)
|
||||
|| NodeGuards.isContinueStatementNode(targetNode)
|
||||
|| NodeGuards.isAwaitExpressionNode(targetNode)
|
||||
|| NodeGuards.isSuperNode(targetNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Node} targetNode
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private static isScopeHoistingFunctionDeclaration (targetNode: ESTree.Node): boolean {
|
||||
if (!NodeGuards.isFunctionDeclarationNode(targetNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const scopeNode: TNodeWithScope = NodeUtils.getScopeOfNode(targetNode);
|
||||
const scopeBody: ESTree.Statement[] = !NodeGuards.isSwitchCaseNode(scopeNode)
|
||||
? <ESTree.Statement[]>scopeNode.body
|
||||
: scopeNode.consequent;
|
||||
const indexInScope: number = scopeBody.indexOf(targetNode);
|
||||
|
||||
if (indexInScope === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const slicedBody: ESTree.Statement[] = scopeBody.slice(0, indexInScope);
|
||||
const hostBlockStatementNode: ESTree.BlockStatement = Nodes.getBlockStatementNode(slicedBody);
|
||||
const functionDeclarationName: string = targetNode.id.name;
|
||||
|
||||
let isScopeHoistedFunctionDeclaration: boolean = false;
|
||||
|
||||
estraverse.traverse(hostBlockStatementNode, {
|
||||
enter: (node: ESTree.Node): estraverse.VisitorOption | void => {
|
||||
if (NodeGuards.isIdentifierNode(node) && node.name === functionDeclarationName) {
|
||||
isScopeHoistedFunctionDeclaration = true;
|
||||
|
||||
return estraverse.VisitorOption.Break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return isScopeHoistedFunctionDeclaration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {BlockStatement} blockStatementNode
|
||||
* @returns {boolean}
|
||||
*/
|
||||
private static isValidCollectedBlockStatementNode (blockStatementNode: ESTree.BlockStatement): boolean {
|
||||
if (!blockStatementNode.body.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let nestedBlockStatementsCount: number = 0;
|
||||
let isValidBlockStatementNode: boolean = true;
|
||||
@@ -117,8 +165,9 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
|
||||
}
|
||||
|
||||
if (
|
||||
nestedBlockStatementsCount > DeadCodeInjectionTransformer.maxNestedBlockStatementsCount ||
|
||||
isProhibitedNode(node)
|
||||
nestedBlockStatementsCount > DeadCodeInjectionTransformer.maxNestedBlockStatementsCount
|
||||
|| DeadCodeInjectionTransformer.isProhibitedNodeInsideCollectedBlockStatement(node)
|
||||
|| DeadCodeInjectionTransformer.isScopeHoistingFunctionDeclaration(node)
|
||||
) {
|
||||
isValidBlockStatementNode = false;
|
||||
|
||||
@@ -183,7 +232,7 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
|
||||
|
||||
let clonedBlockStatementNode: ESTree.BlockStatement = NodeUtils.clone(node);
|
||||
|
||||
if (!DeadCodeInjectionTransformer.isValidBlockStatementNode(clonedBlockStatementNode)) {
|
||||
if (!DeadCodeInjectionTransformer.isValidCollectedBlockStatementNode(clonedBlockStatementNode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -209,16 +258,21 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
|
||||
* @param {NodeGuards} parentNode
|
||||
* @returns {NodeGuards | VisitorOption}
|
||||
*/
|
||||
public transformNode (blockStatementNode: ESTree.BlockStatement, parentNode: ESTree.Node): ESTree.Node | estraverse.VisitorOption {
|
||||
if (this.collectedBlockStatementsTotalLength < DeadCodeInjectionTransformer.minCollectedBlockStatementsCount) {
|
||||
public transformNode (
|
||||
blockStatementNode: ESTree.BlockStatement,
|
||||
parentNode: ESTree.Node
|
||||
): ESTree.Node | estraverse.VisitorOption {
|
||||
const canBreakTraverse: boolean = !this.collectedBlockStatements.length
|
||||
|| this.collectedBlockStatementsTotalLength < DeadCodeInjectionTransformer.minCollectedBlockStatementsCount;
|
||||
|
||||
if (canBreakTraverse) {
|
||||
return estraverse.VisitorOption.Break;
|
||||
}
|
||||
|
||||
if (!this.collectedBlockStatements.length) {
|
||||
return estraverse.VisitorOption.Break;
|
||||
}
|
||||
const isInvalidBlockStatementNode: boolean = !blockStatementNode.body.length
|
||||
|| this.randomGenerator.getMathRandom() > this.options.deadCodeInjectionThreshold;
|
||||
|
||||
if (this.randomGenerator.getMathRandom() > this.options.deadCodeInjectionThreshold) {
|
||||
if (isInvalidBlockStatementNode) {
|
||||
return blockStatementNode;
|
||||
}
|
||||
|
||||
@@ -233,8 +287,9 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
|
||||
const maxInteger: number = this.collectedBlockStatements.length - 1;
|
||||
const randomIndex: number = this.randomGenerator.getRandomInteger(minInteger, maxInteger);
|
||||
const randomBlockStatementNode: ESTree.BlockStatement = this.collectedBlockStatements.splice(randomIndex, 1)[0];
|
||||
const isDuplicateBlockStatementNodes: boolean = randomBlockStatementNode === blockStatementNode;
|
||||
|
||||
if (randomBlockStatementNode === blockStatementNode) {
|
||||
if (isDuplicateBlockStatementNodes) {
|
||||
return blockStatementNode;
|
||||
}
|
||||
|
||||
|
||||
+24
-6
@@ -6,18 +6,36 @@ import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNo
|
||||
|
||||
let obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
|
||||
`
|
||||
var bar = 1;
|
||||
var baz = 2;
|
||||
(function(){
|
||||
var bark = bar + baz;
|
||||
function foo () {
|
||||
function inner1 () {}
|
||||
inner1();
|
||||
}
|
||||
function bar () {
|
||||
function inner2 () {}
|
||||
inner2();
|
||||
}
|
||||
function baz () {
|
||||
function inner3 () {}
|
||||
inner3();
|
||||
}
|
||||
function bark () {
|
||||
function inner4 () {}
|
||||
inner4();
|
||||
}
|
||||
function hawk () {
|
||||
function inner5 () {}
|
||||
inner5();
|
||||
}
|
||||
})();
|
||||
`,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET,
|
||||
compact: false,
|
||||
identifiersPrefix: 'foo',
|
||||
identifierNamesGenerator: 'mangled',
|
||||
renameGlobals: true
|
||||
stringArray: true,
|
||||
stringArrayThreshold: 1,
|
||||
deadCodeInjection: true,
|
||||
deadCodeInjectionThreshold: 1
|
||||
}
|
||||
).getObfuscatedCode();
|
||||
|
||||
|
||||
+72
@@ -481,5 +481,77 @@ describe('DeadCodeInjectionTransformer', () => {
|
||||
assert.notEqual(returnIdentifierName, variableDeclarationIdentifierName);
|
||||
});
|
||||
});
|
||||
|
||||
describe('variant #11 - block statements with empty body', () => {
|
||||
const regExp: RegExp = new RegExp(
|
||||
`function *${variableMatch} *\\(\\) *{ *} *` +
|
||||
`${variableMatch} *\\(\\); *`,
|
||||
'g'
|
||||
);
|
||||
const expectedMatchesLength: number = 5;
|
||||
|
||||
let matchesLength: number = 0;
|
||||
|
||||
before(() => {
|
||||
const code: string = readFileAsString(__dirname + '/fixtures/block-statement-empty-body.js');
|
||||
const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
|
||||
code,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET,
|
||||
stringArray: true,
|
||||
stringArrayThreshold: 1,
|
||||
deadCodeInjection: true,
|
||||
deadCodeInjectionThreshold: 1
|
||||
}
|
||||
);
|
||||
|
||||
const obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
|
||||
const functionMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(regExp);
|
||||
|
||||
if (functionMatches) {
|
||||
matchesLength = functionMatches.length;
|
||||
}
|
||||
});
|
||||
|
||||
it('shouldn\'t add dead code conditions to the block empty block statements', () => {
|
||||
assert.isAtLeast(matchesLength, expectedMatchesLength);
|
||||
});
|
||||
});
|
||||
|
||||
describe('variant #12 - block statement with scope-hoisting', () => {
|
||||
const regExp: RegExp = new RegExp(
|
||||
`${variableMatch} *\\(\\); *` +
|
||||
`var *${variableMatch} *= *0x2; *` +
|
||||
`function *${variableMatch} *\\(\\) *{ *} *`,
|
||||
'g'
|
||||
);
|
||||
const expectedMatchesLength: number = 5;
|
||||
|
||||
let matchesLength: number = 0;
|
||||
|
||||
before(() => {
|
||||
const code: string = readFileAsString(__dirname + '/fixtures/block-statement-with-scope-hoisting.js');
|
||||
const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
|
||||
code,
|
||||
{
|
||||
...NO_ADDITIONAL_NODES_PRESET,
|
||||
stringArray: true,
|
||||
stringArrayThreshold: 1,
|
||||
deadCodeInjection: true,
|
||||
deadCodeInjectionThreshold: 1
|
||||
}
|
||||
);
|
||||
|
||||
const obfuscatedCode: string = obfuscationResult.getObfuscatedCode();
|
||||
const functionMatches: RegExpMatchArray = <RegExpMatchArray>obfuscatedCode.match(regExp);
|
||||
|
||||
if (functionMatches) {
|
||||
matchesLength = functionMatches.length;
|
||||
} });
|
||||
|
||||
it('shouldn\'t collect block statements with scope-hoisting', () => {
|
||||
assert.equal(matchesLength, expectedMatchesLength);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
(function(){
|
||||
function foo () {
|
||||
function inner1 () {}
|
||||
inner1();
|
||||
}
|
||||
function bar () {
|
||||
function inner2 () {}
|
||||
inner2();
|
||||
}
|
||||
function baz () {
|
||||
function inner3 () {}
|
||||
inner3();
|
||||
}
|
||||
function bark () {
|
||||
function inner4 () {}
|
||||
inner4();
|
||||
}
|
||||
function hawk () {
|
||||
function inner5 () {}
|
||||
inner5();
|
||||
}
|
||||
})();
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
(function(){
|
||||
function foo () {
|
||||
var a = 1;
|
||||
inner1();
|
||||
var b = 2;
|
||||
function inner1 () {}
|
||||
var c = 3;
|
||||
}
|
||||
function bar () {
|
||||
var a = 1;
|
||||
inner2();
|
||||
var b = 2;
|
||||
function inner2 () {}
|
||||
var c = 3;
|
||||
}
|
||||
function baz () {
|
||||
var a = 1;
|
||||
inner3();
|
||||
var b = 2;
|
||||
function inner3 () {}
|
||||
var c = 3;
|
||||
}
|
||||
function bark () {
|
||||
var a = 1;
|
||||
inner4();
|
||||
var b = 2;
|
||||
function inner4 () {}
|
||||
var c = 3;
|
||||
}
|
||||
function hawk () {
|
||||
var a = 1;
|
||||
inner5();
|
||||
var b = 2;
|
||||
function inner5 () {}
|
||||
var c = 3;
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user