Releases: Rainyqwq/Lua-Obfuscator
Release list
v3.4.0 — editor cursor alignment, VM advanced mode, theme spread animation
Lua Obfuscator v3.4.0 Release Notes
发布日期:2026-08-20
概要
- 编辑器光标错位修复:textarea 不再被
min-height:100%钳在容器高度内,强制以scrollHeight长高,光标永远停在正确的 gutter 行号上 - VM 高级模式生效:
vm_mode='advanced'不再被 JS 桥全部推成 boolean / 被 worker 的typeof value === 'boolean'过滤器丢弃 - 主题切换扩散动画:基于
document.startViewTransition(Chrome 111+/Edge 111+)+ JS fallback,从按钮位置圆形扩散/收缩回按钮位置,0.28s linear - UI 设计规范合规(Apple Liquid Glass):token 化所有颜色/圆角/阴影/缓动/字号;3 个 panel 视觉统一
- 全局动效统一:13 处裸
.15stransition 全部补var(--ease-out-quart);loading overlay/scrim 用项目缓动 token - 可访问性:
prefers-reduced-motion/prefers-reduced-transparency/prefers-contrast三条 media query 完整覆盖 - 代码健壮性:9 处隐藏 bug 修复(worker race / Worker race / memory leak / null deref / duplicate resize listener 等)
- 新增 design tokens:
--danger/--warn/--dark-mode 变体全部 5 处硬编码替换
修复
编辑器光标错位(核心 bug)
<textarea> 在 height:auto + min-height:100% + overflow:hidden 三者并存时不会随内容自动长高 —— 它被钉在 scroll 容器高度处。当用户输入很多行时,文本溢出被隐藏,但 caret 仍画在 textarea box 内。结果:在末行按 Enter 时光标跳到错误的位置(差约一行高)。
修复(index.html):
- 移除
min-height:100% - 在每次
updateLineCount()中强制textarea.style.height = textarea.scrollHeight + 'px',让 textarea 跟随内容长高
实测对齐偏差从 22.5px → ≤0.19px(亚像素圆整)。
VM 高级模式开关无效
JS→Worker 消息桥把 options 中所有 value 都过滤成 boolean(if (typeof value === 'boolean')),丢弃了 vm_mode='advanced' 等字符串。同时 sync 路径的 obfuscateLua() 走相同过滤逻辑。
修复:
- Worker 的
onmessage按typeof分发 boolean / number / string / array / null obfuscateLuaAsync/obfuscateLua同样改为按类型分发- 数组(
name_whitelist/string_whitelist)也走 table push
_luaObfuscatorReady 单 flag bug
单一 flag 同时代表 main-thread Lua + Worker 都已初始化。但两者是异步独立完成的:主线程同步,Worker 异步。Worker 还没 ready 时点击混淆 → 直接拒绝。
修复:拆成三个 flag:
_luaMainReady— 主线程 Lua 已初始化_luaWorkerReady— Worker 已 postMessageready_luaObfuscatorReady=_luaMainReady && _luaWorkerReady(派生)
obfuscateLuaAsync 快速双击 → 首个 Promise 永远 hang
obfuscateLuaAsync 直接覆盖 _obfuscateCallbacks,没有 reject 前一个 → 第一个 promise 永远不 resolve。修复:覆盖前先 reject(new Error('新的混淆请求已发起,上一个被取消'))。
Worker 内存泄漏
每次 new Worker(URL.createObjectURL(blob)) 都没 revokeObjectURL。URL.createObjectURL 是 ObjectURL,每条都持久占用。修复:Worker 构造后立即 revokeObjectURL(Worker 已快照引用,安全)。
Worker 报错后 handle 仍占用
onerror 设 _luaObfuscatorReady=false 但不 terminate worker → 下次 postMessage 投到死 handle。修复:onerror 里 terminate() + _obfuscateWorker = null,下次自动重建。
Resizer 拖拽时每帧 window.matchMedia(...)
isMobile() 在 mousemove 高频事件里创建新 MediaQueryList。修复:缓存 MQL 到 closure 外层。
DOM null deref(多处)
appendOutput、clearOutput、doObfuscate等:加 null guard- 删除
line-count/output-status后残留的 JS 引用加 null guard
重复的 window.addEventListener('resize', ...)
旧 syncGutterScroll listener 还在;新的在 initUI 里。修复:删旧的。
新增
Theme 切换扩散动画
1. Path 1 — View Transitions API(Chrome 111+/Edge 111+)
document.startViewTransition(() => {
swap(); // 切换 :root.dark
});
transition.ready.then(() => {
// 用 WAAPI 动画 clip-path:从点击位置扩散/收缩回
document.documentElement.animate(
{ clipPath: isDark ? expand : shrink },
{ duration: 280, easing: 'linear', pseudoElement: '::view-transition-...' }
);
});light → dark:新 snapshot(dark)从点击位置扩散覆盖 viewportdark → light:旧 snapshot(dark)从全屏收缩回按钮位置
2. Path 2 — JS fallback(旧浏览器)
body 级 .theme-veil div,clip-path: circle() 从 0 扩到 endRadius(或反向)。veil 颜色 = OLD theme;50% 时 swap class + 切换 veil 颜色 = NEW theme。动画结束 display:none + opacity:0 双重隐藏,避免合成器残留。
3. 闪烁修复
::view-transition-old(root) 配 @keyframes vt-hide { from { opacity:1 } to { opacity:0 } },在 260ms 内淡出,配合 transition.skipTransition() 在 WAAPI finish 事件强制终止 SVT —— 彻底消除"dark→light 末尾黑色闪烁"。
Startup 进入动画
四个元素 stagger 进入(总时长 < 600ms):
nav从 -12px 滑入(delay 0.05s)- 左 panel 从 +14px 滑入(delay 0.18s)
- 右 panel 从 +14px 滑入(delay 0.30s)
- 动作条 + scale(.96) 弹入(delay 0.46s)
prefers-reduced-motion 下所有 delay 归零,立即落到 resting state。
实时跟随系统主题
Theme.userPref 区分"用户已锁定"和"跟随系统"。matchMedia.addEventListener('change', ...) 监听 OS 主题变化;锁定后不再被 OS变化覆盖。带旧浏览器 addListener fallback。
预渲染主题 class(无 FOUC)
<head> 中插入同步小脚本,在 fengari/JS 加载前就根据 locallocalStorage + prefers-color-scheme 给 <html> 加 dark/light class —— 首屏渲染前不闪烁。
Design Tokens 补全
:root {
--danger: #ff375f;
--danger-bg: rgba(255,55,95,.1);
--warn: #ff9f0a;
--warn-bg: rgba(255,159,10,.1);
}
:root.dark {
--danger: #ff5e7a;
--danger-bg: rgba(255,94,122,.12);
--warn: #ffb340;
--warn-bg: rgba(255,179,64,.12);
}替换 5 处硬编码 #ff375f / #ff9f0a:
.panel-header .badge.status-err/.status-warn.btn-danger(含 hover bg).tok-number、.output-sink .error、.output-sink .warn.batch-file-row .fstatus.error、.fbtn:hover
UI 重做(按 DESIGN 设计规范)
Segmented control(快速/均衡/最强/自定义)
- pill
::after从left+width动画改为transform: translateX + scaleX(compositor 友好) - duration 0.35s → 0.22s(Apple HIG)
- 移动端
flex-wrap:wrap时隐藏 pill,按钮变 plain chip,避免跨行穿模
iOS Switch
- track / thumb 统一 0.22s(之前 0.2s + 0.25s 不同步)
- thumb 高度 20px → 18px,更轻盈
- translateX(16px) 落点改精确
Output panel
- radius
--r-panel(22px) →--r-card(18px),与邻居面板统一 - shadow
--sh-card→--sh-panel,与邻居面板统一 - 去掉 1px hairline border(shadow 已经分隔了)
Stats bar
- 左右两栏 padding 统一
var(--pad-x)(之前输出栏用 24px,输入栏 22px,1-5px 错位) - 加
border-top:1px solid var(--hairline)
Action bar
- 所有按钮统一 32px 高(之前主按钮 30px,cluster 34px,4px 错位)
- cluster 用
inset 0 0 0 1px var(--hairline)描边,内部按钮-1px 0 0 var(--hairline)分隔线 - 图标
stroke-width:1.75+currentColor+ round caps(DESIGN icons.md) - 隐藏"全选/全不选"按钮(按 DESIGN 简化)
Whitelist input
- radius
--r-chip(6px) →--r-thumb(12px) - focus
box-shadow: 0 0 0 3pxhalo +z-index:1(不被.unified-panel的overflow:hidden截断)
Placeholder
- 重命名
.ic→.placeholder-icon(避免与 utility.ic冲突) - HTML 用
.placeholder-icon/.placeholder-title/.placeholder-hint三个 scoped class
全局动效统一
| 位置 | 旧 | 新 |
|---|---|---|
13 处 .15s transition |
无 easing(默认 ease) |
加 var(--ease-out-quart) |
.loading-overlay / .drawer-scrim |
ease |
var(--ease-out-quart) |
.btn:active |
100ms 裸数字 |
80ms var(--ease-out-quart) |
.btn transition |
多 duration/easing 混合 | 统一 transform .22s var(--ease-out-quart) |
可访问性
三条 media query 完整覆盖:
@media (prefers-reduced-motion: reduce) {
/* 所有 transition / animation: none */
}
@media (prefers-reduced-transparency: reduce) {
/* glass / backdrop-filter 退化为实色 */
}
@media (prefers-contrast: more) {
/* border 加粗,开关描边可见 */
}Editor 块下的 .enter-nav / .enter-panel / .enter-cta 也在 reduced-motion 下用 0.01s 动画立即落到 resting state。
版本
| 模块 | 版本 |
|---|---|
| index.html (Web) | v3.4.0 |
| package.json | 3.4.0 |
| package-lock.json | 3.4.0 |
| obfuscator.lua | v3.3.0 |
| passes/vm.lua | v3.3.0 |
| passes/vm_advanced.lua | v3.1.0 |
| passes/vm_protect.lua | v2.9.2 |
| passes/num_encrypt.lua | v1.4.0 |
| passes/header.lua | v1.1.1 |
| passes/api_indirect.lua | v1.0.0 |
| passes/string_pool.lua | v1.2.0 |
| passes/var_mangle.lua | v1.2.0 |
| passes/cf_flatten.lua | v1.0.0 |
| passes/bcf.lua | v1.0.0 |
| passes/bb_split.lua | v1.0.0 |
| passes/adv_fake_cf.lua | v1.1.1 |
| passes/junk_comment.lua | v1.0.1 |
| passes/anti_debug.lua | v1.0.0 |
| passes/call_indirect.lua | v1.0.0 |
| passes/string_restore.lua | v1.0.0 |
测试
- 编辑器:headless Edge 实测 caret 对齐 ≤ 0.19px 偏差(27 / 28 / 31 行)
- 混淆:
obfuscate('local x = 1')在 JS fallback 路径 OK,veils 元素创建成功 - Theme:headless Edge 实测
startViewTransition=true,Theme.mq 监听器绑定成功 - 可访问性:
prefers-reduced-motion下动画时长 0.01ms
构建
obfuscator_bundle.lua:22 模块index.html:Web 版本同步更新- 体积:index.html 编辑器 CSS + JS 总长 ≈ 50KB(含 FENGARI_SOURCE / OBFUSCATOR_LUA / build_html.js 注入的 ≈ 220KB 之外)
Migration
无破坏性变更。从 v3.3.0 升级:
- 替换
index.html package.json/package-lock.json的 version 字段更新(npm install 不会重新装,只是 metadata)- 主题切换体验变化:之前是瞬时切换,现在是 0.28s linear wipe 动画(如果用户希望关闭,
prefers-reduced-motion: reduce已自动跳过) - 可访问性:reduced-motion / reduced-transparency / contrast 用户体验改善
Known Issues
- Worker 在
file://协议下仍打印"Unsafe attempt to load URL from frame with URL file://..."—— Chrome 同源策略限制(每个 file: URL 是独立 opaque origin)。Worker 仍正常运行,警告仅是信息性。发行版打包为 Electron 或用chrome://flags/#unsafely-treat-insecure-origin-as-secure启动可消除。
License
MIT · Author: Rainy_qwq · GitHub
v2.10.0
Lua Obfuscator v2.10.0 Release Notes
发布日期:2026-07-24
概要
P2 函数级 VM 保护:通过 --@vm 注解选择性保护关键函数,兼顾强度与性能。
新增
函数级 VM 保护 (Function-Level VM)
- 新增
vm_functionPass(passes/vm_function.lua) - 通过
--@vm注解标记需要保护的顶层函数 - 每个函数独立编译为 VM 字节码,运行时按需解密执行
- 与整文件 VM (
vm_protect) 互斥:启用函数级 VM 时自动关闭整文件 VM
注解语法
--@vm
local function sensitive_algorithm(data)
-- 此函数将被 VM 字节码虚拟化保护
return transform(data)
end
local function normal_function(x)
-- 此函数不受影响
return x + 1
endCLI
--vm-function:启用函数级 VM 保护
Web
- 新增 "函数级VM保护" 复选框
- 预设:balanced/max 默认开启,fast 默认关闭
修复
- 修复
protect_as_expr函数体解析 bug(func_stmt.body→func_stmt.body.stmts) - 修复
protect_as_expr输出作用域问题(移除外层do...end包裹)
测试
tests/test_vm_function.lua:4 个测试用例- 单函数保护
- 多函数选择性保护
- 递归函数保护
- 无注解时透传
版本
- 版本号:v2.10.0
- CLI / Web / 输出头 / package.json / 文档均已更新
升级
git pull origin mainv2.9.0
Lua Obfuscator v2.9.0 Release Notes
?????2026-07-23
??
P1 ????????????/??????????????CLI + Web??
??
????
fast?????? + ???? + ???? + ????????balanced??????? VM / ????max??????? VM + ????
CLI?--preset fast|balanced|max
Web????????
???
- ?????
name_whitelist/--preserve-name <id>???????????? - ??????
string_whitelist/--preserve-string <s>?????????????
??????
- CLI?
--export-config <file>/--import-config <file>?Lua ?? - Bridge?
export_user_config/import_user_config/apply_preset/list_presets - Web???/?? JSON ??
??
tests/test_p1_presets_whitelist.lua- CI ?? P1 ??
- ?? P0 / full / string_pool ????
??
lua build_bundle.lua
node build_html.js
lua tests/test_p1_presets_whitelist.luaWeb ???? Ctrl+F5 ?????
License
MIT ? Rainy_qwq
v2.8.2
Lua Obfuscator v2.8.2 Release Notes
发布日期:2026-07-23
概要
P0 工程质量:自动化回归、流水线超时、Web 混淆超时收紧。
新增
tests/test_p0_regression.lua:单 Pass / 组合 / 语义对拍 / 大文件防挂死 / VM 冒烟.github/workflows/ci.yml:CI 跑 P0 + test_full + string_pool 回归PassManager:run(..., { max_total_ms }):整条流水线总耗时上限- 主程序默认
max_total_ms = 120000 - Web 异步混淆超时 5 分钟 → 90 秒,失败可取消并提示
升级
lua tests/test_p0_regression.lua
lua build_bundle.lua && node build_html.jsLicense
MIT · Rainy_qwq
v2.8.1
Lua Obfuscator v2.8.1 Release Notes
发布日期:2026-07-23
概要
v2.8.1 是一次紧急稳定性修复:修复变量名混淆在含表构造输入上的死循环,以及默认功能开关策略调整。
修复
variable_mangling 死循环(关键)
- 文件:
passes/var_mangle.lua - 现象: 浏览器即使用户不开启 VM,混淆含
{ ... }的源码(例如仓库内passes/instr_sub.lua)也会页面卡死。 - 根因:
collect_table_keys扫描表构造时,遇到{/}调整depth却未推进扫描下标i,导致无限循环。 - 修复: 在
depth + 1/depth - 1时同步i = i + 1。 - 验证: 单独变量名混淆约 28ms;默认全开(无 VM)约 156ms 完成。
行为变化
默认启用的 Pass
除以下两项外,全部默认开启:
| 默认关闭 | 说明 |
|---|---|
vm_protect |
VM 字节码虚拟化 |
anti_debug |
反调试检测 |
默认开启包括:字符串加密、变量名混淆、数字加密、指令替换、虚假控制流、控制流平坦化、BCF、基本块拆分、调用间接化、垃圾注释等。
版本标识
- CLI / Web / 输出头部统一为 v2.8.1
升级
git pull
lua build_bundle.lua
node build_html.jsWeb 用户请 Ctrl+F5 强制刷新。
完整变更基线
更完整的 2.8 系列能力与修复见 RELEASE_NOTES_v2.8.0.md。
License
MIT · Author: Rainy_qwq
https://github.com/Rainyqwq/Lua-Obfuscator
v2.8.0
Lua Obfuscator v2.8.0 Release Notes
发布日期:2026-07-23
概要
v2.8.0 聚焦正确性与可组合性:修掉 VM / 数字加密 / BCF 等会导致错误结果或浏览器卡死的缺陷,并取消“启用 VM 时静默禁用其它 Pass”的做法。Pass 应各自正确、可任意组合。
新功能与能力(累计至 2.8.0)
- 增强字符串加密(string_pool)
每字符串独立密钥、密文切片乱序、哈希索引、多态解码器、诱饵解码路径;Fengari 下使用 32 位安全算术,避免number has no integer representation。 - 反调试(anti_debug)
debug hook / 计时异常 / JIT 状态检测;Web 端放宽计时阈值,减少误报。 - 调用间接化(call_indirection)
全局函数调用经分发表间接化。 - VM op-pool + char-pool
运行时操作码映射打乱 + 字符池编码,贴近真实 VM 保护形态。 - Web
运行超时 hook;功能开关默认可配置;移动端适配相关改动保留。
关键修复
VM(passes/vm.lua)
- 数值
for支持负步长(修复for i=#t,1,-1等,字符串解密器依赖此语义)。 - 修正
FORLOOP继续条件运算符优先级错误(原先正步长几乎不退出 → 卡死)。 - 解释器增加步数上限,损坏字节码时快速失败而非挂死浏览器。
常量数字加密(passes/num_encrypt.lua)
- 禁止不安全的
(n<<k)>>k在高位溢出后回不来的编码。 - 加法路径保证
sum不超出目标位宽,避免 hex 截断。 - 手写 hex /
to_u32,兼容 Fengari。
BCF(passes/bcf.lua)
- 仅包装完整、括号平衡的单行语句;禁止打断多行
function(/ 未闭合调用(修复'end' expected near 'else')。
流水线(obfuscator.lua)
- 移除启用 VM 时自动禁用 instruction_substitution / control_flow_flattening / bogus_control_flow / basic_block_splitting 的逻辑。
其它
- string_pool FNV/LCG 全面 32 位安全。
- 头注释版本统一为 v2.8。
破坏性/行为变化
- 启用 VM 后,用户勾选的结构类 Pass 会真正执行(不再被静默关掉)。若旧文档写“VM 会自动禁用三者”,以本版为准。
- 混淆输出头部标识:
Lua Obfuscator v2.8。
验证
tests/test_full.lua:119/119- 全 Pass ± VM 对算法样例多轮回归通过
- Fengari 执行 VM 输出样例通过
升级说明
- 拉取本版本后执行:
lua build_bundle.lua && node build_html.js - Web 用户 Ctrl+F5 刷新
- 建议重新混淆旧产物(旧 VM 循环语义有缺陷)
License
MIT · Author: Rainy_qwq
https://github.com/Rainyqwq/Lua-Obfuscator
v2.7.0
v2.7.0 Release Notes
v2.7.0 VM 新增 opcode + 改动
新增
1. Opcode 混淆 Op ID
- 随机化 opcode 映射表,使用 Fisher-Yates Shuffle
- 随机化后的 op ID
- 修改
H[runtime_op]()handler 替换所有 if/elseif - 新增
OP_ADD=10动态处理
2. 新增 Char Pool(字符池)
- 运行时生成随机字符集替换部分 tostring 调用为
_chars索引 - 生成形式
(pool_index_1, pool_index_2, ...)替代原始_dec_str - 字符集大小 4-16 个字符
3. XOR 加密
- XOR 动态解密,使用 key + 随机 key 混合 key 生成
- Checksum 校验
对照表
| 功能 | 旧实现 | 新实现 |
|---|---|---|
| Opcode混淆 | 原始 if/elseif 分支 | handler 调用 H[op]() |
| 字符池 | string.byte(s,i) 逐字节 |
byte_to_idx[s[i]] 查表 |
| 字符串解密 | string.byte(tostring(k)) 解密 |
字节拼接 |
| 动态分发 | 固定 OP_ADD=10 分发 |
随机化后的 op id |
| 解密器 | 存储在 _d[] 中 |
字符 + 数字 + upvalue 组合为 _chars[] |
兼容性
- VM 适配 basic_block_splitting 多 Pass
- API 与 CLI 兼容
- 预留 op 扩展位
v2.6.0 性能优化 + 修复 init 检测
- 优化 var_mangle 字符串拼接从 O(n²) 到 O(n)
- 修复 Lua 脚本中 shebang 检测缺陷(
#!/后不能有 package.preload 等) - CLI 默认开启 basic_block_splitting
- release v2.6.0
v2.5.1 修复 CLI 兼容性
- 修复 Pass 管理器
- VM 与 Pass 兼容
v2.6.0
v2.6.0 Release Notes
性能优化
- var_mangle: 单次 scan_identifiers + table.concat 拼接,从 O(n*k) 逐次 sub 拼接优化为 O(n)
- string_pool: restore/restore_raw 改为单次 gsub 替代 N 次 gsub 循环,O(n) 替代 O(n*k)
- pass_manager: 缓存 os.clock 为局部变量,避免每 pass 重复 nil 检查
手机端响应式适配
- 768px + 480px 双断点 CSS
- 移动端面板垂直堆叠(左面板 40vh,右面板 flex:1)
- resizer 移动端跳过鼠标拖拽,resize 时清除 inline flex
- body 从 height:100vh;overflow:hidden 改为移动端 height:auto;overflow:auto
bb_split 默认开启
- CLI 默认 basic_block_splitting = true(obfuscator.lua)
- Web 端 bb_split toggle default = true(index.html)
Bug 修复
- BCF then/else 分支交换修复: 真实代码放 then(恒真谓词,总执行),假代码放 else,修复混淆后运行错误
- bb_split JS 模板字面量转义修复: build_html.js 将 bundle 中反斜杠翻倍后注入 index.html,修复 Fengari 运行时解析错误
测试
- test_full.lua: 119/119 通过
- test_repro_web.lua: 100/100 通过
- test_bisect.lua: 7/7 通过
v2.4.0-alpha
Full Changelog: https://github.com/Rainyqwq/Lua-Obfuscator/commits/v2.4.0-alpha