Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Catalog · How the catalog works
Anything · Lazarus or Delphi
Random projectRandom project

LuaDecompiler

Lua bytecode disassembler and decompiler for Lua 5.1, 5.2, 5.3, 5.4, and 5.5 binary chunks.

View on GitHub

Member rating

No ratings yet.

Sign in to rate this project.

README

cLuaDecompiler

cLuaDecompiler is a Free Pascal/Lazarus Lua bytecode disassembler and decompiler for Lua 5.1, 5.2, 5.3, 5.4, and 5.5 binary chunks.

The project focuses on version-aware bytecode parsing, semantic opcode mapping, SSA-based analysis, and Lua source emission that can be validated against the original program behavior when source is available.

Features

  • Disassembles Lua bytecode with version-aware opcode names and operands.
  • Decompiles .luac files to Lua-like source.
  • Dumps SSA IR for debugging control flow, register lifetimes, phi nodes, and decompiler decisions.
  • Supports built-in opcode tables for Lua 5.1 through 5.5.
  • Supports custom opcode-table loading for patched or obfuscated bytecode.
  • Includes runtime validation that compares original source output with decompiled output under the matching Lua version.
  • Includes source-less bytecode validation for cases where opcode/disassembly coverage is the source of truth.
  • Includes an optional VS Code extension for opening .luac files as decompiled Lua text.

Decompilation Examples

  • Check the examples folder.

Download

Requirements

  • Windows or another platform supported by Free Pascal/Lazarus.
  • Lazarus with LazBuild available in PATH.
  • Python 3 for the validation scripts.
  • Lua and luac binaries for validation, placed under the expected local layout when running the full suite:
text
lua/lua5.1/lua5.1.exe
lua/lua5.1/luac5.1.exe
lua/lua5.2/lua52.exe
lua/lua5.2/luac52.exe
lua/lua5.3/lua53.exe
lua/lua5.3/luac53.exe
lua/lua5.4/lua54.exe
lua/lua5.4/luac54.exe
lua/lua5.5/lua55.exe
lua/lua5.5/luac55.exe

Build

Build from the repository root with Lazarus:

powershell
LazBuild cLuaDecompiler.lpi

The main executable is produced as:

text
cLuaDecompiler.exe

Usage

powershell
.\cLuaDecompiler.exe [options] <file.luac>

Common commands:

powershell
.\cLuaDecompiler.exe --dis sample.luac
.\cLuaDecompiler.exe --dec sample.luac
.\cLuaDecompiler.exe --ssa-dump sample.luac
.\cLuaDecompiler.exe --dec --annotate sample.luac

Options:

text
--dis              Disassemble bytecode
--dec              Decompile to Lua-like source; default mode
--ssa-dump         Dump SSA IR for all protos
--annotate         Add disassembly comments during decompilation
--debug            Emit debug trace to stderr
--lua-version VER  Override detected Lua version: 51, 52, 53, 54, or 55
--opcode-table F   Load a custom opcode mapping from file F
--help             Show CLI help

Examples:

powershell
.\cLuaDecompiler.exe --dec script.luac > script.decompiled.lua
.\cLuaDecompiler.exe --dis script.luac > script.dis.txt
.\cLuaDecompiler.exe --ssa-dump script.luac > script.ssa.txt
.\cLuaDecompiler.exe --lua-version 51 --opcode-table opcode_tables\custom.txt patched.luac

Testing

Run the runtime and bytecode validation suite:

powershell
python run_decompiler_validation.py

Compile local Lua 5.1 fixtures manually:

powershell
tests\compile_luac.bat

run_decompiler_validation.py is the preferred validation path for decompiler correctness because it can:

  • compile source fixtures with the matching Lua version,
  • run the original source,
  • decompile the compiled bytecode,
  • run the decompiled output,
  • compare runtime output,
  • compile stripped-bytecode fixtures,
  • validate source-less bytecode through disassembly and opcode signatures.

Useful validation filters:

powershell
python run_decompiler_validation.py --versions 51
python run_decompiler_validation.py --timeout 10 --out-dir decompiled\validation_tmp

Adding Test Cases

Runtime-equivalence fixtures should either live in:

text
validation/fixtures/runtime/

or be referenced from:

text
validation/runtime_cases.json

Fixtures can declare supported versions at the top of the Lua file:

lua
-- versions: 51 52 53 54 55

Source-less bytecode cases belong in:

text
validation/bytecode_cases.json

Use validation/stubs/ for small fake environments needed to load or compile decompiled source outside the original host application. For bytecode without source, compare disassembly and opcode signatures rather than pretending there is a source-level oracle.

Do not commit generated .luac files from compiled/ or decompiler output from decompiled/.

Architecture Notes

The main pipeline is:

text
LuaChunk.pas -> LuaOpcodes.pas -> LuaSSA.pas -> LuaDecomp.pas/LuaDecomp*.inc

When adding support for a Lua version or opcode pattern:

  1. Verify chunk and instruction decoding in LuaChunk.pas.
  2. Add or correct semantic opcode mapping in LuaOpcodes.pas.
  3. Lift the semantic operation in LuaSSA.pas.
  4. Prefer fixing value/control-flow facts in SSA before patching emitted text.
  5. Update decompiler emission in the matching LuaDecomp*.inc file.
  6. Add runtime validation or source-less bytecode validation.

Useful debug flow:

powershell
.\cLuaDecompiler.exe --dis sample.luac > sample.dis.txt
.\cLuaDecompiler.exe --ssa-dump sample.luac > sample.ssa.txt
.\cLuaDecompiler.exe --dec --annotate sample.luac > sample.lua

The official Lua source browser is useful when opcode behavior or chunk format details are unclear:

text
https://www.lua.org/source/

VS Code Extension

The optional extension in vscode_plugin/ opens .luac files through cLuaDecompiler.exe and presents the result as normal Lua text so the existing Lua syntax highlighter can be used.

The extension can auto-detect cLuaDecompiler.exe near the workspace, or you can set:

text
cluaDecompiler.executablePath

Current Limitations

Decompiler output is best treated as recovered source, not guaranteed original source formatting. Local variable names, closure names, control-flow shape, and expression ordering may differ from the original program while still preserving runtime behavior.

Stripped bytecode and source-less application bytecode should be validated with runtime stubs when possible and with disassembly/opcode coverage when no source oracle exists.

References

Some Of the analyses and algorithms in this project are based on the following public sources:

  • Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy, A Simple, Fast Dominance Algorithm, Rice University, 2001 - iterative dominator and immediate-dominator computation.
  • Ron Cytron, Jeanne Ferrante, Barry K. Rosen, Mark N. Wegman, and F. Kenneth Zadeck, Efficiently Computing Static Single Assignment Form and the Control Dependence Graph, ACM TOPLAS 13(4), 1991 - dominance frontiers, φ-function placement, and the SSA rename pass.
  • Keith D. Cooper and Linda Torczon, Engineering a Compiler - background material on iterative dataflow analysis used as the basis for the dominator and SSA construction passes.
  • The official Lua source browser at https://www.lua.org/source/, including the lopcodes.h, lopcodes.c, and lvm.c files for Lua 5.1, 5.2, 5.3, 5.4, and 5.5 - authoritative opcode tables, instruction formats, and VM semantics.
  • The unluac Lua decompiler project - used as an external comparison oracle for output formatting and structural recovery on shared fixtures.

Copyright

Copyright (C) 2019-present XENOM-X TECHNOLOGY LLC.

License

This project is licensed under the GNU Affero General Public License version 3. See LICENSE.txt for the full license text.

With ❤️ From Home.