A research-oriented deobfuscator for Lua scripts protected by Prometheus/WeAreDevs-style obfuscation.
This repository focuses on pipeline automation, reverse-engineering assistance, and iterative deobfuscation quality improvements.
Status: BETA
Current output quality is useful for analysis, but not consistently equivalent to original source across all variants.
- Pipeline-based processing: staged outputs per file (
parsed,encryption,cff,devirtualization,post+encryption,cleanup) - Constant-array and literal recovery: partial decode/inlining for indexed string patterns
- CFF reduction (heuristic): initial flattening for common dispatcher/state-machine patterns
- Runtime decoder handling: static + experimental Lua-backed dynamic decode attempts
- Anti/garbage cleanup passes: simplify redundant assignments, dead branches, placeholder noise
- Alias and naming cleanup: conservative rename/collapse passes for readability
- Roblox-oriented awareness: handles common Roblox API-like patterns in transformed output
- Batch-friendly workflow: run pipeline on many scripts and compare stage outputs quickly
Requirements:
- Python 3.10+
lua.exeavailable in repo root (included in this workspace)
Install Python dependencies:
pip install -r requirements- Discord support server: https://discord.gg/mSrMzVuc3h
print("gay")--[[ v1.0.0 https://wearedevs.net/obfuscator ]] return(function(...)local A={"\056\051\121\083\113\103\080\076\114\054\067\120", ... } ... end)(...)local floor_fn = math.floor
local random_fn = math.random
local lookup = {}
local remove_fn = table.remove
local items = {}
local char_fn = string.char
for i = 1, 256, 1 do
lookup[i] = i
end
repeat
local pick_index = random_fn(1, #lookup)
local picked = remove_fn(lookup, pick_index)
items[picked] = char_fn(picked - 1)
until #lookup == 0
local prng_bytes = {}
local prng_seed = 0
local prng_state8 = 2
local next_prng_byte = function()
if #prng_bytes == 0 then
prng_seed = (prng_seed * 57 + 19252646634245) % 35184372088832
repeat
prng_state8 = (prng_state8 * 38) % 257
until prng_state8 ~= 1
local remainder = prng_state8 % 32
local shift = 13 - ((prng_state8 - remainder) / 32)
local n = floor_fn(prng_seed / 2 ^ shift)
local mixed = floor_fn((((n % 4294967296) / 2 ^ remainder) % 1) * 4294967296) + floor_fn((n % 4294967296) / 2 ^ remainder)
local low = mixed % 65536
local high = (mixed - low) / 65536
local b1 = low % 256
local b2 = (low - b1) / 256
local b3 = high % 256
local b4 = (high - b3) / 256
prng_bytes = { b1, b2, b3, b4 }
end
return remove_fn(prng_bytes)
end
local decoded_cache = {}
local r28_local = setmetatable({}, { __index = decoded_cache })
local decode_runtime_string = function(encoded_text, cache_key)
if not decoded_cache[cache_key] then
local input_len = string.len(encoded_text)
local decoded_text = ""
for i = 1, input_len, 1 do
local byte_value = string.byte(encoded_text, i)
local lookup_index = (byte_value + next_prng_byte() + 44) % 256 + 1
decoded_text = decoded_text .. items[lookup_index]
end
decoded_cache[cache_key] = decoded_text
end
return decoded_cache[cache_key]
end
print("gay")
returnAdditional reference files:
- Original samples folder: samples/original
- Obfuscated set: samples/obfuscated/print.lua, samples/obfuscated/print2.lua
- Pipeline output samples: pipelines/print.pipeline/cleanup1-2.txt, pipelines/print2.pipeline/cleanup1-2.txt
- Static passes first: parse/format, constant-array attempts, numeric simplification, alias normalization, initial CFF cleanup.
- Devirtualization attempt: tries to reconstruct structure for recognizable Prometheus-like VM layouts.
- Post-encryption cleanup: applies targeted heuristics (anti-noise pruning, alias collapse, conditional/loop simplification).
- Dynamic assist (experimental): uses
lua.exeprobes for runtime decode/callsite recovery where static inference is insufficient. - Final cleanup: readability-focused passes and stage output emission.
python controller_main.py <input.lua> --mode=<mode> [--output=<file>] [--deobf-output=<file>] [--pipeline-dir=<dir>]Modes:
stringspatternsexecutedeobfuscatepipelinefull
# Run full pipeline for one script
python controller_main.py samples/obfuscated/obf8.lua --mode=pipeline
# Run batch pipeline for multiple scripts
$files=@('obf8.lua','obf9.lua','obf10.lua','obf11.lua','obf12.lua','obf13.lua','obf14.lua')
foreach($f in $files){ python controller_main.py ("samples/obfuscated/" + $f) --mode=pipeline }- controller_main.py: CLI orchestration
- deobfuscator_core.py: pipeline logic and transformation passes
- execution_engine.py: dynamic execution helpers
- samples/obfuscated: obfuscated input corpus
- samples/original: original scripts
- samples/reference: reference deobf outputs and step snapshots
- pipelines: per-script stage outputs
- references/Prometheus-master: Prometheus source reference
- Fails on heavy/large scripts: large files or deeply nested obfuscation layers may completely break the pipeline or produce unusable output.
- Extremely noisy variables: scripts with massive amounts of junk variables, alias spam, or generated symbols (local_1, v123, etc.) will remain messy and hard to read.
- Broken / incomplete deobfuscation: output can be partially reconstructed, syntactically broken, or missing logic entirely. In some cases, the script will not run at all after deobfuscation.
- Deobf may fail entirely: certain variants or protections will not be deobfuscated in any meaningful way (output stays close to obfuscated state).
- Very low readability in complex cases: even when “successful”, output can be extremely difficult to understand due to unresolved aliases, fragmented logic, and leftover obfuscation artifacts. Unstable control-flow recovery: complex dispatcher/VM patterns may result in incorrect or corrupted logic reconstruction.
- Runtime decoding is unreliable: dynamic decode attempts can fail, partially decode, or produce incorrect strings depending on variant.
- Garbage/anti-analysis code may persist: not all dead code, traps, or anti-deobfuscation tricks are removed.
- Heuristics are fragile: cleanup and simplification passes may either miss cases or over-simplify and break logic.
- No guarantee of correctness: output is not guaranteed to match original behavior, structure, or semantics in any reliable way.
- Edge cases are everywhere: small changes in obfuscation patterns can completely break parts of the pipeline.
This tool is experimental and may produce unusable or misleading results. Do not expect clean or production-ready output.
- Intended for research, reverse-engineering workflow support, and automation experiments.
- Use only where you have authorization to analyze the script.
- Dynamic execution is best-effort and should be run in isolated environments for untrusted inputs.
- Stronger dynamic seed-map extraction for runtime string decode
- Better semantic role inference for Roblox-oriented variables/functions
- More robust dead-code/garbage elimination
- Improved control-flow reconstruction quality
- Regression scoring harness across larger obfuscated corpora
This project exists to test iterative, measurable deobfuscation workflows on real Prometheus/WeAreDevs samples, combining static transformations with selective dynamic assistance while keeping stage outputs auditable.
Thank @URF6160 for supporting me with resources to do