| Technique | Effect on Decompiler | |-----------|----------------------| | Stripping debug info ( luac -s ) | Loss of local variable names – annoying but not fatal. | | Control flow flattening | Produces irreducible CFG; many decompilers crash or output garbled logic. | | Custom VM/opcodes | Standard decompilers fail entirely; requires reverse engineering the custom loader. | | String encryption (XOR, AES) | Output shows decryption calls instead of literals. | | Dead code & opaque predicates | Decompiler may output nonsense or infinite loops. | | Using luac from modified Lua versions (e.g., LuaJIT, LuaU) | Bytecode incompatible; decompiler must be updated. |
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: luac decompiler
unluac is the de facto standard. It can be invoked as: | | String encryption (XOR, AES) | Output