├── .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 |
--------------------------------------------------------------------------------