Skip to content

Commit

Permalink
Merge pull request #109 from MichaelXF/dev
Browse files Browse the repository at this point in the history
1.7.1
  • Loading branch information
MichaelXF authored Jul 23, 2023
2 parents e96f582 + 8ebc6a6 commit b1fcb1d
Show file tree
Hide file tree
Showing 16 changed files with 404 additions and 34 deletions.
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
# `1.7.1`
Updates

- Fixed [#107](https:/MichaelXF/js-confuser/issues/107)
- - RGF and Integrity clash issue fixed

- Fixed [#106](https:/MichaelXF/js-confuser/issues/106)
- - Object Extraction to properly handle `const` objects

- Fixed [#105](https:/MichaelXF/js-confuser/issues/105)
- - Duplicate Literals Removal updated to not cause this error

- Fixed [#103](https:/MichaelXF/js-confuser/issues/103)
- - Dispatcher will no longer apply to these types of functions to prevent this error

- Added documentation page for [ES5](https:/MichaelXF/js-confuser/blob/master/docs/ES5.md)

- Rollup Plugin created: https:/ayecue/rollup-js-confuser (Thanks @ayecue !)

# `1.7.0`
Updates

Expand Down
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -737,7 +737,6 @@ function iVQoGQD(...iVQoGQD){
stringSplitting: 0.75,

// Use at own risk
eval: false,
rgf: false
}
```
Expand Down Expand Up @@ -828,7 +827,6 @@ These features are experimental or a security concern.
```js
{
target: "node",
eval: true, // (security concern)
rgf: true, // (security concern)

// set to false for web-related scripts
Expand Down
2 changes: 1 addition & 1 deletion docs/ControlFlowFlattening.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ while (mJMdMhJ + A1Nyvv + xDwpOk6 != 83) {
}
```

As seen in the example, your code will bee wrapped in a large, complicated switch statement. The makes the behavior of your program very hard to understand and is resistent to deobfuscators. This comes with a large performance reduction.
As seen in the example, your code will be wrapped in a large, complicated switch statement. The makes the behavior of your program very hard to understand and is resistent to deobfuscators. This comes with a large performance reduction.

## Flattening Control Structures

Expand Down
197 changes: 197 additions & 0 deletions docs/ES5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
## `ES5`

The ES5 option converts most ES6+ features into ES5 compatible code.

Option name: `es5`

Option values: `true/false`

Note: Does not cover all cases such as Promises or Generator functions. Use [Babel](https://babel.dev/).

The ES5 option is intended to undo any ES6 feature the obfuscator adds to your code. If you input ES5 code, and enable the `es5` option, you can be guaranteed to have ES5 compatible output.

## Example

```js
// Input
function print(...messages){
console.log(...messages); // The spread operator (...)
// was introduced in ES6!
}

print("Hello", "World"); // "Hello World"

// Output
var __p_2580918143;
function print() {
var __p_7607361496;
var messages, __p_2591841272 = (__p_7607361496 = Array.prototype.slice.call(arguments), messages = __p_7607361496.slice(0));
(__p_2580918143 = console).log.apply(__p_2580918143, [].concat(Array.prototype.slice.call(messages)));
}
print('Hello', 'World'); // "Hello World"
```

## Polyfill Array Methods

When the ES5 option is enabled, array method polyfills will be injected to the top of your script.

```js
if (!Array.prototype.forEach) {
Array.prototype.forEach = function forEach(callback, thisArg) {
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
var array = this;
thisArg = thisArg || this;
for (var i = 0, l = array.length; i !== l; ++i) {
callback.call(thisArg, array[i], i, array);
}
};
}
```

## Destructuring

The ES5 option supports transpiling the destructuring patterns.

```js
// Input
var {userName, email} = { userName: "John", email: "[email protected]" };

// Output
var __p_7467473759;
var userName, email, __p_4755992742 = (__p_7467473759 = {
userName: 'John',
email: '[email protected]'
}, userName = __p_7467473759.userName, email = __p_7467473759.email);
```

## Spread Operator

The ES5 option supports transpiling the spread operator.

```js
// Input
array.push(...objects);

// Output
var __p_6344935930;
(__p_6344935930 = array).push.apply(__p_6344935930, [].concat(Array.prototype.slice.call(objects)));
```

## Template String

The ES5 option supports transpiling template strings.

```js
// Input
var myString = `Hello ${userName}`;

// Output
var myString = 'Hello ' + (userName + '');
```

## Object getters/setters

The ES5 option supports transpiling getter and setter methods.

```js
// Input
var _name;
var myObject = {
get name(){
return _name;
},
set name(newName){
_name = newName;
}
};

// Output
function __p_6886881506(base, computedProps, getters, setters) {
for (var i = 0; i < computedProps.length; i++) {
base[computedProps[i][0]] = computedProps[i][1];
}
var keys = Object.create(null);
Object.keys(getters).forEach(function (key) {
return keys[key] = 1;
});
Object.keys(setters).forEach(function (key) {
return keys[key] = 1;
});
Object.keys(keys).forEach(function (key) {
Object.defineProperty(base, key, {
set: setters[key],
get: getters[key],
configurable: true
});
});
return base;
}
var _name;
var myObject = __p_6886881506({}, [], {
'name': function () {
return _name;
}
}, {
'name': function (newName) {
_name = newName;
}
});
```

## Arrow Functions

The ES5 option converts arrow functions into regular functions.

```js
// Input
var print = message => console.log(message);

// Output
var print = function (message) {
return console.log(message);
};
```

## Const/Let

The ES5 option converts `const` and `let` to a regular `var` keyword.

```js
// Input
let myVar1 = true;
const myVar2 = "String";

// Output
var myVar1 = true;
var myVar2 = 'String';
```

## Classes

The ES5 option partially supports transpiling classes.

## Reserved Identifiers

The ES5 option will change any illegal uses of reserved identifiers.

```js
// Input
var myObject = {true: 1};
myObject.for = true;

// Output
var myObject = {"true": 1};
myObject["for"] = true;
```

## Features not supported

- Promises
- Async / Await
- Generator functions
- Nullish coalescing
- Optional chaining

Use [Babel](https://babel.dev/) to transpile these features. JS-Confuser will only support features the obfuscator may potentially add to your code.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "js-confuser",
"version": "1.7.0",
"version": "1.7.1",
"description": "JavaScript Obfuscation Tool.",
"main": "dist/index.js",
"types": "index.d.ts",
Expand All @@ -22,7 +22,7 @@
"author": "MichaelXF",
"license": "MIT",
"dependencies": {
"acorn": "^8.8.2",
"acorn": "^8.10.0",
"escodegen": "^2.0.0"
},
"devDependencies": {
Expand Down
10 changes: 10 additions & 0 deletions src/transforms/dispatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,16 @@ export default class Dispatcher extends Transform {
return "EXIT";
}
}

// Avoid functions with function expressions as they have a different scope
if (
(oo.type === "FunctionExpression" ||
oo.type === "ArrowFunctionExpression") &&
pp.find((x) => x == o.params)
) {
illegalFnNames.add(name);
return "EXIT";
}
});

functionDeclarations[name] = [o, p];
Expand Down
6 changes: 5 additions & 1 deletion src/transforms/extraction/duplicateLiteralsRemoval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { ComputeProbabilityMap } from "../../probability";
import { ok } from "assert";
import { chance, choice, getRandomInteger } from "../../util/random";
import { getBlock } from "../../traverse";
import { getIdentifierInfo } from "../../util/identifiers";

/**
* [Duplicate Literals Removal](https://docs.jscrambler.com/code-integrity/documentation/transformations/duplicate-literals-removal) replaces duplicate literals with a variable name.
Expand Down Expand Up @@ -221,7 +222,10 @@ export default class DuplicateLiteralsRemoval extends Transform {

transform(object: Node, parents: Node[]) {
return () => {
var value = object.value;
if (object.type === "Identifier") {
var info = getIdentifierInfo(object, parents);
if (info.isLabel || info.spec.isDefined) return;
}
if (object.regex) {
return;
}
Expand Down
4 changes: 4 additions & 0 deletions src/transforms/extraction/objectExtraction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,10 @@ export default class ObjectExtraction extends Transform {
...variableDeclarators
);

if (declaration.kind === "const") {
declaration.kind = "var";
}

// update all identifiers that pointed to the old object
objectDefChanges[name] &&
objectDefChanges[name].forEach((change) => {
Expand Down
9 changes: 9 additions & 0 deletions src/transforms/rgf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from "../util/gen";
import { getIdentifierInfo } from "../util/identifiers";
import { prepend, getDefiningContext } from "../util/insert";
import Integrity from "./lock/integrity";
import Transform from "./transform";

/**
Expand Down Expand Up @@ -200,6 +201,14 @@ export default class RGF extends Transform {

if (obfuscator.options.lock) {
delete obfuscator.options.lock.countermeasures;

// Integrity will not recursively apply to RGF'd functions. This is intended.
var lockTransform = obfuscator.transforms["Lock"];
if (lockTransform) {
lockTransform.before = lockTransform.before.filter(
(beforeTransform) => !(beforeTransform instanceof Integrity)
);
}
}

var transforms = obfuscator.array.filter(
Expand Down
9 changes: 6 additions & 3 deletions src/util/identifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,14 @@ export function getIdentifierInfo(object: Node, parents: Node[]) {
parents.find((x) => x.type == "VariableDeclaration") &&
objectPatternCheck(object, parents);

var functionIndex = parents.findIndex((x) => isFunction(x));

// Assignment pattern check!
if (isVariableDeclaration) {
var slicedParents = parents.slice(0, varIndex - 1);
var slicedParents = parents.slice(
0,
functionIndex != -1 ? Math.min(varIndex, functionIndex) : varIndex
);
var i = 0;
for (var parent of slicedParents) {
var childNode = slicedParents[i - 1] || object;
Expand All @@ -113,8 +118,6 @@ export function getIdentifierInfo(object: Node, parents: Node[]) {
forIndex != -1 &&
parents[forIndex].init == (parents[forIndex - 1] || object);

var functionIndex = parents.findIndex((x) => isFunction(x));

var isFunctionDeclaration =
functionIndex != -1 &&
parents[functionIndex].type == "FunctionDeclaration" &&
Expand Down
27 changes: 27 additions & 0 deletions test/transforms/dispatcher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -373,3 +373,30 @@ test("Variant #16: Don't change functions that use 'eval'", async () => {

expect(TEST_OUTPUT).toStrictEqual(2);
});

// https:/MichaelXF/js-confuser/issues/103
test("Variant #17: Don't break default parameter, function expression", async () => {
var output = await JsConfuser(
`
var X = "Correct Value";
function printX(
getX = function () {
return X;
}
) {
var X = "Incorrect Value";
TEST_OUTPUT = getX();
}
printX();
`,
{ target: "node", dispatcher: true }
);

var TEST_OUTPUT;
eval(output);

expect(TEST_OUTPUT).toStrictEqual("Correct Value");
});
Loading

0 comments on commit b1fcb1d

Please sign in to comment.