Lua Decompiler -

The Deep Dive into Lua Decompilers: From Bytecode Back to Source

Typical decompilation approach

  1. Parse binary chunk — verify header, extract version/flags, and read function prototypes.
  2. Disassemble instructions — map numeric opcodes to mnemonic operations and operands.
  3. Recover control flow — build basic blocks, compute jump targets, and reconstruct structured control flow (if/else, while, for).
  4. Reconstruct expressions — combine sequences of instructions into arithmetic, logical, and table operations.
  5. Name generation — assign readable names to registers/locals/upvalues since original names may be unavailable.
  6. Emit source — pretty-print reconstructed AST to Lua-like source with indentation and idiomatic constructs.
  7. Optional post-processing — constant folding, dead-code elimination, and reconstructing local scope ranges when debug info exists.

Part 4: How a Decompiler Thinks (The Algorithm)

Let’s walk through the mental model of unluac. If you feed it bytecode, here is the simplified step-by-step:

Step 1: Disassembly The decompiler reads the binary header (magic number, version, endianness) and walks through the list of prototypes (functions). It converts each bytecode instruction into a human-readable mnemonic (e.g., OP_MOVE, OP_ADD).

Step 2: Expression Extraction The decompiler scans backwards through the bytecode to build expressions.

Step 3: Block Partitioning (Basic Blocks) The decompiler divides the instruction stream into "basic blocks"—straight-line code with no jumps in or out (except at the end). It finds JMP (goto) instructions and marks block boundaries.

Step 4: Control Flow Graph (CFG) Building The decompiler maps how blocks connect. If instruction 5 jumps to instruction 10, a line is drawn. This graph reveals the loops and conditionals.

Step 5: High-Level Structure Recovery (The Hard Part) Here is where decompilers guess. The CFG might look like:

The decompiler then outputs the reconstructed syntax. lua decompiler

Why is this imperfect? Consider a switch statement (if-elseif chain) versus a binary search tree of ifs. The bytecode looks identical. The decompiler must guess.


Part 10: Practical Tutorial – Recovering a Lost Script

Scenario: You have game.luac from a backup. You remember it was a utility script for a Discord bot, but you lost the original .lua.

Step 1 – Identify Lua version:

hexdump -C game.luac | head

Lua 5.1 header: 1b 4c 75 61 51 Lua 5.4 header: 1b 4c 75 61 54

Step 2 – Run unluac:

java -jar unluac.jar --rawstring game.luac > recovered.lua

The --rawstring flag prevents escaping issues. The Deep Dive into Lua Decompilers: From Bytecode

Step 3 – Manual cleanup: Replace local var_0, var_1 with meaningful names using find/replace. Re-add comments from memory.

Step 4 – Validate:

lua recovered.lua

If it crashes, the decompiler likely mis-nested an end or else. Compare the bytecode with ChunkSpy to fix manually.

Step 5 – If unluac fails: Try LuaDec for Lua 5.1 or use luac -l -l game.luac (the -l -l flag dumps detailed bytecode). Write a small Lua script to reconstruct simple blocks.


Setup

# Download unluac.jar from GitHub
wget https://github.com/unluac/unluac/releases/latest/download/unluac.jar

2. When would you need one?


Introduction: The Unseen Layer of Lua Scripting

Lua is celebrated as the "perfect embedded language." From powering video games like World of Warcraft and Roblox to driving hardware in routers and set-top boxes, its lightweight speed is a key feature. To achieve this speed, Lua scripts are compiled into bytecode—a low-level, numerical representation of your code that the Lua Virtual Machine (VM) can execute rapidly.

However, what happens when you have the bytecode (often a .luac file or embedded within a game’s asset archive) but have lost the original .lua source code? Enter the Lua decompiler. Parse binary chunk — verify header, extract version/flags,

A Lua decompiler is a tool designed to reverse the compilation process. It reads the binary bytecode, parses the VM instructions, and attempts to reconstruct human-readable, editable Lua source code.

This article explores the technical intricacies, the major tools, the legal landscape, and the future of Lua decompilation.


The Challenge

Lua bytecode is high-level, but it discards:

This is why decompiling Lua perfectly is often impossible—you get the logic, but not the original poetry.


Part 6: Obfuscation and Anti-Decompilation

If you are a game developer wanting to protect your Lua scripts, understand that decompilation is an arms race.