{"id":22095997,"url":"https://github.com/perimeterx/restringer","last_synced_at":"2025-05-15T04:04:26.199Z","repository":{"id":48183882,"uuid":"516086385","full_name":"PerimeterX/restringer","owner":"PerimeterX","description":"A Javascript Deobfuscator","archived":false,"fork":false,"pushed_at":"2024-12-25T14:11:09.000Z","size":673,"stargazers_count":428,"open_issues_count":15,"forks_count":42,"subscribers_count":11,"default_branch":"main","last_synced_at":"2025-05-15T04:04:14.710Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PerimeterX.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-07-20T18:04:34.000Z","updated_at":"2025-05-07T14:44:21.000Z","dependencies_parsed_at":"2024-01-14T09:35:21.637Z","dependency_job_id":"376e4b0e-31a8-4d08-8ccd-cd0fbb578784","html_url":"https://github.com/PerimeterX/restringer","commit_stats":{"total_commits":102,"total_committers":6,"mean_commits":17.0,"dds":0.4411764705882353,"last_synced_commit":"521907fba198f12d603cb16878843a4bed610a3d"},"previous_names":[],"tags_count":39,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerimeterX%2Frestringer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerimeterX%2Frestringer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerimeterX%2Frestringer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PerimeterX%2Frestringer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PerimeterX","download_url":"https://codeload.github.com/PerimeterX/restringer/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254270641,"owners_count":22042858,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-01T04:09:20.437Z","updated_at":"2025-05-15T04:04:26.152Z","avatar_url":"https://github.com/PerimeterX.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Restringer\n[![Node.js CI](https://github.com/PerimeterX/restringer/actions/workflows/node.js.yml/badge.svg?branch=main)](https://github.com/PerimeterX/restringer/actions/workflows/node.js.yml)\n[![Downloads](https://img.shields.io/npm/dm/restringer.svg?maxAge=43200)](https://www.npmjs.com/package/restringer)\n\nDeobfuscate Javascript and reconstruct strings.\nSimplify cumbersome logic where possible while adhering to scope limitations.\n\nTry it online @ [restringer.tech](https://restringer.tech).\n\nFor comments and suggestions feel free to open an issue or find me on Twitter - [@ctrl__esc](https://twitter.com/ctrl__esc) \n\n## Table of Contents\n* [Installation](#installation)\n  * [npm](#npm)\n  * [Clone The Repo](#clone-the-repo)\n* [Usage](#usage)\n  * [Command-Line Usage](#command-line-usage) \n  * [Use as a Module](#use-as-a-module) \n* [Create Custom Deobfuscators](#create-custom-deobfuscators)\n  * [Boilerplate Code for Starting from Scratch](#boilerplate-code-for-starting-from-scratch)\n* [Read More](#read-more)\n***\n\n## Installation \n### npm\n```shell\nnpm install -g restringer\n```\n\n### Clone The Repo\nRequires Node 16 or newer.\n```shell\ngit clone git@github.com:PerimeterX/restringer.git\ncd restringer\nnpm install\n```\n\n***\n\n## Usage\nThe [restringer.js](src/restringer.js) uses generic deobfuscation methods that reconstruct and restore obfuscated strings and simplifies redundant logic meant only to encumber.\nREstringer employs the [Obfuscation Detector](https://github.com/PerimeterX/obfuscation-detector/blob/main/README.md) to identify specific types of obfuscation for which\nthere's a need to apply specific deobfuscation methods in order to circumvent anti-debugging mechanisms or other code traps\npreventing the script from being deobfuscated.   \n\n### Command-Line Usage\n```\nUsage: restringer input_filename [-h] [-c] [-q | -v] [-m M] [-o [output_filename]]\n\npositional arguments:\n\tinput_filename                  The obfuscated JS file\n\noptional arguments:\n\t-h, --help                      Show this help message and exit.\n\t-c, --clean                     Remove dead nodes from script after deobfuscation is complete (unsafe).\n\t-q, --quiet                     Suppress output to stdout. Output result only to stdout if the -o option is not set.\n\t\t\t\t\t\t\t\t\tDoes not go with the -v option.\n\t-m, --max-iterations M          Run at most M iterations\n\t-v, --verbose                   Show more debug messages while deobfuscating. Does not go with the -q option.\n\t-o, --output [output_filename]  Write deobfuscated script to output_filename. \n\t\t\t\t\t\t\t\t\t\u003cinput_filename\u003e-deob.js is used if no filename is provided.\n```\nExamples:\n- Print the deobfuscated script to stdout.\n  ```shell\n   restringer [target-file.js]\n  ```\n- Save the deobfuscated script to output.js.\n  ```shell\n   restringer [target-file.js] -o output.js\n  ```\n- Deobfuscate and print debug info.\n  ```shell\n   restringer [target-file.js] -v\n  ```\n- Deobfuscate without printing anything but the deobfuscated output.\n  ```shell\n   restringer [target-file.js] -q\n  ```\n\n\n### Use as a Module\n\n```javascript\nimport {REstringer} from 'restringer';\n\nconst restringer = new REstringer('\"RE\" + \"stringer\"');\nif (restringer.deobfuscate()) {\n  console.log(restringer.script);\n} else {\n  console.log('Nothing was deobfuscated :/');\n}\n// Output: 'REstringer';\n```\n\n***\n## Create Custom Deobfuscators\nREstringer is highly modularized. It exposes modules that allow creating custom deobfuscators \nthat can solve specific problems.\n\nThe basic structure of such a deobfuscator would be an array of deobfuscation modules \n(either [safe](src/modules/safe) or [unsafe](src/modules/unsafe)), run via flAST's applyIteratively utility function.\n\nUnsafe modules run code through `eval` (using [isolated-vm](https://www.npmjs.com/package/isolated-vm) to be on the safe side) while safe modules do not.\n\n```javascript\nimport {applyIteratively} from 'flast';\nimport {safe, unsafe} from 'restringer';\nconst {normalizeComputed} = safe;\nconst {resolveDefiniteBinaryExpressions, resolveLocalCalls} = unsafe;\nlet script = 'obfuscated JS here';\nconst deobModules = [\n  resolveDefiniteBinaryExpressions,\n  resolveLocalCalls,\n  normalizeComputed,\n];\nscript = applyIteratively(script, deobModules);\nconsole.log(script); // Deobfuscated script\n```\n\nWith the additional `candidateFilter` function argument, it's possible to narrow down the targeted nodes:\n```javascript\nimport {unsafe} from 'restringer';\nconst {resolveLocalCalls} = unsafe;\nimport {applyIteratively} from 'flast';\nlet script = 'obfuscated JS here';\n\n// It's better to define a function with a meaningful name that can show up in the log \nfunction resolveLocalCallsInGlobalScope(arb) {\n  return resolveLocalCalls(arb, n =\u003e n.parentNode?.type === 'Program');\n}\nscript = applyIteratively(script, [resolveLocalCallsInGlobalScope]);\nconsole.log(script); // Deobfuscated script\n```\n\nYou can also customize any deobfuscation method while still using REstringer without running the loop yourself:\n```javascript\nimport fs from 'node:fs';\nimport {REstringer} from 'restringer';\n\nconst inputFilename = process.argv[2];\nconst code = fs.readFileSync(inputFilename, 'utf-8');\nconst res = new REstringer(code);\n\n// res.logger.setLogLevelDebug();\nres.detectObfuscationType = false;  // Skip obfuscation type detection, including any pre and post processors\n\nconst targetFunc = res.unsafeMethods.find(m =\u003e m.name === 'resolveLocalCalls');\nlet changes = 0;\t\t// Resolve only the first 5 calls\nres.safeMethods[res.unsafeMethods.indexOf(targetFunc)] = function customResolveLocalCalls(n) {return targetFunc(n, () =\u003e changes++ \u003c 5)}\n\nres.deobfuscate();\n\nif (res.script !== code) {\n  console.log('[+] Deob successful');\n  fs.writeFileSync(`${inputFilename}-deob.js`, res.script, 'utf-8');\n} else console.log('[-] Nothing deobfuscated :/');\n```\n\n*** \n\n### Boilerplate code for starting from scratch\n```javascript\nimport {applyIteratively, logger} from 'flast';\n// Optional loading from file\n// import fs from 'node:fs';\n// const inputFilename = process.argv[2] || 'target.js';\n// const code = fs.readFileSync(inputFilename, 'utf-8');\nconst code = `(function() {\n  function createMessage() {return 'Hello' + ' ' + 'there!';}\n  function print(msg) {console.log(msg);}\n  print(createMessage());\n})();`;\n\nlogger.setLogLevelDebug();\n\n/**\n * Replace specific strings with other strings\n * @param {Arborist} arb\n * @return {Arborist}\n */\nfunction replaceSpecificLiterals(arb) {\n\tconst replacements = {\n        'Hello': 'General',\n        'there!': 'Kenobi!',\n    };\n    // Iterate over only the relevant nodes by targeting specific types using the typeMap property on the root node\n\tconst relevantNodes = [\n\t\t...(arb.ast[0].typeMap.Literal || []),\n        // ...(arb.ast.typeMap.TemplateLiteral || []), // unnecessary for this example, but this is how to add more types\n    ];\n    for (const n of relevantNodes) {\n        if (replacements[n.value]) {\n          // dynamically define a replacement node by creating an object with a type and value properties\n          // markNode(n) would delete the node, while markNode(n, {...}) would replace the node with the supplied node.\n          arb.markNode(n, {type: 'Literal', value: replacements[n.value]});\n        }\n    }\n  return arb;\n}\n\nlet script = code;\n\nscript = applyIteratively(script, [\n  replaceSpecificLiterals,\n]);\n\nif (code !== script) {\n  console.log(script);\n  // fs.writeFileSync(inputFilename + '-deob.js', script, 'utf-8');\n} else console.log(`No changes`);\n```\n***\n\n## Read More\n* [Processors](src/processors/README.md)\n* [Contribution guide](CONTRIBUTING.md)\n* [Obfuscation Detector](https://github.com/PerimeterX/obfuscation-detector/blob/main/README.md)\n* [flAST](https://github.com/PerimeterX/flast/blob/main/README.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fperimeterx%2Frestringer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fperimeterx%2Frestringer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fperimeterx%2Frestringer/lists"}