Skip to content

Commit

Permalink
Fix and improve unwrapSimpleOperations (#124)
Browse files Browse the repository at this point in the history
* Add missing operations

* Handle update expressions as well

* Verify refs are call expressions before replacement

* Add tests for unary and update operations

* Remove completed TODO
  • Loading branch information
BenBaryoPX authored Nov 3, 2024
1 parent 6009a3e commit 394c221
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 16 deletions.
22 changes: 10 additions & 12 deletions src/modules/safe/unwrapSimpleOperations.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
const operators = ['+', '-', '*', '/', '%', '&', '|', '&&', '||', '**', '^',
'<=', '>=', '<', '>', '==', '===', '!=',
const operators = ['+', '-', '*', '/', '%', '&', '|', '&&', '||', '**', '^', '<=', '>=', '<', '>', '==', '===', '!=',
'!==', '<<', '>>', '>>>', 'in', 'instanceof', '??'];
const fixes = ['!', '~', '-', '+', '--', '++', 'typeof'];
const fixes = ['!', '~', '-', '+', 'typeof', 'void', 'delete', '--', '++']; // as in prefix and postfix operators

/**
* @param {ASTNode} n
Expand All @@ -21,10 +20,9 @@ function matchBinaryOrLogical(n) {
* @param {Arborist} arb
*/
function handleBinaryOrLogical(c, arb) {
// noinspection JSUnresolvedVariable
const refs = (c.scope.block?.id?.references || []).map(r => r.parentNode);
for (const ref of refs) {
if (ref.arguments.length === 2) arb.markNode(ref, {
if (ref.type === 'CallExpression' && ref.arguments.length === 2) arb.markNode(ref, {
type: c.type,
operator: c.operator,
left: ref.arguments[0],
Expand All @@ -37,8 +35,8 @@ function handleBinaryOrLogical(c, arb) {
* @param {ASTNode} n
* @return {boolean}
*/
function matchUnary(n) {
return n.type === 'UnaryExpression' &&
function matchUnaryOrUpdate(n) {
return ['UnaryExpression', 'UpdateExpression'].includes(n.type) &&
fixes.includes(n.operator) &&
n.parentNode.type === 'ReturnStatement' &&
n.parentNode.parentNode?.body?.length === 1 &&
Expand All @@ -49,10 +47,10 @@ function matchUnary(n) {
* @param {ASTNode} c
* @param {Arborist} arb
*/
function handleUnary(c, arb) {
function handleUnaryAndUpdate(c, arb) {
const refs = (c.scope.block?.id?.references || []).map(r => r.parentNode);
for (const ref of refs) {
if (ref.arguments.length === 1) arb.markNode(ref, {
if (ref.type === 'CallExpression' && ref.arguments.length === 1) arb.markNode(ref, {
type: c.type,
operator: c.operator,
prefix: c.prefix,
Expand All @@ -70,15 +68,15 @@ function handleUnary(c, arb) {
function unwrapSimpleOperations(arb, candidateFilter = () => true) {
for (let i = 0; i < arb.ast.length; i++) {
const n = arb.ast[i];
if ((matchBinaryOrLogical(n) || matchUnary(n)) &&
candidateFilter(n)) {
if ((matchBinaryOrLogical(n) || matchUnaryOrUpdate(n)) && candidateFilter(n)) {
switch (n.type) {
case 'BinaryExpression':
case 'LogicalExpression':
handleBinaryOrLogical(n, arb);
break;
case 'UnaryExpression':
handleUnary(n, arb);
case 'UpdateExpression':
handleUnaryAndUpdate(n, arb);
break;
}
}
Expand Down
25 changes: 24 additions & 1 deletion tests/modules.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ describe('SAFE: unwrapIIFEs', async () => {
});
describe('SAFE: unwrapSimpleOperations', async () => {
const targetModule = (await import('../src/modules/safe/unwrapSimpleOperations.js')).default;
it('TP-1', () => {
it('TP-1: Binary operations', () => {
const code = `function add(b,c){return b + c;}
function minus(b,c){return b - c;}
function mul(b,c){return b * c;}
Expand Down Expand Up @@ -623,6 +623,29 @@ typeof 1;
const result = applyModuleToCode(code, targetModule);
assert.strictEqual(result, expected);
});
it('TP-2 Unary operations', () => {
const code = `function unaryNegation(v) {return -v;}
function unaryPlus(v) {return +v;}
function logicalNot(v) {return !v;}
function bitwiseNot(v) {return ~v;}
function typeofOp(v) {return typeof v;}
function deleteOp(v) {return delete v;}
function voidOp(v) {return void v;}
(unaryNegation(1), unaryPlus(2), logicalNot(3), bitwiseNot(4), typeofOp(5), deleteOp(6), voidOp(7));
`;
const expected = `function unaryNegation(v) {\n return -v;\n}\nfunction unaryPlus(v) {\n return +v;\n}\nfunction logicalNot(v) {\n return !v;\n}\nfunction bitwiseNot(v) {\n return ~v;\n}\nfunction typeofOp(v) {\n return typeof v;\n}\nfunction deleteOp(v) {\n return delete v;\n}\nfunction voidOp(v) {\n return void v;\n}\n-1, +2, !3, ~4, typeof 5, delete 6, void 7;`;
const result = applyModuleToCode(code, targetModule);
assert.strictEqual(result, expected);
});
it('TP-3 Update operations', () => {
const code = `function incrementPre(v) {return ++v;}
function decrementPost(v) {return v--;}
(incrementPre(a), decrementPost(b));
`;
const expected = `function incrementPre(v) {\n return ++v;\n}\nfunction decrementPost(v) {\n return v--;\n}\n++a, b--;`;
const result = applyModuleToCode(code, targetModule);
assert.strictEqual(result, expected);
});
});
describe('SAFE: separateChainedDeclarators', async () => {
const targetModule = (await import('../src/modules/safe/separateChainedDeclarators.js')).default;
Expand Down
3 changes: 0 additions & 3 deletions tests/samples.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import {fileURLToPath} from 'node:url';
import {join} from 'node:path';
import {REstringer} from '../src/restringer.js';



function getDeobfuscatedCode(code) {
const restringer = new REstringer(code);
restringer.logger.setLogLevel(restringer.logger.logLevels.NONE);
Expand Down Expand Up @@ -105,5 +103,4 @@ describe('Samples tests', () => {
const result = getDeobfuscatedCode(code);
assert.strictEqual(result, expected);
});
it.todo('Fix issue with multiple comments in Local Proxies sample');
});

0 comments on commit 394c221

Please sign in to comment.