├── .gitignore ├── README.md ├── index.js ├── jsconfig.json ├── package.json └── src ├── BreakPointAST.js └── lib ├── BreakPointASTBuilder.js ├── BreakPointASTObfuscation.js ├── BreakPointASTObfuscationMBA.js └── BreakPointASTReplacement.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | .vscode 4 | .env -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## J5afe Javascript Obfuscation/Deobfuscation Framework 2 | 3 | ### Installation 4 | ``` 5 | npm i j5afe 6 | ``` 7 | Dependencies: 8 | Acorn.js 9 | 10 | Acorn is used for the parser and BreakpointAST takes care of the recursive path. 11 | 12 | example 13 | ```js 14 | 15 | var code = ` 16 | let lol = 10; 17 | 18 | console.log(lol) 19 | 20 | if (lol == 10 + 10) { 21 | console.log("ok") 22 | } else { 23 | console.log("nop") 24 | } 25 | ` 26 | 27 | let br = new BreakPointAST(code) 28 | 29 | br.addNodeBreakPoint("CallExpression", function(ctx, node) { 30 | console.log(node) 31 | }) 32 | 33 | br.addNodeBreakPoint("Literal", function(ctx,node) { 34 | console.log(node.value) 35 | }) 36 | 37 | br.addNodeBreakPoint("BinaryExpression", function(ctx, node) { 38 | console.log(ctx.evaluate(node)) 39 | }) 40 | 41 | br.walk() 42 | 43 | ``` 44 | 45 | 46 | ## Obfuscation 47 | You can create constant unfolding 48 | 49 | ```js 50 | let d = 10 * 20 + 30 >> 1333333333; 51 | let dd = "lol" 52 | ``` 53 | become 54 | ```js 55 | let d = (10 + 1337) * (20 + 1337) + (30 + 1337) >> 1333333333 + 1337; 56 | let dd = "lol"; 57 | ``` 58 | 59 | with 60 | ```js 61 | br.addNodeBreakPoint("Literal", function(ctx, node, builder) { 62 | 63 | if (typeof node.value !== 'number') 64 | return 65 | ctx.replaceExpression(node, builder.createBinaryExpression( 66 | '+', builder.createLiteral(node.value), builder.createLiteral(1337) 67 | ) 68 | ) 69 | }) 70 | ``` 71 | 72 | ```js 73 | let d = 10 * 10 + (1 ^ 2) + 2000 * 300 - (2 * 6); 74 | let dd = "lol" 75 | ``` 76 | 77 | Become 78 | ```js 79 | let d = (function ok() { 80 | return 10; 81 | })() * (function ok() { 82 | return 10; 83 | })() + ((function ok() { 84 | return 1; 85 | })() ^ (function ok() { 86 | return 2; 87 | })()) + (function ok() { 88 | return 2000; 89 | })() * (function ok() { 90 | return 300; 91 | })() - (function ok() { 92 | return 2; 93 | })() * (function ok() { 94 | return 6; 95 | })(); 96 | let dd = "lol"; 97 | ``` 98 | With 99 | 100 | ```js 101 | let br = new BreakPointAST(code) 102 | 103 | br.addNodeBreakPoint("Literal", function(ctx, node, builder) { 104 | 105 | if (typeof node.value !== 'number') return; 106 | 107 | ctx.replaceExpression( 108 | node, 109 | builder.createCallExpression( 110 | builder.createFunctionExpression( 111 | id=builder.createIdentifier("ok"), 112 | expression=false, 113 | generator=false, 114 | async=false, 115 | params=[], 116 | body=builder.createBlockStatement( 117 | body=[ 118 | builder.createReturnStatement( 119 | argument=builder.createLiteral( 120 | node.value 121 | ) 122 | ) 123 | ] 124 | ) 125 | ) 126 | ) 127 | ) 128 | }) 129 | 130 | br.walk() 131 | ``` 132 | 133 | ## MBA linear equation random 134 | ```js 135 | let d = (10 + 10) + 5 * 6 + 6 + 30 * 1337 - 10; 136 | let dd = "lol" 137 | ``` 138 | 139 | Become 140 | ```js 141 | let d = -(- -(-10 + -10) + -(5 * 6)) - -6 - -(30 * 1337) + -10; 142 | let dd = "lol"; 143 | ``` 144 | With 145 | ```js 146 | let br = new BreakPointAST(code) 147 | 148 | br.addNodeBreakPoint("BinaryExpression", function(ctx, node, builder, obfu) { 149 | ctx.replaceExpression(node, obfu.generate_mba(node)) 150 | }) 151 | 152 | br.walk() 153 | ``` 154 | 155 | ## Deobfuscation 156 | Constant folding 157 | ```js 158 | let d = (10 + 1337) * (20 + 1337) + (30 + 1337) >> 1333333333 + 1337; 159 | let dd = "lol"; 160 | ``` 161 | 162 | Become 163 | ```js 164 | let d = 111; 165 | let dd = "lol"; 166 | ``` 167 | with 168 | ```js 169 | br.addNodeBreakPoint("BinaryExpression", function(ctx, node, builder) { 170 | ctx.replaceExpression(node, builder.createLiteral(ctx.evaluate(node))) 171 | }) 172 | 173 | br.walk() 174 | ``` 175 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import BreakPointAST from './src/BreakPointAST.js' 2 | 3 | 4 | export { 5 | BreakPointAST as default 6 | } 7 | 8 | //console.log(br.ast.body[0].declarations[0]) 9 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | }, 5 | "exclude": [ 6 | "node_modules" 7 | ] 8 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "dependencies": { 4 | "acorn": "^8.10.0" 5 | }, 6 | "name": "j5afe", 7 | "description": "Dependencies:\r Acorn.js", 8 | "version": "1.0.2", 9 | "main": "index.js", 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/Lexterl33t/j5afe.git" 16 | }, 17 | "keywords": [ 18 | "Obfuscation/Deobfuscation", 19 | "javascript", 20 | "framework" 21 | ], 22 | "author": "Lxt3H", 23 | "license": "ISC", 24 | "bugs": { 25 | "url": "https://github.com/Lexterl33t/j5afe/issues" 26 | }, 27 | "homepage": "https://github.com/Lexterl33t/J5afe#readme" 28 | } 29 | -------------------------------------------------------------------------------- /src/BreakPointAST.js: -------------------------------------------------------------------------------- 1 | import * as acorn from 'acorn' 2 | 3 | import BreakPointASTBuilder from './lib/BreakPointASTBuilder.js'; 4 | import BreakPointASTObfuscationMBA from './lib/BreakPointASTObfuscationMBA.js'; 5 | import BreakPointASTReplacement from './lib/BreakPointASTReplacement.js'; 6 | 7 | export default class BreakPointAST extends acorn.Parser { 8 | 9 | constructor(source_code) { 10 | if (!source_code) throw new Error("Unknow source code !") 11 | 12 | super({ecmaVersion: 'latest'}, source_code) 13 | this.ast = this.parse() 14 | this.eventListener = {} 15 | this.breakpoint_replacement = new BreakPointASTReplacement().getReplacementList() 16 | } 17 | 18 | check_valid_replacement_expression(node, new_node) { 19 | for (let n of this.breakpoint_replacement[node.type]) { 20 | if (n === new_node.type) 21 | return true 22 | } 23 | 24 | return false; 25 | } 26 | 27 | 28 | replaceExpression(node, new_node) { 29 | if (node.constructor.name !== 'Node' && new_node.constructor.name !== 'Node') throw new Error("Node and new node must be Node") 30 | 31 | if(!this.check_valid_replacement_expression(node, new_node)) throw new Error(`${node.type} can't be replaced by ${new_node.type}`) 32 | 33 | Object.assign(node, new_node) 34 | 35 | } 36 | 37 | 38 | evaluate(node) { 39 | if (node.type !== 'BinaryExpression') throw new Error("Only binary expression can be evaluate") 40 | 41 | return this._evaluate(node) 42 | } 43 | 44 | _evaluate(node) { 45 | if (!node) 46 | return 0; 47 | 48 | switch (node.type) { 49 | case 'Literal': 50 | return node.value 51 | case 'BinaryExpression': 52 | switch(node.operator) { 53 | case '+': return this._evaluate(node.left) + this._evaluate(node.right) 54 | case '-': return this._evaluate(node.left) - this._evaluate(node.right) 55 | case '*': return this._evaluate(node.left) * this._evaluate(node.right) 56 | case '/': return this._evaluate(node.left) / this._evaluate(node.right) 57 | case '%': return this._evaluate(node.left) % this._evaluate(node.right) 58 | case '>>': return this._evaluate(node.left) >> this._evaluate(node.right) 59 | case '<<': return this._evaluate(node.left) << this._evaluate(node.right) 60 | case '&': return this._evaluate(node.left) & this._evaluate(node.right) 61 | case '|': return this._evaluate(node.left) | this._evaluate(node.right) 62 | case '^': return this._evaluate(node.left) ^ this._evaluate(node.right) 63 | } 64 | } 65 | } 66 | 67 | 68 | addNodeBreakPoint(typeName, callback) { 69 | if (typeof callback !== "function") 70 | throw new Error("Second parameter must be callback") 71 | 72 | this.eventListener[typeName] = callback 73 | } 74 | 75 | walk() { 76 | for (let node of this.ast.body) { 77 | this.depth(node) 78 | } 79 | } 80 | 81 | depth(node) { 82 | if (!node) 83 | return; 84 | 85 | switch(node.type) { 86 | case 'VariableDeclaration': 87 | if (this.eventListener[node.type]) { 88 | this.eventListener[node.type](this, node, (new BreakPointASTBuilder(this, node))) 89 | } 90 | 91 | for (let decl of node.declarations) { 92 | return this.depth(decl) 93 | } 94 | break 95 | case 'VariableDeclarator': 96 | if (this.eventListener[node.type]) { 97 | this.eventListener[node.type](this, node, (new BreakPointASTBuilder(this, node))) 98 | } 99 | this.depth(node.id) 100 | this.depth(node.init) 101 | break 102 | case 'ReturnStatement': 103 | if (this.eventListener[node.type]) { 104 | this.eventListener[node.type](this, node, (new BreakPointASTBuilder(this, node))) 105 | } 106 | 107 | this.depth(node.argument) 108 | break 109 | case 'AssignmentExpression': 110 | if (this.eventListener[node.type]) { 111 | this.eventListener[node.type](this, node, (new BreakPointASTBuilder(this, node))) 112 | } 113 | this.depth(node.left) 114 | this.depth(node.right) 115 | break 116 | case 'ArrowFunctionExpression': 117 | if (this.eventListener[node.type]) { 118 | this.eventListener[node.type](this, node, (new BreakPointASTBuilder(this, node))) 119 | } 120 | 121 | this.depth(node.params) 122 | this.depth(node.body) 123 | break 124 | case 'ForOfStatement': 125 | if (this.eventListener[node.type]) { 126 | this.eventListener[node.type](this, node, (new BreakPointASTBuilder(this, node))) 127 | } else { 128 | this.depth(node.left) 129 | this.depth(node.right) 130 | this.depth(node.body) 131 | } 132 | break 133 | case 'IfStatement': 134 | if (this.eventListener[node.type]) { 135 | this.eventListener[node.type](this, node, (new BreakPointASTBuilder(this, node))) 136 | } 137 | this.depth(node.test) 138 | this.depth(node.consequents) 139 | this.depth(node.alternate) 140 | break 141 | case 'BinaryExpression': 142 | if (this.eventListener[node.type]) { 143 | this.eventListener[node.type](this, node, (new BreakPointASTBuilder(this, node)), (new BreakPointASTObfuscationMBA(this, node))) 144 | } else { 145 | this.depth(node.left) 146 | this.depth(node.right) 147 | } 148 | break 149 | case 'Literal': 150 | if (this.eventListener[node.type]) { 151 | this.eventListener[node.type](this, node, (new BreakPointASTBuilder(this, node))) 152 | } 153 | return 154 | case 'ExpressionStatement': 155 | if (this.eventListener[node.type]) { 156 | this.eventListener[node.type](this, node, (new BreakPointASTBuilder(this, node))) 157 | } 158 | this.depth(node.expression) 159 | break 160 | case 'CallExpression': 161 | if (this.eventListener[node.type]) { 162 | this.eventListener[node.type](this, node, (new BreakPointASTBuilder(this, node))) 163 | } 164 | this.depth(node.callee) 165 | for (let arg of node.arguments) { 166 | this.depth(arg) 167 | } 168 | break 169 | case 'FunctionDeclaration': 170 | if (this.eventListener[node.type]) { 171 | this.eventListener[node.type](this, node, (new BreakPointASTBuilder(this, node))) 172 | } 173 | this.depth(node.id) 174 | for (let param of node.params) { 175 | this.depth(param) 176 | } 177 | this.depth(node.body) 178 | break 179 | case 'BlockStatement': 180 | if (this.eventListener[node.type]) { 181 | this.eventListener[node.type](this, node, (new BreakPointASTBuilder(this, node))) 182 | } 183 | for (let bd of node.body) { 184 | this.depth(bd) 185 | } 186 | break 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/lib/BreakPointASTBuilder.js: -------------------------------------------------------------------------------- 1 | import * as acorn from 'acorn' 2 | 3 | export default class BreakPointASTBuilder { 4 | 5 | constructor(ctx, node) { 6 | this.node = node; 7 | this.ctx = ctx 8 | } 9 | 10 | createUnaryExpression(op, prefix=true, argument) { 11 | if (!this.ctx) return false; 12 | 13 | if (!this.node) return false; 14 | 15 | let n = new acorn.Node(this.ctx) 16 | 17 | n.type = "UnaryExpression" 18 | n.start = this.node.start 19 | n.operator = op 20 | n.prefix = prefix 21 | n.argument = argument 22 | return n 23 | } 24 | 25 | createLiteral(value) { 26 | if (!this.ctx) return false; 27 | 28 | if (!this.node) return false; 29 | 30 | let n = new acorn.Node(this.ctx) 31 | n.type = "Literal" 32 | n.start = this.node.start 33 | n.value = value 34 | n.raw = value.toString() 35 | return n 36 | } 37 | 38 | createIdentifier(name) { 39 | if (!this.ctx) return false; 40 | if (!this.node) return false; 41 | 42 | let n = new acorn.Node(this.ctx) 43 | 44 | n.type = "Identifier" 45 | n.start = this.node.start 46 | n.name = name; 47 | 48 | return n 49 | } 50 | 51 | createBinaryExpression(op, left, right) { 52 | if (!this.ctx) return false; 53 | if (!this.node) return false; 54 | 55 | let n = new acorn.Node(this.ctx) 56 | 57 | n.type = "BinaryExpression" 58 | n.start = this.node.start 59 | n.left = left; 60 | n.operator = op; 61 | n.right = right 62 | return n 63 | } 64 | 65 | createVariableDeclaration(kind, declarations) { 66 | if (!this.ctx) return false; 67 | if (!this.node) return false; 68 | 69 | let n = new acorn.Node(this.ctx) 70 | 71 | n.type = "VariableDeclaration" 72 | n.start = this.node.start 73 | n.declarations = declarations 74 | n.kind = kind 75 | 76 | return n 77 | } 78 | 79 | createVariableDeclarator(id, init) { 80 | if (!this.ctx) return false; 81 | if (!this.node) return false; 82 | 83 | let n = new acorn.Node(this.ctx) 84 | 85 | n.type = "VariableDeclarator" 86 | n.start = this.node.start 87 | n.id = id 88 | n.init = init 89 | 90 | return n 91 | } 92 | 93 | createExpressionStatement(expression) { 94 | if (!this.ctx) return false; 95 | if (!this.node) return false; 96 | 97 | let n = new acorn.Node(this.ctx) 98 | 99 | n.type = "ExpressionStatement" 100 | n.start = this.node.start 101 | n.expression = expression 102 | 103 | return n 104 | } 105 | 106 | 107 | createAssignmentExpression(op, left, right) { 108 | if (!this.ctx) return false; 109 | if (!this.node) return false; 110 | 111 | let n = new acorn.Node(this.ctx) 112 | 113 | n.type = "AssignmentExpression" 114 | n.start = this.node.start 115 | n.left = left 116 | n.right = right 117 | n.operator = op 118 | 119 | return n 120 | } 121 | 122 | createFunctionDeclaration(id, expression=false, generator=false, async=false, params=[], body) { 123 | if (!this.ctx) return false; 124 | if (!this.node) return false; 125 | 126 | if (body.type !== 'BlockStatement') throw new Error("Body must be BlockStatement") 127 | 128 | let n = new acorn.Node(this.ctx) 129 | 130 | n.type = "FunctionDeclaration" 131 | n.start = this.node.start 132 | n.id = id 133 | n.expression = expression 134 | n.async = async 135 | n.params = params 136 | n.generator = generator 137 | n.body = body 138 | 139 | return n 140 | } 141 | 142 | createBlockStatement(body = []) { 143 | if (!this.ctx) return false; 144 | if (!this.node) return false; 145 | 146 | let n = new acorn.Node(this.ctx) 147 | 148 | n.type = "BlockStatement" 149 | n.start = this.node.start 150 | n.body = body 151 | return n 152 | } 153 | 154 | createReturnStatement(argument) { 155 | if (!this.ctx) return false; 156 | if (!this.node) return false; 157 | 158 | let n = new acorn.Node(this.ctx) 159 | 160 | n.type = "ReturnStatement" 161 | n.start = this.node.start 162 | n.argument = argument 163 | return n 164 | } 165 | 166 | createCallExpression(callee, args = [], optional=false) { 167 | if (!this.ctx) return false; 168 | if (!this.node) return false; 169 | 170 | let n = new acorn.Node(this.ctx) 171 | 172 | n.type = "CallExpression" 173 | n.start = this.node.start 174 | n.optional = optional 175 | n.callee = callee 176 | n.arguments = args 177 | return n; 178 | } 179 | 180 | createFunctionExpression(id, expression=false, generator=false, async=false, params=[], body) { 181 | if (!this.ctx) return false; 182 | if (!this.node) return false; 183 | 184 | 185 | if (body.type !== 'BlockStatement') throw new Error("Body must be BlockStatement") 186 | 187 | let n = new acorn.Node(this.ctx) 188 | 189 | n.type = "FunctionExpression" 190 | n.start = this.node.start 191 | n.id = id 192 | n.expression = expression 193 | n.async = async 194 | n.params = params 195 | n.generator = generator 196 | n.body = body 197 | 198 | return n; 199 | } 200 | } -------------------------------------------------------------------------------- /src/lib/BreakPointASTObfuscation.js: -------------------------------------------------------------------------------- 1 | import BreakPointASTBuilder from './BreakPointASTBuilder.js' 2 | export default class BreakPointASTObfuscation extends BreakPointASTBuilder { 3 | constructor(ctx, node) { 4 | super(ctx, node) 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/lib/BreakPointASTObfuscationMBA.js: -------------------------------------------------------------------------------- 1 | import BreakPointASTObfuscation from './BreakPointASTObfuscation.js'; 2 | 3 | 4 | 5 | export default class BreakPointASTObfuscationMBA extends BreakPointASTObfuscation { 6 | 7 | constructor(ctx, node) { 8 | super(ctx, node) 9 | } 10 | 11 | generate_mba(node) { 12 | //if (node.type !== 'BinaryExpression') throw new Error("Node must be binary expression") 13 | 14 | return this._generate_mba(node) 15 | } 16 | 17 | _generate_mba(node) { 18 | 19 | if (!node) return; 20 | 21 | let choice; 22 | switch (node.type) { 23 | case 'BinaryExpression': 24 | switch(node.operator) { 25 | case '+': 26 | choice = Math.floor(Math.random() * 3) 27 | switch (choice) { 28 | case 0: 29 | return this.addition_mba1(node) 30 | case 1: 31 | return this.addition_mba2(node) 32 | case 2: 33 | return this.addition_mba3(node) 34 | } 35 | case '-': 36 | choice = Math.floor(Math.random() * 2) 37 | switch (choice) { 38 | case 0: 39 | return this.substract_mba1(node) 40 | case 1: 41 | return this.substract_mba2(node) 42 | } 43 | } 44 | case 'Literal': 45 | return node 46 | case 'UnaryExpression': 47 | return node 48 | case 'Identifier': 49 | return node 50 | } 51 | } 52 | 53 | addition_mba1(node) { 54 | return super.createBinaryExpression( 55 | '-', 56 | this._generate_mba(node.left), 57 | super.createUnaryExpression( 58 | '-', 59 | true, 60 | this._generate_mba(node.right) 61 | ) 62 | ) 63 | } 64 | 65 | addition_mba2(node) { 66 | 67 | return super.createUnaryExpression( 68 | '-', 69 | true, 70 | super.createBinaryExpression( 71 | '+', 72 | super.createUnaryExpression( 73 | '-', 74 | true, 75 | this._generate_mba(node.left) 76 | ), 77 | super.createUnaryExpression( 78 | '-', 79 | true, 80 | this._generate_mba(node.right) 81 | ) 82 | ) 83 | ) 84 | } 85 | 86 | addition_mba3(node) { 87 | let rand_num = Math.floor(Math.random() * 999999) 88 | return super.createBinaryExpression( 89 | '-', 90 | super.createBinaryExpression( 91 | '+', 92 | super.createBinaryExpression( 93 | '+', 94 | this._generate_mba(node.left), 95 | super.createLiteral( 96 | rand_num 97 | ) 98 | ), 99 | this._generate_mba(node.right) 100 | ), 101 | super.createLiteral( 102 | rand_num 103 | ) 104 | ) 105 | } 106 | 107 | substract_mba1(node) { 108 | return super.createBinaryExpression( 109 | '+', 110 | this._generate_mba(node.left), 111 | super.createUnaryExpression( 112 | '-', 113 | true, 114 | this._generate_mba(node.right) 115 | ) 116 | ) 117 | } 118 | 119 | substract_mba2(node) { 120 | let rand_num = Math.floor(Math.random() * 999999) 121 | 122 | return super.createBinaryExpression( 123 | '-', 124 | super.createBinaryExpression( 125 | '-', 126 | super.createBinaryExpression( 127 | '+', 128 | this._generate_mba(node.left), 129 | super.createLiteral( 130 | rand_num 131 | ) 132 | ), 133 | this._generate_mba(node.right) 134 | ), 135 | super.createLiteral( 136 | rand_num 137 | ) 138 | ) 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/lib/BreakPointASTReplacement.js: -------------------------------------------------------------------------------- 1 | export default class BreakPointASTReplacement { 2 | 3 | constructor() { 4 | this.replacementList = { 5 | "Literal": [ 6 | "Literal", "Identifier", "BinaryExpression", "ExpressionStatement", 7 | "CallExpression" 8 | ], 9 | "BinaryExpression": [ 10 | "Literal", "Identifier", "CallExpression", "BinaryExpression", 11 | "UnaryExpression" 12 | ], 13 | "UnaryExpression": [ 14 | "BinaryExpression" 15 | ] 16 | } 17 | } 18 | 19 | getReplacementList() { 20 | if (!this.replacementList) throw new Error("Replacement list not initialised") 21 | 22 | return this.replacementList 23 | } 24 | } 25 | 26 | --------------------------------------------------------------------------------