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 +177 -0
- package/core/antidebug.js +15 -0
- package/core/beautifyGuard.js +21 -0
- package/core/controlflow.js +150 -0
- package/core/deadcode.js +91 -0
- package/core/junkcode.js +18 -0
- package/core/parser.js +21 -0
- package/core/rc4.js +42 -0
- package/core/renamer.js +64 -0
- package/core/runtime.js +66 -0
- package/core/selfdefend.js +23 -0
- package/core/stringArray.js +80 -0
- package/core/unicode.js +29 -0
- package/package.json +41 -0
- package/utils/random.js +14 -0
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,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
|
+
}
|
package/core/deadcode.js
ADDED
|
@@ -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
|
+
}
|
package/core/junkcode.js
ADDED
|
@@ -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
|
+
}
|
package/core/renamer.js
ADDED
|
@@ -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
|
+
}
|
package/core/runtime.js
ADDED
|
@@ -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
|
+
}
|
package/core/unicode.js
ADDED
|
@@ -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
|
+
}
|
package/utils/random.js
ADDED
|
@@ -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
|
+
}
|