Skip to content

Commit 52c413f

Browse files
committed
Fix #95
1 parent 484c5df commit 52c413f

File tree

4 files changed

+166
-58
lines changed

4 files changed

+166
-58
lines changed

src/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,6 @@ export const reservedIdentifiers = new Set([
7979
"eval",
8080
"arguments",
8181
]);
82+
83+
export const noRenameVariablePrefix = "__NO_JS_CONFUSER_RENAME__";
84+
export const placeholderVariablePrefix = "__p_";

src/transforms/identifier/renameVariables.ts

Lines changed: 120 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,14 @@ import {
88
isContext,
99
isLexContext,
1010
clone,
11+
isFunction,
1112
} from "../../util/insert";
1213
import Transform from "../transform";
13-
import { reservedIdentifiers } from "../../constants";
14+
import {
15+
noRenameVariablePrefix,
16+
placeholderVariablePrefix,
17+
reservedIdentifiers,
18+
} from "../../constants";
1419
import { ComputeProbabilityMap } from "../../probability";
1520
import VariableAnalysis from "./variableAnalysis";
1621

@@ -33,6 +38,9 @@ export default class RenameVariables extends Transform {
3338
// Ref to VariableAnalysis data
3439
variableAnalysis: VariableAnalysis;
3540

41+
// Option to re-use previously generated names
42+
reusePreviousNames = true;
43+
3644
constructor(o) {
3745
super(o, ObfuscateOrder.RenameVariables);
3846

@@ -45,10 +53,10 @@ export default class RenameVariables extends Transform {
4553
}
4654

4755
match(object: Node, parents: Node[]) {
48-
return isContext(object);
56+
return isContext(object) || object.type === "Identifier";
4957
}
5058

51-
transform(object: Node, parents: Node[]) {
59+
transformContext(object: Node, parents: Node[]) {
5260
// 2. Notice this is on 'onEnter' (top-down)
5361
var isGlobal = object.type == "Program";
5462
var type = isGlobal
@@ -76,7 +84,7 @@ export default class RenameVariables extends Transform {
7684
var possible = new Set<string>();
7785

7886
// 3. Try to re-use names when possible
79-
if (this.generated.length && !isGlobal) {
87+
if (this.reusePreviousNames && this.generated.length && !isGlobal) {
8088
var allReferences = new Set<string>();
8189
var nope = new Set(defined);
8290
walk(object, [], (o, p) => {
@@ -115,8 +123,8 @@ export default class RenameVariables extends Transform {
115123
// 4. Defined names to new names
116124
for (var name of defined) {
117125
if (
118-
!name.startsWith("__NO_JS_CONFUSER_RENAME__") && // Variables prefixed with '__NO_JS_CONFUSER_RENAME__' are never renamed
119-
(isGlobal && !name.startsWith("__p_") // Variables prefixed with '__p_' are created by the obfuscator, always renamed
126+
!name.startsWith(noRenameVariablePrefix) && // Variables prefixed with '__NO_JS_CONFUSER_RENAME__' are never renamed
127+
(isGlobal && !name.startsWith(placeholderVariablePrefix) // Variables prefixed with '__p_' are created by the obfuscator, always renamed
120128
? ComputeProbabilityMap(this.options.renameGlobals, (x) => x, name)
121129
: true) &&
122130
ComputeProbabilityMap(
@@ -151,71 +159,127 @@ export default class RenameVariables extends Transform {
151159
}
152160
}
153161

162+
// console.log(object.type, newNames);
154163
this.changed.set(object, newNames);
164+
}
155165

156-
// 5. Update Identifier node's 'name' property
157-
walk(object, parents, (o, p) => {
158-
if (o.type == "Identifier") {
159-
if (
160-
reservedIdentifiers.has(o.name) ||
161-
this.options.globalVariables.has(o.name)
162-
) {
163-
return;
164-
}
166+
transformIdentifier(object: Node, parents: Node[]) {
167+
const identifierName = object.name;
168+
if (
169+
reservedIdentifiers.has(identifierName) ||
170+
this.options.globalVariables.has(identifierName)
171+
) {
172+
return;
173+
}
165174

166-
if (o.$renamed) {
167-
return;
168-
}
175+
if (object.$renamed) {
176+
return;
177+
}
169178

170-
var info = getIdentifierInfo(o, p);
179+
var info = getIdentifierInfo(object, parents);
171180

172-
if (info.spec.isExported) {
173-
return;
174-
}
181+
if (info.spec.isExported) {
182+
return;
183+
}
175184

176-
if (!info.spec.isReferenced) {
177-
return;
178-
}
185+
if (!info.spec.isReferenced) {
186+
return;
187+
}
188+
189+
var contexts = [object, ...parents].filter((x) => isContext(x));
190+
var newName = null;
191+
192+
// Function default parameter check!
193+
var functionIndices = [];
194+
for (var i in parents) {
195+
if (isFunction(parents[i])) {
196+
functionIndices.push(i);
197+
}
198+
}
199+
200+
for (var functionIndex of functionIndices) {
201+
if (parents[functionIndex].id === object) {
202+
// This context is not referenced, so remove it
203+
contexts = contexts.filter(
204+
(context) => context != parents[functionIndex]
205+
);
206+
continue;
207+
}
208+
if (parents[functionIndex].params === parents[functionIndex - 1]) {
209+
var isReferencedHere = true;
179210

180-
var contexts = [o, ...p].filter((x) => isContext(x));
181-
var newName = null;
211+
var slicedParents = parents.slice(0, functionIndex);
212+
var forIndex = 0;
213+
for (var parent of slicedParents) {
214+
var childNode = slicedParents[forIndex - 1] || object;
182215

183-
for (var check of contexts) {
184216
if (
185-
this.variableAnalysis.defined.has(check) &&
186-
this.variableAnalysis.defined.get(check).has(o.name)
217+
parent.type === "AssignmentPattern" &&
218+
parent.right === childNode
187219
) {
188-
if (this.changed.has(check) && this.changed.get(check)[o.name]) {
189-
newName = this.changed.get(check)[o.name];
190-
break;
191-
}
220+
isReferencedHere = false;
221+
break;
192222
}
223+
224+
forIndex++;
193225
}
194226

195-
if (newName && typeof newName === "string") {
196-
// Strange behavior where the `local` and `imported` objects are the same
197-
if (info.isImportSpecifier) {
198-
var importSpecifierIndex = p.findIndex(
199-
(x) => x.type === "ImportSpecifier"
200-
);
201-
if (
202-
importSpecifierIndex != -1 &&
203-
p[importSpecifierIndex].imported ===
204-
(p[importSpecifierIndex - 1] || o) &&
205-
p[importSpecifierIndex].imported &&
206-
p[importSpecifierIndex].imported.type === "Identifier"
207-
) {
208-
p[importSpecifierIndex].imported = clone(
209-
p[importSpecifierIndex - 1] || o
210-
);
211-
}
212-
}
227+
if (!isReferencedHere) {
228+
// This context is not referenced, so remove it
229+
contexts = contexts.filter(
230+
(context) => context != parents[functionIndex]
231+
);
232+
}
233+
}
234+
}
213235

214-
// console.log(o.name, "->", newName);
215-
o.name = newName;
216-
o.$renamed = true;
236+
for (var check of contexts) {
237+
if (
238+
this.variableAnalysis.defined.has(check) &&
239+
this.variableAnalysis.defined.get(check).has(identifierName)
240+
) {
241+
if (
242+
this.changed.has(check) &&
243+
this.changed.get(check)[identifierName]
244+
) {
245+
newName = this.changed.get(check)[identifierName];
246+
break;
217247
}
218248
}
219-
});
249+
}
250+
251+
if (newName && typeof newName === "string") {
252+
// Strange behavior where the `local` and `imported` objects are the same
253+
if (info.isImportSpecifier) {
254+
var importSpecifierIndex = parents.findIndex(
255+
(x) => x.type === "ImportSpecifier"
256+
);
257+
if (
258+
importSpecifierIndex != -1 &&
259+
parents[importSpecifierIndex].imported ===
260+
(parents[importSpecifierIndex - 1] || object) &&
261+
parents[importSpecifierIndex].imported &&
262+
parents[importSpecifierIndex].imported.type === "Identifier"
263+
) {
264+
parents[importSpecifierIndex].imported = clone(
265+
parents[importSpecifierIndex - 1] || object
266+
);
267+
}
268+
}
269+
270+
// console.log(o.name, "->", newName);
271+
// 5. Update Identifier node's 'name' property
272+
object.name = newName;
273+
object.$renamed = true;
274+
}
275+
}
276+
277+
transform(object: Node, parents: Node[]) {
278+
var matchType = object.type === "Identifier" ? "Identifier" : "Context";
279+
if (matchType === "Identifier") {
280+
this.transformIdentifier(object, parents);
281+
} else {
282+
this.transformContext(object, parents);
283+
}
220284
}
221285
}

src/transforms/transform.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ import { ok } from "assert";
99
import Obfuscator from "../obfuscator";
1010
import { ObfuscateOptions } from "../options";
1111
import { ComputeProbabilityMap } from "../probability";
12-
import { reservedIdentifiers, reservedKeywords } from "../constants";
12+
import {
13+
placeholderVariablePrefix,
14+
reservedIdentifiers,
15+
reservedKeywords,
16+
} from "../constants";
1317
import { ObfuscateOrder } from "../order";
1418

1519
/**
@@ -176,7 +180,7 @@ export default class Transform {
176180
[...Array(size)]
177181
.map(() => Math.floor(Math.random() * 10).toString(10))
178182
.join("");
179-
return "__p_" + genRanHex(10);
183+
return placeholderVariablePrefix + genRanHex(10);
180184
}
181185

182186
/**

test/transforms/identifier/renameVariables.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,3 +582,40 @@ test("Variant #23: Re-use previously generated names", async () => {
582582

583583
expect(TEST_OUTPUT).toStrictEqual("Correct Value");
584584
});
585+
586+
test("Variant #24: Reference function name with parameter", async () => {
587+
var output = await JsConfuser(
588+
`
589+
function myFunction(myFunction){
590+
myFunction.property = "Correct Value";
591+
}
592+
593+
myFunction(myFunction);
594+
TEST_OUTPUT = myFunction.property;
595+
`,
596+
{ target: "node", renameVariables: true }
597+
);
598+
599+
var TEST_OUTPUT;
600+
eval(output);
601+
602+
expect(TEST_OUTPUT).toStrictEqual("Correct Value");
603+
});
604+
605+
test("Variant #25: Reference catch parameter", async () => {
606+
var output = await JsConfuser(
607+
`
608+
try {
609+
throw "Correct Value";
610+
} catch ( e ) {
611+
TEST_OUTPUT = e;
612+
}
613+
`,
614+
{ target: "node", renameVariables: true }
615+
);
616+
617+
var TEST_OUTPUT;
618+
eval(output);
619+
620+
expect(TEST_OUTPUT).toStrictEqual("Correct Value");
621+
});

0 commit comments

Comments
 (0)