Skip to content

Commit

Permalink
feat: inline simple string arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
j4k0xb committed Aug 14, 2023
1 parent c90c5e3 commit 6e40f6b
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 0 deletions.
19 changes: 19 additions & 0 deletions src/deobfuscator/stringArray.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import traverse, { NodePath } from '@babel/traverse';
import * as t from '@babel/types';
import * as m from '@codemod/matchers';
import { inlineArray } from '../utils/inline';
import { isReadonlyObject } from '../utils/matcher';
import { renameFast } from '../utils/rename';

export interface StringArray {
Expand Down Expand Up @@ -53,6 +55,7 @@ export function findStringArray(ast: t.Node): StringArray | undefined {
);

traverse(ast, {
// Wrapped string array from later javascript-obfuscator versions
FunctionDeclaration(path) {
if (matcher.match(path.node)) {
const length = arrayExpression.current!.elements.length;
Expand All @@ -69,6 +72,22 @@ export function findStringArray(ast: t.Node): StringArray | undefined {
path.stop();
}
},
// Simple string array inlining (only `array[0]`, `array[1]` etc references, no rotating/decoding).
// May be used by older or different obfuscators
VariableDeclaration(path) {
if (!variableDeclaration.match(path.node)) return;

const length = arrayExpression.current!.elements.length;
const binding = path.scope.getBinding(arrayIdentifier.current!.name)!;
const memberAccess = m.memberExpression(
m.fromCapture(arrayIdentifier),
m.numericLiteral(m.matcher(value => value < length))
);
if (!isReadonlyObject(binding, memberAccess)) return;

inlineArray(arrayExpression.current!, binding.referencePaths);
path.remove();
},
});

return result;
Expand Down
18 changes: 18 additions & 0 deletions src/utils/inline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,24 @@ import * as t from '@babel/types';
import * as m from '@codemod/matchers';
import { findParent } from './matcher';

/**
* Make sure the array is immutable and references are valid before using!
*
* Example:
* `const arr = ["foo", "bar"]; console.log(arr[0]);` -> `console.log("foo");`
*/
export function inlineArray(
array: t.ArrayExpression,
references: NodePath[]
): void {
for (const reference of references) {
const memberPath = reference.parentPath! as NodePath<t.MemberExpression>;
const property = memberPath.node.property as t.NumericLiteral;
const index = property.value;
const replacement = array.elements[index]!;
memberPath.replaceWith(replacement);
}
}
/**
* Inline function used in control flow flattening (that only returns an expression)
* Example:
Expand Down
2 changes: 2 additions & 0 deletions test/__snapshots__/deobfuscator.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ exports[`deobfuscate obfuscator.io-rotator-unary.js 1`] = `
hi();"
`;

exports[`deobfuscate simple-string-array.js 1`] = `"console.log(\\"Hello, World!\\");"`;

exports[`inline decoder > inline function 1`] = `
"function decoder() {}
decoder(1);
Expand Down
1 change: 1 addition & 0 deletions test/deobfuscator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ test.each([
'obfuscator.io-control-flow.js',
'obfuscator.io-control-flow-keys.js',
'obfuscator.io-high.js',
'simple-string-array.js',
])('deobfuscate %s', async filename => {
const result = await webcrack(
await readFile(join('test', 'samples', filename), 'utf8')
Expand Down
2 changes: 2 additions & 0 deletions test/samples/simple-string-array.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const arr = ['log', 'Hello, World!'];
console[arr[0]](arr[1]);

0 comments on commit 6e40f6b

Please sign in to comment.