npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

restringer

v2.0.4

Published

Deobfuscate Javascript with emphasis on reconstructing strings

Downloads

1,158

Readme

Restringer

Node.js CI Downloads

Deobfuscate Javascript and reconstruct strings. Simplify cumbersome logic where possible while adhering to scope limitations.

Try it online @ restringer.tech.

For comments and suggestions feel free to open an issue or find me on Twitter - @ctrl__esc

Table of Contents


Installation

npm

npm install -g restringer

Clone The Repo

Requires Node 16 or newer.

git clone [email protected]:PerimeterX/restringer.git
cd restringer
npm install

Usage

The restringer.js uses generic deobfuscation methods that reconstruct and restore obfuscated strings and simplifies redundant logic meant only to encumber. REstringer employs the Obfuscation Detector to identify specific types of obfuscation for which there's a need to apply specific deobfuscation methods in order to circumvent anti-debugging mechanisms or other code traps preventing the script from being deobfuscated.

Command-Line Usage

Usage: restringer input_filename [-h] [-c] [-q | -v] [-m M] [-o [output_filename]]

positional arguments:
	input_filename                  The obfuscated JS file

optional arguments:
	-h, --help                      Show this help message and exit.
	-c, --clean                     Remove dead nodes from script after deobfuscation is complete (unsafe).
	-q, --quiet                     Suppress output to stdout. Output result only to stdout if the -o option is not set.
									Does not go with the -v option.
	-m, --max-iterations M          Run at most M iterations
	-v, --verbose                   Show more debug messages while deobfuscating. Does not go with the -q option.
	-o, --output [output_filename]  Write deobfuscated script to output_filename. 
									<input_filename>-deob.js is used if no filename is provided.

Examples:

  • Print the deobfuscated script to stdout.
     restringer [target-file.js]
  • Save the deobfuscated script to output.js.
     restringer [target-file.js] -o output.js
  • Deobfuscate and print debug info.
     restringer [target-file.js] -v
  • Deobfuscate without printing anything but the deobfuscated output.
     restringer [target-file.js] -q

Use as a Module

import {REstringer} from 'restringer';

const restringer = new REstringer('"RE" + "stringer"');
if (restringer.deobfuscate()) {
  console.log(restringer.script);
} else {
  console.log('Nothing was deobfuscated :/');
}
// Output: 'REstringer';

Create Custom Deobfuscators

REstringer is highly modularized. It exposes modules that allow creating custom deobfuscators that can solve specific problems.

The basic structure of such a deobfuscator would be an array of deobfuscation modules (either safe or unsafe), run via flAST's applyIteratively utility function.

Unsafe modules run code through eval (using isolated-vm to be on the safe side) while safe modules do not.

import {applyIteratively} from 'flast';
import {safe, unsafe} from 'restringer';
const {normalizeComputed} = safe;
const {resolveDefiniteBinaryExpressions, resolveLocalCalls} = unsafe;
let script = 'obfuscated JS here';
const deobModules = [
  resolveDefiniteBinaryExpressions,
  resolveLocalCalls,
  normalizeComputed,
];
script = applyIteratively(script, deobModules);
console.log(script); // Deobfuscated script

With the additional candidateFilter function argument, it's possible to narrow down the targeted nodes:

import {unsafe} from 'restringer';
const {resolveLocalCalls} = unsafe;
import {applyIteratively} from 'flast';
let script = 'obfuscated JS here';

// It's better to define a function with a meaningful name that can show up in the log 
function resolveLocalCallsInGlobalScope(arb) {
  return resolveLocalCalls(arb, n => n.parentNode?.type === 'Program');
}
script = applyIteratively(script, [resolveLocalCallsInGlobalScope]);
console.log(script); // Deobfuscated script

You can also customize any deobfuscation method while still using REstringer without running the loop yourself:

import fs from 'node:fs';
import {REstringer} from 'restringer';

const inputFilename = process.argv[2];
const code = fs.readFileSync(inputFilename, 'utf-8');
const res = new REstringer(code);

// res.logger.setLogLevelDebug();
res.detectObfuscationType = false;  // Skip obfuscation type detection, including any pre and post processors

const targetFunc = res.unsafeMethods.find(m => m.name === 'resolveLocalCalls');
let changes = 0;		// Resolve only the first 5 calls
res.safeMethods[res.unsafeMethods.indexOf(targetFunc)] = function customResolveLocalCalls(n) {return targetFunc(n, () => changes++ < 5)}

res.deobfuscate();

if (res.script !== code) {
  console.log('[+] Deob successful');
  fs.writeFileSync(`${inputFilename}-deob.js`, res.script, 'utf-8');
} else console.log('[-] Nothing deobfuscated :/');

Boilerplate code for starting from scratch

import {applyIteratively, logger} from 'flast';
// Optional loading from file
// import fs from 'node:fs';
// const inputFilename = process.argv[2] || 'target.js';
// const code = fs.readFileSync(inputFilename, 'utf-8');
const code = `(function() {
  function createMessage() {return 'Hello' + ' ' + 'there!';}
  function print(msg) {console.log(msg);}
  print(createMessage());
})();`;

logger.setLogLevelDebug();

/**
 * Replace specific strings with other strings
 * @param {Arborist} arb
 * @return {Arborist}
 */
function replaceSpecificLiterals(arb) {
	const replacements = {
        'Hello': 'General',
        'there!': 'Kenobi!',
    };
    // Iterate over only the relevant nodes by targeting specific types using the typeMap property on the root node
	const relevantNodes = [
		...(arb.ast[0].typeMap.Literal || []),
        // ...(arb.ast.typeMap.TemplateLiteral || []), // unnecessary for this example, but this is how to add more types
    ];
    for (const n of relevantNodes) {
        if (replacements[n.value]) {
          // dynamically define a replacement node by creating an object with a type and value properties
          // markNode(n) would delete the node, while markNode(n, {...}) would replace the node with the supplied node.
          arb.markNode(n, {type: 'Literal', value: replacements[n.value]});
        }
    }
  return arb;
}

let script = code;

script = applyIteratively(script, [
  replaceSpecificLiterals,
]);

if (code !== script) {
  console.log(script);
  // fs.writeFileSync(inputFilename + '-deob.js', script, 'utf-8');
} else console.log(`No changes`);

Read More