jsarmor 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cli/index.js ADDED
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "fs";
4
+ import path from "path";
5
+ import chalk from "chalk";
6
+ import gradient from "gradient-string";
7
+ import cliProgress from "cli-progress";
8
+ import generate from "@babel/generator";
9
+
10
+ import { parseCode } from "../core/parser.js";
11
+ import { renameVariables } from "../core/renamer.js";
12
+ import { encodeStrings } from "../core/stringArray.js";
13
+ import { flattenControlFlow } from "../core/controlflow.js";
14
+ import { unicodeEscape } from "../core/unicode.js";
15
+ import { generateJunk } from "../core/junkcode.js";
16
+
17
+ import { injectDeadCode, generateDeadRuntime } from "../core/deadcode.js";
18
+
19
+ import { injectAntiDebug } from "../core/antidebug.js";
20
+ import { antiBeautify } from "../core/beautifyGuard.js";
21
+ import { selfDefend } from "../core/selfdefend.js";
22
+
23
+ const neon = gradient(["#00ffff","#ff00ff","#00ff9f"]);
24
+
25
+ try{
26
+
27
+ const args = process.argv.slice(2);
28
+
29
+ const command = args[0];
30
+ const file = args[1];
31
+
32
+ let outputName = "obf.js";
33
+
34
+ const outIndex = args.indexOf("-o");
35
+
36
+ if(outIndex !== -1 && args[outIndex+1]){
37
+ outputName = args[outIndex+1];
38
+ }
39
+
40
+ if(!command || command === "help"){
41
+
42
+ console.log(neon(`
43
+ JSArmor - JavaScript Obfuscator
44
+
45
+ Usage:
46
+
47
+ jsarmor gen input.js
48
+ jsarmor gen input.js -o output.js
49
+
50
+ `));
51
+
52
+ process.exit(0);
53
+
54
+ }
55
+
56
+ if(command !== "gen"){
57
+
58
+ console.log(chalk.red("Unknown command"));
59
+ console.log("Use: jsarmor gen input.js");
60
+
61
+ process.exit(1);
62
+
63
+ }
64
+
65
+ if(!file){
66
+
67
+ console.log(chalk.red("Please provide input file"));
68
+ console.log("Usage: jsarmor gen input.js");
69
+
70
+ process.exit(1);
71
+
72
+ }
73
+
74
+ if(!fs.existsSync(file)){
75
+ console.log(chalk.red(`File not found: ${file}`));
76
+ process.exit(1);
77
+ }
78
+
79
+ let code = fs.readFileSync(file,"utf8");
80
+
81
+ let shebang = "";
82
+
83
+ if(code.startsWith("#!")){
84
+ const end = code.indexOf("\n");
85
+ shebang = code.slice(0,end);
86
+ code = code.slice(end);
87
+ }
88
+
89
+ console.log(
90
+ neon(`┌──────────────────────────────┐
91
+ │ NodeJS Obfuscator v3 │
92
+ └──────────────────────────────┘`)
93
+ );
94
+
95
+ const bar = new cliProgress.SingleBar({
96
+ format:"progress [{bar}] {percentage}% | {value}/{total}",
97
+ barCompleteChar:"█",
98
+ barIncompleteChar:"░"
99
+ });
100
+
101
+ bar.start(6,0);
102
+
103
+ const ast = parseCode(code);
104
+ bar.increment();
105
+
106
+ renameVariables(ast);
107
+ bar.increment();
108
+
109
+ const pool = encodeStrings(ast);
110
+ bar.increment();
111
+
112
+ /* dead code transform */
113
+ injectDeadCode(ast);
114
+ bar.increment();
115
+
116
+ flattenControlFlow(ast);
117
+ bar.increment();
118
+
119
+ unicodeEscape(ast);
120
+ bar.increment();
121
+
122
+ bar.stop();
123
+
124
+ const output = generate.default(ast,{
125
+ jsescOption:{
126
+ minimal:true
127
+ }
128
+ }).code;
129
+
130
+ const runtimeJunk = generateDeadRuntime();
131
+
132
+ const final =
133
+ (shebang ? shebang + "\n" : "") +
134
+ injectAntiDebug() +
135
+ antiBeautify() +
136
+ selfDefend() +
137
+ `var _STRINGS=${JSON.stringify(pool)};\n` +
138
+ runtimeJunk +
139
+ generateJunk() +
140
+ output;
141
+
142
+ const distDir = path.join(process.cwd(),"dist");
143
+
144
+ if(!fs.existsSync(distDir)){
145
+ fs.mkdirSync(distDir,{recursive:true});
146
+ }
147
+
148
+ const outFile = path.join(distDir,"obf.js");
149
+
150
+ fs.writeFileSync(outFile,final);
151
+
152
+ console.log();
153
+ console.log(chalk.green("✔ Parsing AST"));
154
+ console.log(chalk.green("✔ Variables renamed"));
155
+ console.log(chalk.green("✔ Strings encoded"));
156
+ console.log(chalk.green("✔ Dead code injected"));
157
+ console.log(chalk.green("✔ Control flow flattened"));
158
+ console.log(chalk.green("✔ Unicode escape applied"));
159
+ console.log();
160
+ console.log(chalk.bold.green("✔ Obfuscation Complete"));
161
+ console.log(chalk.cyan(`Output: ${outFile}`));
162
+
163
+ const originalSize = code.length;
164
+ const newSize = final.length;
165
+
166
+ console.log(
167
+ chalk.yellow(
168
+ `Size: ${(originalSize/1024).toFixed(2)} KB → ${(newSize/1024).toFixed(2)} KB`
169
+ )
170
+ );
171
+
172
+ }catch(err){
173
+
174
+ console.log(chalk.red("Obfuscation failed"));
175
+ console.error(err);
176
+
177
+ }
@@ -0,0 +1,15 @@
1
+ export function injectAntiDebug(){
2
+
3
+ return `
4
+
5
+ (function(){
6
+ setInterval(function(){
7
+ try{
8
+ (function(){debugger})()
9
+ }catch(e){}
10
+ },2000)
11
+ })();
12
+
13
+ `;
14
+
15
+ }
@@ -0,0 +1,21 @@
1
+ export function antiBeautify(){
2
+
3
+ return `
4
+
5
+ (function(){
6
+
7
+ const start=Date.now()
8
+
9
+ debugger
10
+
11
+ const end=Date.now()
12
+
13
+ if(end-start>100){
14
+ while(true){}
15
+ }
16
+
17
+ })();
18
+
19
+ `;
20
+
21
+ }
@@ -0,0 +1,150 @@
1
+ import traverseModule from "@babel/traverse";
2
+ import * as t from "@babel/types";
3
+
4
+ const traverse = traverseModule.default;
5
+
6
+ export function flattenControlFlow(ast){
7
+
8
+ traverse(ast,{
9
+
10
+ Function(path){
11
+
12
+ // skip async / generator
13
+ if(path.node.async || path.node.generator){
14
+ return;
15
+ }
16
+
17
+ const body = path.node.body.body;
18
+
19
+ if(!Array.isArray(body)) return;
20
+
21
+ if(body.length < 4) return;
22
+
23
+ const hoisted = [];
24
+
25
+ body.forEach(stmt=>{
26
+
27
+ if(t.isVariableDeclaration(stmt)){
28
+
29
+ stmt.declarations.forEach(dec=>{
30
+ hoisted.push(
31
+ t.variableDeclarator(
32
+ dec.id,
33
+ null
34
+ )
35
+ );
36
+ });
37
+
38
+ }
39
+
40
+ });
41
+
42
+ const cases = [];
43
+
44
+ body.forEach((stmt,i)=>{
45
+
46
+ const statements = [];
47
+
48
+ if(t.isVariableDeclaration(stmt)){
49
+
50
+ stmt.declarations.forEach(dec=>{
51
+
52
+ if(dec.init){
53
+
54
+ statements.push(
55
+ t.expressionStatement(
56
+ t.assignmentExpression(
57
+ "=",
58
+ dec.id,
59
+ dec.init
60
+ )
61
+ )
62
+ );
63
+
64
+ }
65
+
66
+ });
67
+
68
+ }else{
69
+
70
+ statements.push(stmt);
71
+
72
+ }
73
+
74
+ // next state
75
+ statements.push(
76
+ t.expressionStatement(
77
+ t.updateExpression(
78
+ "++",
79
+ t.identifier("_s")
80
+ )
81
+ )
82
+ );
83
+
84
+ statements.push(t.breakStatement());
85
+
86
+ cases.push(
87
+ t.switchCase(
88
+ t.numericLiteral(i),
89
+ statements
90
+ )
91
+ );
92
+
93
+ });
94
+
95
+ const whileLoop = t.whileStatement(
96
+ t.booleanLiteral(true),
97
+ t.blockStatement([
98
+
99
+ t.switchStatement(
100
+ t.identifier("_s"),
101
+ cases
102
+ ),
103
+
104
+ t.ifStatement(
105
+ t.binaryExpression(
106
+ ">=",
107
+ t.identifier("_s"),
108
+ t.numericLiteral(body.length)
109
+ ),
110
+ t.breakStatement()
111
+ )
112
+
113
+ ])
114
+ );
115
+
116
+ const newBody = [];
117
+
118
+ // state variable
119
+ newBody.push(
120
+ t.variableDeclaration(
121
+ "let",
122
+ [
123
+ t.variableDeclarator(
124
+ t.identifier("_s"),
125
+ t.numericLiteral(0)
126
+ )
127
+ ]
128
+ )
129
+ );
130
+
131
+ // hoisted vars
132
+ if(hoisted.length){
133
+
134
+ newBody.push(
135
+ t.variableDeclaration(
136
+ "let",
137
+ hoisted
138
+ )
139
+ );
140
+
141
+ }
142
+
143
+ newBody.push(whileLoop);
144
+
145
+ path.node.body.body = newBody;
146
+
147
+ }
148
+ });
149
+
150
+ }
@@ -0,0 +1,91 @@
1
+ import traverse from "@babel/traverse";
2
+ import * as t from "@babel/types";
3
+ import { randomInt } from "../utils/random.js";
4
+
5
+ const patterns = [
6
+
7
+ "var _a=Math.random();if(_a>2){console.log(_a)}",
8
+ "var _b=0;for(var i=0;i<10;i++){_b+=i}",
9
+ "try{JSON.parse('x')}catch(e){}",
10
+ "var _c=[1,2,3];_c.reverse().join(',')",
11
+ "var _d=Date.now();_d+=Math.random()",
12
+ "var _e='abc'.split('').reverse().join('')",
13
+ "Boolean(Math.random())",
14
+ "Math.floor(Math.random()*999)",
15
+ "var _f={a:1,b:2};Object.keys(_f)",
16
+ "var _g=0;while(_g<1){_g++}"
17
+
18
+ ];
19
+
20
+ for(let i=0;i<90;i++){
21
+
22
+ patterns.push(`
23
+ var _junk${i}=Math.random()*${randomInt(1,999)};
24
+ if(_junk${i}>99999){
25
+ console.log(_junk${i})
26
+ }
27
+ `);
28
+
29
+ }
30
+
31
+ function randomPattern(){
32
+
33
+ return patterns[Math.floor(Math.random()*patterns.length)];
34
+
35
+ }
36
+
37
+ export function injectDeadCode(ast){
38
+
39
+ traverse.default(ast,{
40
+
41
+ BlockStatement(path){
42
+
43
+ if(Math.random()>0.4) return;
44
+
45
+ const fake = t.ifStatement(
46
+
47
+ t.binaryExpression(
48
+ ">",
49
+ t.numericLiteral(randomInt(0,100)),
50
+ t.numericLiteral(1000)
51
+ ),
52
+
53
+ t.blockStatement([
54
+
55
+ t.expressionStatement(
56
+ t.callExpression(
57
+ t.memberExpression(
58
+ t.identifier("console"),
59
+ t.identifier("log")
60
+ ),
61
+ [
62
+ t.stringLiteral("dead_"+randomInt(1000,9999))
63
+ ]
64
+ )
65
+ )
66
+
67
+ ])
68
+
69
+ );
70
+
71
+ path.node.body.unshift(fake);
72
+
73
+ }
74
+
75
+ });
76
+
77
+ }
78
+
79
+ export function generateDeadRuntime(){
80
+
81
+ let out="";
82
+
83
+ for(let i=0;i<30;i++){
84
+
85
+ out+=randomPattern()+"\n";
86
+
87
+ }
88
+
89
+ return out;
90
+
91
+ }
@@ -0,0 +1,18 @@
1
+ export function generateJunk() {
2
+
3
+ const junk = [];
4
+
5
+ for (let i = 0; i < 20; i++) {
6
+
7
+ junk.push(`
8
+ var _junk${i} = Math.random() * ${i};
9
+ if(_junk${i} > 9999){
10
+ console.log(_junk${i});
11
+ }
12
+ `);
13
+
14
+ }
15
+
16
+ return junk.join("\n");
17
+
18
+ }
package/core/parser.js ADDED
@@ -0,0 +1,21 @@
1
+ import { parse } from "@babel/parser";
2
+
3
+ export function parseCode(code){
4
+
5
+ return parse(code,{
6
+ sourceType:"unambiguous",
7
+ allowReturnOutsideFunction:true,
8
+ plugins:[
9
+ "jsx",
10
+ "classProperties",
11
+ "classPrivateProperties",
12
+ "classPrivateMethods",
13
+ "dynamicImport",
14
+ "optionalChaining",
15
+ "nullishCoalescingOperator",
16
+ "objectRestSpread",
17
+ "topLevelAwait"
18
+ ]
19
+ });
20
+
21
+ }
package/core/rc4.js ADDED
@@ -0,0 +1,42 @@
1
+ export function rc4(key, str){
2
+
3
+ let s=[]
4
+ let j=0
5
+ let x
6
+ let res=""
7
+
8
+ for(let i=0;i<256;i++){
9
+ s[i]=i
10
+ }
11
+
12
+ for(let i=0;i<256;i++){
13
+
14
+ j=(j+s[i]+key.charCodeAt(i%key.length))%256
15
+
16
+ x=s[i]
17
+ s[i]=s[j]
18
+ s[j]=x
19
+
20
+ }
21
+
22
+ let i=0
23
+ j=0
24
+
25
+ for(let y=0;y<str.length;y++){
26
+
27
+ i=(i+1)%256
28
+ j=(j+s[i])%256
29
+
30
+ x=s[i]
31
+ s[i]=s[j]
32
+ s[j]=x
33
+
34
+ let k=s[(s[i]+s[j])%256]
35
+
36
+ res+=String.fromCharCode(str.charCodeAt(y)^k)
37
+
38
+ }
39
+
40
+ return res
41
+
42
+ }
@@ -0,0 +1,64 @@
1
+ import traverseModule from "@babel/traverse";
2
+
3
+ const traverse = traverseModule.default;
4
+
5
+ const reserved = new Set([
6
+ "require",
7
+ "module",
8
+ "exports",
9
+ "__dirname",
10
+ "__filename",
11
+ "console",
12
+ "process",
13
+ "Buffer",
14
+ "setTimeout",
15
+ "setInterval"
16
+ ]);
17
+
18
+ function randomName(){
19
+ const chars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
20
+ let name="_";
21
+ for(let i=0;i<6;i++){
22
+ name+=chars[Math.floor(Math.random()*chars.length)];
23
+ }
24
+ return name;
25
+ }
26
+
27
+ export function renameVariables(ast){
28
+
29
+ traverse(ast,{
30
+ Program(path){
31
+
32
+ const bindings = path.scope.bindings;
33
+
34
+ for(const key in bindings){
35
+
36
+ const binding = bindings[key];
37
+
38
+ if(reserved.has(key)) continue;
39
+
40
+ // skip imports
41
+ if(binding.path.isImportSpecifier?.()) continue;
42
+ if(binding.path.isImportDefaultSpecifier?.()) continue;
43
+ if(binding.path.isImportNamespaceSpecifier?.()) continue;
44
+
45
+ // skip function parameters
46
+ if(binding.kind === "param") continue;
47
+
48
+ // skip destructuring
49
+ if(binding.path.isObjectPattern?.()) continue;
50
+ if(binding.path.isArrayPattern?.()) continue;
51
+
52
+ // skip globals
53
+ if(!binding.scope.parent) continue;
54
+
55
+ const newName=randomName();
56
+
57
+ path.scope.rename(key,newName);
58
+
59
+ }
60
+
61
+ }
62
+ });
63
+
64
+ }
@@ -0,0 +1,66 @@
1
+ import { rc4 } from "./rc4.js"
2
+
3
+ export function buildStringRuntime(pool){
4
+
5
+ const key=Math.random().toString(36).slice(2)
6
+
7
+ const encrypted=pool.map(s=>{
8
+
9
+ const enc=rc4(key,s)
10
+
11
+ return Buffer.from(enc).toString("base64")
12
+
13
+ })
14
+
15
+ return `
16
+
17
+ var _STRINGS=${JSON.stringify(encrypted)};
18
+
19
+ (function(){
20
+
21
+ function rc4(k,str){
22
+
23
+ var s=[],j=0,x,res="";
24
+
25
+ for(var i=0;i<256;i++)s[i]=i;
26
+
27
+ for(i=0;i<256;i++){
28
+
29
+ j=(j+s[i]+k.charCodeAt(i%k.length))%256;
30
+
31
+ x=s[i];s[i]=s[j];s[j]=x;
32
+
33
+ }
34
+
35
+ i=0;j=0;
36
+
37
+ for(var y=0;y<str.length;y++){
38
+
39
+ i=(i+1)%256;
40
+ j=(j+s[i])%256;
41
+
42
+ x=s[i];s[i]=s[j];s[j]=x;
43
+
44
+ var k2=s[(s[i]+s[j])%256];
45
+
46
+ res+=String.fromCharCode(str.charCodeAt(y)^k2);
47
+
48
+ }
49
+
50
+ return res;
51
+
52
+ }
53
+
54
+ for(var i=0;i<_STRINGS.length;i++){
55
+
56
+ var data=atob(_STRINGS[i])
57
+
58
+ _STRINGS[i]=rc4("${key}",data)
59
+
60
+ }
61
+
62
+ })();
63
+
64
+ `
65
+
66
+ }
@@ -0,0 +1,23 @@
1
+ export function selfDefend(){
2
+
3
+ return `
4
+
5
+ (function(){
6
+
7
+ function guard(){
8
+
9
+ const src=guard.toString()
10
+
11
+ if(!src.includes("guard")){
12
+ while(true){}
13
+ }
14
+
15
+ }
16
+
17
+ setInterval(guard,4000)
18
+
19
+ })();
20
+
21
+ `;
22
+
23
+ }
@@ -0,0 +1,80 @@
1
+ import traverseModule from "@babel/traverse";
2
+ import * as t from "@babel/types";
3
+
4
+ const traverse = traverseModule.default;
5
+
6
+ export function encodeStrings(ast){
7
+
8
+ const pool = [];
9
+ const map = new Map();
10
+
11
+ function getIndex(value){
12
+
13
+ if(map.has(value)){
14
+ return map.get(value);
15
+ }
16
+
17
+ const index = pool.length;
18
+
19
+ pool.push(value);
20
+ map.set(value,index);
21
+
22
+ return index;
23
+
24
+ }
25
+
26
+ traverse(ast,{
27
+ StringLiteral(path){
28
+
29
+ const value = path.node.value;
30
+
31
+ /* skip unsafe places */
32
+
33
+ if(path.parent.type === "Directive") return;
34
+
35
+ if(
36
+ path.parent.type === "CallExpression" &&
37
+ path.parent.callee.name === "require"
38
+ ){
39
+ return;
40
+ }
41
+
42
+ if(path.parent.type === "ImportDeclaration"){
43
+ return;
44
+ }
45
+
46
+ if(
47
+ path.parent.type === "ObjectProperty" &&
48
+ path.parent.key === path.node
49
+ ){
50
+ return;
51
+ }
52
+
53
+ if(
54
+ path.parent.type === "MemberExpression" &&
55
+ path.parent.property === path.node
56
+ ){
57
+ return;
58
+ }
59
+
60
+ if(path.parent.type === "TemplateLiteral"){
61
+ return;
62
+ }
63
+
64
+ const index = getIndex(value);
65
+
66
+ path.replaceWith(
67
+ t.memberExpression(
68
+ t.identifier("_STRINGS"),
69
+ t.numericLiteral(index),
70
+ true
71
+ )
72
+ );
73
+
74
+ }
75
+
76
+ });
77
+
78
+ return pool;
79
+
80
+ }
@@ -0,0 +1,29 @@
1
+ import traverseModule from "@babel/traverse";
2
+
3
+ const traverse = traverseModule.default;
4
+
5
+ function toUnicode(str){
6
+
7
+ return str
8
+ .split("")
9
+ .map(c => "\\u" + c.charCodeAt(0).toString(16).padStart(4,"0"))
10
+ .join("");
11
+
12
+ }
13
+
14
+ export function unicodeEscape(ast){
15
+
16
+ traverse(ast,{
17
+ StringLiteral(path){
18
+
19
+ const val = path.node.value;
20
+
21
+ path.node.extra = {
22
+ raw: `"${toUnicode(val)}"`,
23
+ rawValue: val
24
+ };
25
+
26
+ }
27
+ });
28
+
29
+ }
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "jsarmor",
3
+ "version": "1.0.0",
4
+ "description": "Advanced JavaScript Obfuscator",
5
+ "type": "module",
6
+ "main": "cli/index.js",
7
+
8
+ "bin": {
9
+ "jsarmor": "./cli/index.js"
10
+ },
11
+
12
+ "scripts": {
13
+ "start": "node cli/index.js"
14
+ },
15
+
16
+ "dependencies": {
17
+ "@babel/parser": "^7.26.0",
18
+ "@babel/traverse": "^7.26.0",
19
+ "@babel/types": "^7.26.0",
20
+ "@babel/generator": "^7.26.0",
21
+ "chalk": "^5.3.0",
22
+ "gradient-string": "^2.0.2",
23
+ "cli-progress": "^3.12.0"
24
+ },
25
+
26
+ "keywords": [
27
+ "obfuscator",
28
+ "javascript",
29
+ "security",
30
+ "jsarmor"
31
+ ],
32
+
33
+ "files": [
34
+ "cli",
35
+ "core",
36
+ "utils"
37
+ ],
38
+
39
+ "author": "_kingktn",
40
+ "license": "MIT"
41
+ }
@@ -0,0 +1,14 @@
1
+ export function randomString(length = 6) {
2
+ const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
3
+ let result = "_0x";
4
+
5
+ for (let i = 0; i < length; i++) {
6
+ result += chars[Math.floor(Math.random() * chars.length)];
7
+ }
8
+
9
+ return result;
10
+ }
11
+
12
+ export function randomInt(min, max) {
13
+ return Math.floor(Math.random() * (max - min) + min);
14
+ }