Skip to content

Repository files navigation

WeAreDevs / Prometheus Deobfuscator (Beta)

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.

Key features

  • 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

Installation

Requirements:

  • Python 3.10+
  • lua.exe available in repo root (included in this workspace)

Install Python dependencies:

pip install -r requirements

Support

Examples

ORG

print("gay")

Obfuscated

--[[ v1.0.0 https://wearedevs.net/obfuscator ]] return(function(...)local A={"\056\051\121\083\113\103\080\076\114\054\067\120", ... } ... end)(...)

Deobfuscated reconstructed (current beta output, print.lua snippet)

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")
return

Additional reference files:

How it works

  1. Static passes first: parse/format, constant-array attempts, numeric simplification, alias normalization, initial CFF cleanup.
  2. Devirtualization attempt: tries to reconstruct structure for recognizable Prometheus-like VM layouts.
  3. Post-encryption cleanup: applies targeted heuristics (anti-noise pruning, alias collapse, conditional/loop simplification).
  4. Dynamic assist (experimental): uses lua.exe probes for runtime decode/callsite recovery where static inference is insufficient.
  5. Final cleanup: readability-focused passes and stage output emission.

CLI

python controller_main.py <input.lua> --mode=<mode> [--output=<file>] [--deobf-output=<file>] [--pipeline-dir=<dir>]

Modes:

  • strings
  • patterns
  • execute
  • deobfuscate
  • pipeline
  • full

Quickstart

# 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 }

Project structure

Known limitations (Beta)

  • 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.

Notes and scope

  • 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.

Roadmap

  • 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

Why

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.

Gratitude

Thank @URF6160 for supporting me with resources to do

About

deobf Prometheus/Wearedev but bad

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages