API Examples
Installation:
npm install @itsharper/webcrack
yarn add @itsharper/webcrack
pnpm add @itsharper/webcrack --allow-build=isolated-vm
Basic Usage
INFO
All examples are shown with ESM syntax. For CommonJS, use the following instead:
const { webcrack } = require('@itsharper/webcrack');
webcrack('const a = 1+1;').then((result) => {
console.log(result.code); // 'const a = 2;'
});
import { webcrack } from '@itsharper/webcrack';
const result = await webcrack('const a = 1+1;');
console.log(result.code); // 'const a = 2;'
Save the deobfuscated code and the unpacked bundle to the given directory:
import fs from 'fs';
import { webcrack } from '@itsharper/webcrack';
const code = fs.readFileSync('bundle.js', 'utf8');
const result = await webcrack(code);
await result.save('output-dir');
Get Bundle Info
const { bundle } = await webcrack(code);
bundle.type; // 'webpack' or 'browserify'
bundle.entryId; // '0'
bundle.modules; // Map(10) { '0' => Module { id: '0', ... }, 1 => ... }
const entry = bundle.modules.get(bundle.entryId);
entry.id; // '0'
entry.path; // './index.js'
entry.code; // 'const a = require("./1.js");'
Options
The default options are:
await webcrack(code, {
jsx: true, // Decompile react components to JSX
unpack: true, // Extract modules from the bundle
unminify: true, // Unminify the code
deobfuscate: true, // Deobfuscate the code
mangle: false, // Mangle variable names
plugins: {}, // Explained below
sandbox, // Explained below
});
Only mangle variable names that match a filter:
await webcrack(code, {
mangle: (id) => id.startsWith('_0x'),
});
Other options include:
mappings
: Themappings
option takes a function that receives an instance of @codemod/matchers, and returns an object that maps any matching nodes, to the path specified in the object key.
Browser Usage & Sandbox
The sandbox
option has to be passed when trying to deobfuscate string arrays in a browser. In future versions, this should hopefully not be necessary anymore.
It is an (optionally async) function that takes a code
parameter and returns the evaluated value.
Security warning
Simplest possible implementation, avoid using due to potentially executing malicious code
const result = await webcrack('function _0x317a(){....', { sandbox: eval });
More secure version with sandybox and CSP:
const sandbox = await Sandybox.create();
const iframe = document.querySelector('.sandybox');
iframe?.contentDocument?.head.insertAdjacentHTML(
'afterbegin',
`<meta http-equiv="Content-Security-Policy" content="default-src 'none';">`,
);
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
async function evalCode(code) {
const fn = await sandbox.addFunction(`() => ${code}`);
return Promise.race([
fn(),
sleep(10_000).then(() => Promise.reject(new Error('Sandbox timeout'))),
]).finally(() => sandbox.removeFunction(fn));
}
const result = await webcrack('function _0x317a(){....', { sandbox: evalCode });
Customize Paths
Useful for reverse-engineering and tracking changes across multiple versions of a bundle.
The mappings
option takes a function that receives an instance of @codemod/matchers, and returns an object that maps any matching nodes, to the path specified in the object key.
If a matching node in the AST of a module is found, it will be renamed to the given path.
- Path starting with
./
are relative to the output directory. - Otherwise, the path is treated as a node module.
const result = await webcrack(code, {
mappings: (m) => ({
'./utils/color.js': m.regExpLiteral('^#([0-9a-f]{3}){1,2}$'),
'lodash/index.js': m.memberExpression(
m.identifier('lodash'),
m.identifier('map'),
),
}),
});
await result.save('output-dir');
New folder structure:
├── index.js
├── utils
│ └── color.js
└── node_modules
└── lodash
└── index.js
See @codemod/matchers for more information about matchers.
Plugins
Experimental
This API is only available in the beta version and might change in future versions.
Webcrack's processing pipeline consists of six key stages:
- Parse: The input code is parsed into an Abstract Syntax Tree (AST).
- Prepare: Performs basic normalization, such as adding block statements.
- Deobfuscate
- Transpile and Unminify
- JSX and Unpack
- Generate: Converts the modified AST back into executable code.
You can extend or modify webcrack's behavior by hooking into its pipeline stages using plugins. Plugins allow you to manipulate the AST at specific stages of the pipeline.
Supported Stages
The plugins
option lets you specify an array of plugins for the following stages:
afterParse
afterPrepare
afterDeobfuscate
afterUnminify
afterUnpack
Plugins are executed sequentially in the order they are defined for each stage.
Writing Plugins
Refer to the Babel Plugin Handbook for a detailed guide on writing plugins.
Webcrack's plugin API is similar to Babel's but only the following utility libraries are provided to the plugin function:
Example Plugin
import { webcrack } from '@itsharper/webcrack';
function myPlugin({ types: t }) {
return {
pre() {
console.log('Running before traversal');
},
visitor: {
NumericLiteral(path) {
console.log('Found a number:', path.node.value);
path.replaceWith(t.stringLiteral('x'));
},
},
post() {
console.log('Running after traversal');
},
};
}
const result = await webcrack('1 + 1', {
plugins: {
afterParse: [myPlugin],
},
});
console.log(result.code); // '"xx"'
Using Babel plugins
It should be compatible with most Babel plugins as long as they only access the limited API specified above.
import removeConsole from 'babel-plugin-transform-remove-console';
import { webcrack } from '@itsharper/webcrack';
const result = await webcrack('consol.log(a), b()', {
plugins: {
afterUnminify: [removeConsole],
},
});
console.log(result.code); // 'b();'