Skip to content

Commit

Permalink
feat: detect arrow-function iife (#127)
Browse files Browse the repository at this point in the history
  • Loading branch information
j4k0xb committed Nov 30, 2024
1 parent e044573 commit 6d8c7f7
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 114 deletions.
33 changes: 25 additions & 8 deletions packages/webcrack/src/ast-utils/matcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,34 @@ export function constObjectProperty(
);
}

export function matchIife(
body?: m.Matcher<t.Statement[]> | m.Matcher<t.Statement>[],
): m.Matcher<t.CallExpression> {
return m.callExpression(
m.functionExpression(null, [], body ? m.blockStatement(body) : undefined),
[],
export function anonymousFunction(
params?:
| m.Matcher<(t.Identifier | t.RestElement | t.Pattern)[]>
| (
| m.Matcher<t.Identifier>
| m.Matcher<t.Pattern>
| m.Matcher<t.RestElement>
)[],
body?: m.Matcher<t.BlockStatement>,
): m.Matcher<t.FunctionExpression | t.ArrowFunctionExpression> {
return m.or(
m.functionExpression(null, params, body, false),
m.arrowFunctionExpression(params, body),
);
}

export const iife = matchIife();
export const emptyIife = matchIife([]);
export function iife(
params?:
| m.Matcher<(t.Identifier | t.RestElement | t.Pattern)[]>
| (
| m.Matcher<t.Identifier>
| m.Matcher<t.Pattern>
| m.Matcher<t.RestElement>
)[],
body?: m.Matcher<t.BlockStatement>,
): m.Matcher<t.CallExpression> {
return m.callExpression(anonymousFunction(params, body));
}

/**
* Matches both identifier properties and string literal computed properties
Expand Down
52 changes: 27 additions & 25 deletions packages/webcrack/src/deobfuscate/array-rotator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import type { NodePath } from '@babel/traverse';
import type * as t from '@babel/types';
import * as m from '@codemod/matchers';
import { callExpression } from '@codemod/matchers';
import { constMemberExpression, findParent, infiniteLoop } from '../ast-utils';
import {
constMemberExpression,
findParent,
iife,
infiniteLoop,
} from '../ast-utils';
import type { StringArray } from './string-array';

export type ArrayRotator = NodePath<t.ExpressionStatement>;
Expand Down Expand Up @@ -35,30 +40,27 @@ export function findArrayRotator(
],
);

const callMatcher = m.callExpression(
m.functionExpression(
null,
m.anything(),
m.blockStatement(
m.anyList(
m.zeroOrMore(),
infiniteLoop(
m.matcher((node) => {
return (
m
.containerOf(callExpression(m.identifier('parseInt')))
.match(node) &&
m
.blockStatement([
m.tryStatement(
m.containerOf(pushShift),
m.containerOf(pushShift),
),
])
.match(node)
);
}),
),
const callMatcher = iife(
m.anything(),
m.blockStatement(
m.anyList(
m.zeroOrMore(),
infiniteLoop(
m.matcher((node) => {
return (
m
.containerOf(callExpression(m.identifier('parseInt')))
.match(node) &&
m
.blockStatement([
m.tryStatement(
m.containerOf(pushShift),
m.containerOf(pushShift),
),
])
.match(node)
);
}),
),
),
),
Expand Down
2 changes: 1 addition & 1 deletion packages/webcrack/src/deobfuscate/debug-protection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export default {

binding?.referencePaths.forEach((ref) => {
if (intervalCall.match(ref.parent)) {
findParent(ref, iife)?.remove();
findParent(ref, iife())?.remove();
}
});

Expand Down
138 changes: 71 additions & 67 deletions packages/webcrack/src/deobfuscate/self-defending.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ import * as m from '@codemod/matchers';
import type { Transform } from '../ast-utils';
import {
constMemberExpression,
emptyIife,
falseMatcher,
findParent,
matchIife,
iife,
trueMatcher,
} from '../ast-utils';

Expand All @@ -34,82 +33,87 @@ export default {
// const callControllerFunctionName = (function() { ... })();
const matcher = m.variableDeclarator(
m.identifier(callController),
matchIife([
// let firstCall = true;
m.variableDeclaration(undefined, [
m.variableDeclarator(firstCall, trueMatcher),
]),
// return function (context, fn) {
m.returnStatement(
m.functionExpression(
null,
[context, fn],
m.blockStatement([
m.variableDeclaration(undefined, [
// const rfn = firstCall ? function() {
m.variableDeclarator(
rfn,
m.conditionalExpression(
m.fromCapture(firstCall),
m.functionExpression(
null,
[],
m.blockStatement([
// if (fn) {
m.ifStatement(
m.fromCapture(fn),
m.blockStatement([
// const res = fn.apply(context, arguments);
m.variableDeclaration(undefined, [
m.variableDeclarator(
res,
m.callExpression(
constMemberExpression(
m.fromCapture(fn),
'apply',
iife(
[],
m.blockStatement([
// let firstCall = true;
m.variableDeclaration(undefined, [
m.variableDeclarator(firstCall, trueMatcher),
]),
// return function (context, fn) {
m.returnStatement(
m.functionExpression(
null,
[context, fn],
m.blockStatement([
m.variableDeclaration(undefined, [
// const rfn = firstCall ? function() {
m.variableDeclarator(
rfn,
m.conditionalExpression(
m.fromCapture(firstCall),
m.functionExpression(
null,
[],
m.blockStatement([
// if (fn) {
m.ifStatement(
m.fromCapture(fn),
m.blockStatement([
// const res = fn.apply(context, arguments);
m.variableDeclaration(undefined, [
m.variableDeclarator(
res,
m.callExpression(
constMemberExpression(
m.fromCapture(fn),
'apply',
),
[
m.fromCapture(context),
m.identifier('arguments'),
],
),
[
m.fromCapture(context),
m.identifier('arguments'),
],
),
]),
// fn = null;
m.expressionStatement(
m.assignmentExpression(
'=',
m.fromCapture(fn),
m.nullLiteral(),
),
),
// return res;
m.returnStatement(m.fromCapture(res)),
]),
// fn = null;
m.expressionStatement(
m.assignmentExpression(
'=',
m.fromCapture(fn),
m.nullLiteral(),
),
),
// return res;
m.returnStatement(m.fromCapture(res)),
]),
),
]),
),
]),
),
// : function() {}
m.functionExpression(null, [], m.blockStatement([])),
),
// : function() {}
m.functionExpression(null, [], m.blockStatement([])),
),
]),
// firstCall = false;
m.expressionStatement(
m.assignmentExpression(
'=',
m.fromCapture(firstCall),
falseMatcher,
),
),
// return rfn;
m.returnStatement(m.fromCapture(rfn)),
]),
// firstCall = false;
m.expressionStatement(
m.assignmentExpression(
'=',
m.fromCapture(firstCall),
falseMatcher,
),
),
// return rfn;
m.returnStatement(m.fromCapture(rfn)),
]),
),
),
),
]),
]),
),
);

const emptyIife = iife([], m.blockStatement([]));

return {
VariableDeclarator(path) {
if (!matcher.match(path.node)) return;
Expand Down
24 changes: 11 additions & 13 deletions packages/webcrack/src/unpack/browserify/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@ import type { NodePath } from '@babel/traverse';
import * as t from '@babel/types';
import * as m from '@codemod/matchers';
import type { Transform } from '../../ast-utils';
import {
constKey,
getPropName,
matchIife,
renameParameters,
} from '../../ast-utils';
import { constKey, getPropName, iife, renameParameters } from '../../ast-utils';
import type { Bundle } from '../bundle';
import { resolveDependencyTree } from '../path';
import { BrowserifyBundle } from './bundle';
Expand Down Expand Up @@ -55,14 +50,17 @@ export const unpackBrowserify = {
m.identifier(),
]),
// (function () { function init(files, cache, entryIds) {...} return init; })()(...)
matchIife([
m.functionDeclaration(undefined, [
m.identifier(),
m.identifier(),
m.identifier(),
iife(
[],
m.blockStatement([
m.functionDeclaration(undefined, [
m.identifier(),
m.identifier(),
m.identifier(),
]),
m.returnStatement(m.identifier()),
]),
m.returnStatement(m.identifier()),
]),
),
),
[
m.objectExpression(files),
Expand Down

0 comments on commit 6d8c7f7

Please sign in to comment.