unluac is the de facto standard. It can be invoked as:
-- Source: print("Hello") -- After control flow flattening (conceptual bytecode equivalent): -- Jumps to unrelated blocks; decompiler outputs massive nested if-else chains. Input (original source): luac decompiler
Report ID: CS-SRE-0426 Subject: Reversing Lua Bytecode ( luac output) Date: April 14, 2026 Author: AI Research & Analysis Unit 1. Executive Summary This report investigates the ecosystem of decompilers targeting the Lua programming language, specifically the bytecode format generated by the standard luac compiler. Decompilation—the process of translating low-level bytecode back into high-level source code—is a critical tool for software maintenance, legacy system recovery, and security auditing. However, for Lua, it presents unique challenges due to the language's dynamic nature, rapid evolution of its virtual machine (VM), and the intentional or unintentional loss of high-level information during compilation. unluac is the de facto standard
function factorial(n) if n <= 1 then return 1 else return n * factorial(n - 1) end end print(factorial(5)) Perfect recovery despite stripping. Local variable name n is preserved? No – unluac inferred it from the GETLOCAL instruction's register mapping, but the name n is actually generic. However, because the function parameter name is not stored, unluac defaults to the name given in the prototype's local list (which is empty). Wait — the output above shows n . In reality, stripped bytecode loses all local names; the decompiler would output function factorial(arg0) or function factorial(local_1) . Correction: The example above is idealized. Actual stripped output would be: Executive Summary This report investigates the ecosystem of
Our analysis finds that while perfect decompilation of Lua bytecode is theoretically impossible due to information loss (e.g., local variable names, comments, and some control structures), modern tools like unluac , LuaDec , and py-lua-decompiler achieve high-fidelity reconstruction for most standard Lua 5.1–5.4 bytecode. However, the proliferation of custom Lua VMs in game engines (e.g., Roblox, World of Warcraft) and embedded systems necessitates specialized, often manually updated, decompilers.
local function factorial(n) if n <= 1 then return 1 else return n * factorial(n - 1) end end print(factorial(5)) luac -s -o factorial.luac factorial.lua (stripped, no debug info)