luau-obfuscator 1.0.4 → 1.0.5
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/.github/workflows/release.yml +56 -56
- package/generated/grammers.luau +3377 -3410
- package/generated/tests.luau +1817 -1910
- package/package.json +1 -1
- package/scripts/test.js +21 -21
- package/smoketest/grammers.luau +1174 -1174
- package/smoketest/tests.luau +114 -114
- package/src/passes/ConstantArray.ts +90 -90
- package/src/passes/EncryptNumbers.ts +122 -122
- package/src/passes/EncryptStrings.ts +162 -162
- package/src/passes/GlobalMapping.ts +193 -193
- package/src/passes/InsertJunk.ts +184 -184
- package/src/passes/Minify.ts +5 -5
- package/src/passes/StripTypes.ts +191 -191
- package/src/passes/WrapInFunction.ts +31 -31
- package/tsconfig.json +20 -20
- package/tsup.config.ts +9 -9
package/src/passes/InsertJunk.ts
CHANGED
|
@@ -1,185 +1,185 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
Program, Block, Statement, Expression,
|
|
3
|
-
} from "luau-parser"
|
|
4
|
-
import {
|
|
5
|
-
identifier, numberLiteral, binary, localStatement, doStatement, block,
|
|
6
|
-
ifStatement, ifClause,
|
|
7
|
-
} from "./nodeFactory"
|
|
8
|
-
|
|
9
|
-
export interface InsertJunkOptions {
|
|
10
|
-
/** 기존 statement 하나 앞에 junk를 끼워넣을 확률 (0~1) */
|
|
11
|
-
probability: number
|
|
12
|
-
/** 블록 하나당 최대 삽입 개수 */
|
|
13
|
-
maxPerBlock: number
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function randomInt(min: number, max: number): number {
|
|
17
|
-
return Math.floor(Math.random() * (max - min + 1)) + min
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function randomName(): string {
|
|
21
|
-
return "_" + globalThis.crypto.randomUUID().replace(/-/g, "")
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function junkNumberExpr(): Expression {
|
|
25
|
-
const op = Math.random() < 0.5 ? "+" : "*"
|
|
26
|
-
return binary(op, numberLiteral(randomInt(1, 999)), numberLiteral(randomInt(1, 999)))
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* 실행돼도 관찰 가능한 부작용이 전혀 없는 더미 statement 하나를 만듦.
|
|
31
|
-
* - 매번 새 랜덤 이름을 쓰므로 다른 변수와 충돌하지 않음
|
|
32
|
-
* - if 분기 조건이 실제로 참/거짓 어느 쪽이어도 몸통이 쓸모없는 local 선언뿐이라
|
|
33
|
-
* 상관없음 (opaque predicate를 엄밀하게 항상 거짓으로 만들 필요가 없음)
|
|
34
|
-
*/
|
|
35
|
-
function buildJunkStatement(): Statement {
|
|
36
|
-
const variants: Array<() => Statement> = [
|
|
37
|
-
() => localStatement(randomName(), junkNumberExpr()),
|
|
38
|
-
() => doStatement(block([localStatement(randomName(), junkNumberExpr())])),
|
|
39
|
-
() => ifStatement([
|
|
40
|
-
ifClause(
|
|
41
|
-
binary("==", numberLiteral(randomInt(1, 999)), numberLiteral(randomInt(1, 999))),
|
|
42
|
-
block([localStatement(randomName(), junkNumberExpr())]),
|
|
43
|
-
),
|
|
44
|
-
]),
|
|
45
|
-
]
|
|
46
|
-
return variants[randomInt(0, variants.length - 1)]()
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function runInsertJunk(program: Program, options: InsertJunkOptions): void {
|
|
50
|
-
function processExpr(expr: Expression): void {
|
|
51
|
-
switch (expr.type) {
|
|
52
|
-
case "InterpolatedStringExpression":
|
|
53
|
-
for (const part of expr.parts) {
|
|
54
|
-
if (part.kind === "expression") processExpr(part.expression)
|
|
55
|
-
}
|
|
56
|
-
return
|
|
57
|
-
case "FunctionExpression":
|
|
58
|
-
processBlock(expr.func.body)
|
|
59
|
-
return
|
|
60
|
-
case "TableExpression":
|
|
61
|
-
for (const field of expr.fields) {
|
|
62
|
-
if (field.type === "TableFieldPositional") processExpr(field.value)
|
|
63
|
-
else if (field.type === "TableFieldNamed") processExpr(field.value)
|
|
64
|
-
else { processExpr(field.key); processExpr(field.value) }
|
|
65
|
-
}
|
|
66
|
-
return
|
|
67
|
-
case "BinaryExpression":
|
|
68
|
-
processExpr(expr.left)
|
|
69
|
-
processExpr(expr.right)
|
|
70
|
-
return
|
|
71
|
-
case "UnaryExpression":
|
|
72
|
-
processExpr(expr.argument)
|
|
73
|
-
return
|
|
74
|
-
case "MemberExpression":
|
|
75
|
-
processExpr(expr.object)
|
|
76
|
-
return
|
|
77
|
-
case "IndexExpression":
|
|
78
|
-
processExpr(expr.object)
|
|
79
|
-
processExpr(expr.index)
|
|
80
|
-
return
|
|
81
|
-
case "CallExpression":
|
|
82
|
-
processExpr(expr.callee)
|
|
83
|
-
expr.arguments.forEach(processExpr)
|
|
84
|
-
return
|
|
85
|
-
case "MethodCallExpression":
|
|
86
|
-
processExpr(expr.object)
|
|
87
|
-
expr.arguments.forEach(processExpr)
|
|
88
|
-
return
|
|
89
|
-
case "ParenthesizedExpression":
|
|
90
|
-
processExpr(expr.expression)
|
|
91
|
-
return
|
|
92
|
-
case "TypeAssertionExpression":
|
|
93
|
-
processExpr(expr.expression)
|
|
94
|
-
return
|
|
95
|
-
case "IfElseExpression":
|
|
96
|
-
for (const clause of expr.clauses) {
|
|
97
|
-
processExpr(clause.condition)
|
|
98
|
-
processExpr(clause.body)
|
|
99
|
-
}
|
|
100
|
-
processExpr(expr.alternate)
|
|
101
|
-
return
|
|
102
|
-
default:
|
|
103
|
-
return
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function processStatement(stmt: Statement): void {
|
|
108
|
-
switch (stmt.type) {
|
|
109
|
-
case "LocalStatement":
|
|
110
|
-
stmt.init.forEach(processExpr)
|
|
111
|
-
return
|
|
112
|
-
case "LocalFunctionStatement":
|
|
113
|
-
processBlock(stmt.func.body)
|
|
114
|
-
return
|
|
115
|
-
case "FunctionDeclarationStatement":
|
|
116
|
-
processBlock(stmt.func.body)
|
|
117
|
-
return
|
|
118
|
-
case "AssignmentStatement":
|
|
119
|
-
stmt.targets.forEach(processExpr)
|
|
120
|
-
stmt.values.forEach(processExpr)
|
|
121
|
-
return
|
|
122
|
-
case "CompoundAssignmentStatement":
|
|
123
|
-
processExpr(stmt.target)
|
|
124
|
-
processExpr(stmt.value)
|
|
125
|
-
return
|
|
126
|
-
case "CallStatement":
|
|
127
|
-
processExpr(stmt.expression)
|
|
128
|
-
return
|
|
129
|
-
case "DoStatement":
|
|
130
|
-
processBlock(stmt.body)
|
|
131
|
-
return
|
|
132
|
-
case "WhileStatement":
|
|
133
|
-
processExpr(stmt.condition)
|
|
134
|
-
processBlock(stmt.body)
|
|
135
|
-
return
|
|
136
|
-
case "RepeatStatement":
|
|
137
|
-
processBlock(stmt.body)
|
|
138
|
-
processExpr(stmt.condition)
|
|
139
|
-
return
|
|
140
|
-
case "IfStatement":
|
|
141
|
-
for (const clause of stmt.clauses) {
|
|
142
|
-
processExpr(clause.condition)
|
|
143
|
-
processBlock(clause.body)
|
|
144
|
-
}
|
|
145
|
-
if (stmt.alternate) processBlock(stmt.alternate)
|
|
146
|
-
return
|
|
147
|
-
case "NumericForStatement":
|
|
148
|
-
processExpr(stmt.start)
|
|
149
|
-
processExpr(stmt.end)
|
|
150
|
-
if (stmt.step) processExpr(stmt.step)
|
|
151
|
-
processBlock(stmt.body)
|
|
152
|
-
return
|
|
153
|
-
case "GenericForStatement":
|
|
154
|
-
stmt.iterators.forEach(processExpr)
|
|
155
|
-
processBlock(stmt.body)
|
|
156
|
-
return
|
|
157
|
-
case "ReturnStatement":
|
|
158
|
-
stmt.arguments.forEach(processExpr)
|
|
159
|
-
return
|
|
160
|
-
default:
|
|
161
|
-
return
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/** 자식 블록들부터 먼저 처리한 뒤(재귀 무한 방지 — 새로 끼워넣는 junk는
|
|
166
|
-
* 다시 처리하지 않음), 현재 블록에 junk를 끼워넣음. junk는 기존 statement
|
|
167
|
-
* "앞"에만 넣으므로 return/break/continue(항상 블록 맨 끝에만 올 수 있음)
|
|
168
|
-
* 뒤에 잘못 삽입될 일이 없음. */
|
|
169
|
-
function processBlock(blk: Block): void {
|
|
170
|
-
for (const stmt of blk.statements) processStatement(stmt)
|
|
171
|
-
|
|
172
|
-
const result: Statement[] = []
|
|
173
|
-
let inserted = 0
|
|
174
|
-
for (const stmt of blk.statements) {
|
|
175
|
-
if (inserted < options.maxPerBlock && Math.random() < options.probability) {
|
|
176
|
-
result.push(buildJunkStatement())
|
|
177
|
-
inserted++
|
|
178
|
-
}
|
|
179
|
-
result.push(stmt)
|
|
180
|
-
}
|
|
181
|
-
blk.statements = result
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
processBlock(program.body)
|
|
1
|
+
import type {
|
|
2
|
+
Program, Block, Statement, Expression,
|
|
3
|
+
} from "luau-parser"
|
|
4
|
+
import {
|
|
5
|
+
identifier, numberLiteral, binary, localStatement, doStatement, block,
|
|
6
|
+
ifStatement, ifClause,
|
|
7
|
+
} from "./nodeFactory"
|
|
8
|
+
|
|
9
|
+
export interface InsertJunkOptions {
|
|
10
|
+
/** 기존 statement 하나 앞에 junk를 끼워넣을 확률 (0~1) */
|
|
11
|
+
probability: number
|
|
12
|
+
/** 블록 하나당 최대 삽입 개수 */
|
|
13
|
+
maxPerBlock: number
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function randomInt(min: number, max: number): number {
|
|
17
|
+
return Math.floor(Math.random() * (max - min + 1)) + min
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function randomName(): string {
|
|
21
|
+
return "_" + globalThis.crypto.randomUUID().replace(/-/g, "")
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function junkNumberExpr(): Expression {
|
|
25
|
+
const op = Math.random() < 0.5 ? "+" : "*"
|
|
26
|
+
return binary(op, numberLiteral(randomInt(1, 999)), numberLiteral(randomInt(1, 999)))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 실행돼도 관찰 가능한 부작용이 전혀 없는 더미 statement 하나를 만듦.
|
|
31
|
+
* - 매번 새 랜덤 이름을 쓰므로 다른 변수와 충돌하지 않음
|
|
32
|
+
* - if 분기 조건이 실제로 참/거짓 어느 쪽이어도 몸통이 쓸모없는 local 선언뿐이라
|
|
33
|
+
* 상관없음 (opaque predicate를 엄밀하게 항상 거짓으로 만들 필요가 없음)
|
|
34
|
+
*/
|
|
35
|
+
function buildJunkStatement(): Statement {
|
|
36
|
+
const variants: Array<() => Statement> = [
|
|
37
|
+
() => localStatement(randomName(), junkNumberExpr()),
|
|
38
|
+
() => doStatement(block([localStatement(randomName(), junkNumberExpr())])),
|
|
39
|
+
() => ifStatement([
|
|
40
|
+
ifClause(
|
|
41
|
+
binary("==", numberLiteral(randomInt(1, 999)), numberLiteral(randomInt(1, 999))),
|
|
42
|
+
block([localStatement(randomName(), junkNumberExpr())]),
|
|
43
|
+
),
|
|
44
|
+
]),
|
|
45
|
+
]
|
|
46
|
+
return variants[randomInt(0, variants.length - 1)]()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function runInsertJunk(program: Program, options: InsertJunkOptions): void {
|
|
50
|
+
function processExpr(expr: Expression): void {
|
|
51
|
+
switch (expr.type) {
|
|
52
|
+
case "InterpolatedStringExpression":
|
|
53
|
+
for (const part of expr.parts) {
|
|
54
|
+
if (part.kind === "expression") processExpr(part.expression)
|
|
55
|
+
}
|
|
56
|
+
return
|
|
57
|
+
case "FunctionExpression":
|
|
58
|
+
processBlock(expr.func.body)
|
|
59
|
+
return
|
|
60
|
+
case "TableExpression":
|
|
61
|
+
for (const field of expr.fields) {
|
|
62
|
+
if (field.type === "TableFieldPositional") processExpr(field.value)
|
|
63
|
+
else if (field.type === "TableFieldNamed") processExpr(field.value)
|
|
64
|
+
else { processExpr(field.key); processExpr(field.value) }
|
|
65
|
+
}
|
|
66
|
+
return
|
|
67
|
+
case "BinaryExpression":
|
|
68
|
+
processExpr(expr.left)
|
|
69
|
+
processExpr(expr.right)
|
|
70
|
+
return
|
|
71
|
+
case "UnaryExpression":
|
|
72
|
+
processExpr(expr.argument)
|
|
73
|
+
return
|
|
74
|
+
case "MemberExpression":
|
|
75
|
+
processExpr(expr.object)
|
|
76
|
+
return
|
|
77
|
+
case "IndexExpression":
|
|
78
|
+
processExpr(expr.object)
|
|
79
|
+
processExpr(expr.index)
|
|
80
|
+
return
|
|
81
|
+
case "CallExpression":
|
|
82
|
+
processExpr(expr.callee)
|
|
83
|
+
expr.arguments.forEach(processExpr)
|
|
84
|
+
return
|
|
85
|
+
case "MethodCallExpression":
|
|
86
|
+
processExpr(expr.object)
|
|
87
|
+
expr.arguments.forEach(processExpr)
|
|
88
|
+
return
|
|
89
|
+
case "ParenthesizedExpression":
|
|
90
|
+
processExpr(expr.expression)
|
|
91
|
+
return
|
|
92
|
+
case "TypeAssertionExpression":
|
|
93
|
+
processExpr(expr.expression)
|
|
94
|
+
return
|
|
95
|
+
case "IfElseExpression":
|
|
96
|
+
for (const clause of expr.clauses) {
|
|
97
|
+
processExpr(clause.condition)
|
|
98
|
+
processExpr(clause.body)
|
|
99
|
+
}
|
|
100
|
+
processExpr(expr.alternate)
|
|
101
|
+
return
|
|
102
|
+
default:
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function processStatement(stmt: Statement): void {
|
|
108
|
+
switch (stmt.type) {
|
|
109
|
+
case "LocalStatement":
|
|
110
|
+
stmt.init.forEach(processExpr)
|
|
111
|
+
return
|
|
112
|
+
case "LocalFunctionStatement":
|
|
113
|
+
processBlock(stmt.func.body)
|
|
114
|
+
return
|
|
115
|
+
case "FunctionDeclarationStatement":
|
|
116
|
+
processBlock(stmt.func.body)
|
|
117
|
+
return
|
|
118
|
+
case "AssignmentStatement":
|
|
119
|
+
stmt.targets.forEach(processExpr)
|
|
120
|
+
stmt.values.forEach(processExpr)
|
|
121
|
+
return
|
|
122
|
+
case "CompoundAssignmentStatement":
|
|
123
|
+
processExpr(stmt.target)
|
|
124
|
+
processExpr(stmt.value)
|
|
125
|
+
return
|
|
126
|
+
case "CallStatement":
|
|
127
|
+
processExpr(stmt.expression)
|
|
128
|
+
return
|
|
129
|
+
case "DoStatement":
|
|
130
|
+
processBlock(stmt.body)
|
|
131
|
+
return
|
|
132
|
+
case "WhileStatement":
|
|
133
|
+
processExpr(stmt.condition)
|
|
134
|
+
processBlock(stmt.body)
|
|
135
|
+
return
|
|
136
|
+
case "RepeatStatement":
|
|
137
|
+
processBlock(stmt.body)
|
|
138
|
+
processExpr(stmt.condition)
|
|
139
|
+
return
|
|
140
|
+
case "IfStatement":
|
|
141
|
+
for (const clause of stmt.clauses) {
|
|
142
|
+
processExpr(clause.condition)
|
|
143
|
+
processBlock(clause.body)
|
|
144
|
+
}
|
|
145
|
+
if (stmt.alternate) processBlock(stmt.alternate)
|
|
146
|
+
return
|
|
147
|
+
case "NumericForStatement":
|
|
148
|
+
processExpr(stmt.start)
|
|
149
|
+
processExpr(stmt.end)
|
|
150
|
+
if (stmt.step) processExpr(stmt.step)
|
|
151
|
+
processBlock(stmt.body)
|
|
152
|
+
return
|
|
153
|
+
case "GenericForStatement":
|
|
154
|
+
stmt.iterators.forEach(processExpr)
|
|
155
|
+
processBlock(stmt.body)
|
|
156
|
+
return
|
|
157
|
+
case "ReturnStatement":
|
|
158
|
+
stmt.arguments.forEach(processExpr)
|
|
159
|
+
return
|
|
160
|
+
default:
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** 자식 블록들부터 먼저 처리한 뒤(재귀 무한 방지 — 새로 끼워넣는 junk는
|
|
166
|
+
* 다시 처리하지 않음), 현재 블록에 junk를 끼워넣음. junk는 기존 statement
|
|
167
|
+
* "앞"에만 넣으므로 return/break/continue(항상 블록 맨 끝에만 올 수 있음)
|
|
168
|
+
* 뒤에 잘못 삽입될 일이 없음. */
|
|
169
|
+
function processBlock(blk: Block): void {
|
|
170
|
+
for (const stmt of blk.statements) processStatement(stmt)
|
|
171
|
+
|
|
172
|
+
const result: Statement[] = []
|
|
173
|
+
let inserted = 0
|
|
174
|
+
for (const stmt of blk.statements) {
|
|
175
|
+
if (inserted < options.maxPerBlock && Math.random() < options.probability) {
|
|
176
|
+
result.push(buildJunkStatement())
|
|
177
|
+
inserted++
|
|
178
|
+
}
|
|
179
|
+
result.push(stmt)
|
|
180
|
+
}
|
|
181
|
+
blk.statements = result
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
processBlock(program.body)
|
|
185
185
|
}
|
package/src/passes/Minify.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export function minifyPrinted(code: string): string {
|
|
2
|
-
return code
|
|
3
|
-
.replace(/\r\n|\r|\n/g, " ")
|
|
4
|
-
.replace(/[ \t]+/g, " ")
|
|
5
|
-
.trim()
|
|
1
|
+
export function minifyPrinted(code: string): string {
|
|
2
|
+
return code
|
|
3
|
+
.replace(/\r\n|\r|\n/g, " ")
|
|
4
|
+
.replace(/[ \t]+/g, " ")
|
|
5
|
+
.trim()
|
|
6
6
|
}
|